Java NIO通信基础示例详解

下面是“Java NIO通信基础示例详解”的完整攻略。

概述

Java NIO是Java 1.4版本引入的一种新的I/O处理方式。相较于传统的I/O方式,NIO采用了非阻塞式I/O模型,使得I/O的效率更高。本文将详细讲解Java NIO通信的基础知识和实现方式。

NIO简介

NIO是New IO的缩写,它是用来替代传统的Java IO的。Java IO(流和相关类)是面向流的,而NIO是面向缓冲区的。NIO提供了与传统IO不同的优点,例如在面向连接的传输方式中(如TCP),NIO可以通过一个单独的线程处理多个连接的输入和输出数据。

NIO的三个核心组件

1. 缓冲区 Buffer

缓冲区是一个对象,可以在其包含的数组中存储数据。Java NIO中的所有缓冲区都是 Buffer 类的子类。 Buffer 类(如下所示)有以下几个核心属性:

  • 容量(Capacity):缓冲区中最多可以存储的数据元素的数量。缓冲区的容量在创建时被固定,并且不能更改。
  • 位置(Position):下一个要读取/写入的元素索引。它默认是0,当数据被写入到缓冲区中时,位置向后移动,当从缓冲区中读取数据时,位置向前移动。
  • 限制(Limit):可以访问和操作的数据索引的下一个位置索引。默认情况下,限制等于容量。
public abstract class Buffer {
    // 容量
    private int capacity;
    // 下一个要读取/写入的元素索引
    private int position = 0;
    // 可以访问和操作的数据索引的下一个位置索引
    private int limit;
    .........
}

2. 通道 Channel

在Java NIO中, Channel是与数据源进行交互的实体。 数据源可以是文件,套接字或服务器端 另一个进程。 Channel的特点是可以被异步地读写。

常用的 Channel 类有:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

3. 选择器 Selector

Selector是Java NIO中的核心组件之一,用于处理多个 Channel的异步和非阻塞IO操作。可以注册到选择器的每个通道都可以被单个线程处理。因此,一个线程可处理多个输入和输出通道。当一个Channel需要进行读写或者连接时,可以使用选择器进行管理。因为在单个线程中,可以管理多个Channel,因此可以提高系统的运行效率。

NIO通信基础示例

示例1:实现简单的文件读写

首先,我们需要先创建一个Buffer对象将数据写入到文件中,然后读出来。

public void fileChannelDemo() throws IOException {
    String content = "hello, Java NIO";
    RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
    FileChannel channel = file.getChannel();
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    // 写入Buffer
    buffer.put(content.getBytes());
    // 缓存区从写模式转为读模式
    buffer.flip();
    // 将buffer中数据写入到通道
    channel.write(buffer);
    buffer.clear();
    // 将数据从通道读取到buffer中
    channel.read(buffer);
    buffer.flip();
    byte[] result = new byte[buffer.remaining()];
    buffer.get(result);
    System.out.println(new String(result));
    file.close();
}

示例2:实现客户端与服务端通信

在这个例子中,我们将实现客户端和服务端通信,客户端将向服务端发送"hello, NIO"字符串,并从服务端接收到"hello, Java NIO"字符串。

服务端部分

public class Server {
    public void server() throws IOException {
        //创建选择器
        Selector selector = Selector.open();
        //创建ServerSocketChannel对象
        ServerSocketChannel server = ServerSocketChannel.open();
        //设置通道为非阻塞状态
        server.configureBlocking(false);
        //设置监听的地址
        server.socket().bind(new InetSocketAddress(9999));
        //将通道注册在选择器上,设置监听事件为:连接(accept)
        server.register(selector, SelectionKey.OP_ACCEPT);
        //循环等待客户端连接
        while(true){
            if(selector.select(1000) == 0){
                continue;
            }
            //获取选择的键集合
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIter = keys.iterator();
            while(keyIter.hasNext()){
                SelectionKey key = keyIter.next();
                //删除已经处理的键信息
                keyIter.remove();
                //处理连接事件(当有客户端连接时)
                if(key.isAcceptable()){
                    //获取相应的ServerSocketChannel
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    //调用accept方法,接收客户端的连接
                    SocketChannel socket = serverChannel.accept();
                    //设置非阻塞状态
                    socket.configureBlocking(false);
                    //在选择器上注册相应的事件:读(read)
                    socket.register(selector, SelectionKey.OP_READ);
                } else if(key.isReadable()){//处理读事件
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int readBytes = socketChannel.read(buffer);
                    if(readBytes > 0){
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String body = new String(bytes, "UTF-8");  
                        System.out.println("Server: Receive Message -> "+ body);
                        String response = "hello, Java NIO";
                        ByteBuffer outBuffer = ByteBuffer.wrap(response.getBytes());
                        socketChannel.write(outBuffer);//将消息回送给客户端
                    }
                }
            }
        }
    }

}

客户端部分

public class Client {
        public void client() throws IOException {
            //创建SocketChannel对象
            SocketChannel socketChannel = SocketChannel.open();
            //设置通道为非阻塞状态
            socketChannel.configureBlocking(false);
            //连接服务端
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
            Selector selector = Selector.open();
            //将通道注册在选择器上,设置监听事件为:连接(connect)
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while(true){
                if(selector.select(1000) == 0){
                    continue;
                }
                //获取选择的键集合
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> keyIter = keys.iterator();
                while(keyIter.hasNext()){
                    SelectionKey key = keyIter.next();
                    //删除已经处理的键信息
                    keyIter.remove();
                    //连接事件(由于客户端是由连接服务器开始的,因此处理连接事件在这个例子中可有可无)
                    if(key.isConnectable()){
                        SocketChannel channel = (SocketChannel) key.channel();  
                        //判断是否连接成功
                        if(channel.finishConnect()){
                            System.out.println("Client: Connect to server successful!");
                            ByteBuffer buffer = ByteBuffer.wrap("hello, NIO".getBytes());
                            buffer.flip();
                            channel.write(buffer);//向服务器发送消息
                        } else {
                            System.exit(1);
                        }
                    } else if(key.isReadable()){//处理读事件
                        SocketChannel socketChannel1 = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int readBytes = socketChannel1.read(buffer);
                        if(readBytes > 0){
                            buffer.flip();
                            byte[] bytes = new byte[buffer.remaining()];
                            buffer.get(bytes);
                            String body = new String(bytes, "UTF-8");  
                            System.out.println("Client: Receive Message -> " + body);
                        }
                    }
                }
            }
        }
}

上述实例代码实现了Java NIO的基本应用,可以尝试自己写更多的样例来加深理解。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java NIO通信基础示例详解 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Java多线程atomic包介绍及使用方法

    下面是详细讲解“Java多线程atomic包介绍及使用方法”的完整攻略。 什么是Java多线程atomic包? Java多线程中的atomic包是提供原子性操作的一个工具包。所谓原子性操作,是指一个操作在执行时不会被其他线程的操作所干扰,不会出现数据不一致或者执行结果不完整的情况。atomic包中的类都是安全的,可以保证在多线程的环境下进行数据操作时不会造成…

    Java 2023年5月26日
    00
  • Java获取UTC时间的方法详解

    Java获取UTC时间的方法详解 什么是UTC时间 UTC(Coordinated Universal Time,协调世界时)是一种全球使用的时间标准,与格林威治标准时间(GMT,Greenwich Mean Time)等价。UTC时间是按照原子钟计时的,且与地球自转无关,因此是一种非常精确的时间标准。 Java中获取UTC时间的方法 要在Java中获取UT…

    Java 2023年5月20日
    00
  • java实现即时通信的完整步骤分享

    下面我将为大家详细讲解Java实现即时通信的步骤及示例: 步骤一:选择通信协议 实现即时通信的第一步是选择合适的通信协议,常用的通信协议有TCP、UDP和HTTP等。其中TCP协议是面向连接的、可靠的协议,适用于保证数据可靠传输的场景;UDP协议是无连接的、不可靠的协议,适用于实时性要求较高的场景;HTTP协议是应用最为广泛的协议,适用于数据传输量较大、要求…

    Java 2023年5月18日
    00
  • 在Ubuntu20.04 LTS中配置Java开发环境

    下面我来为你讲解如何在Ubuntu20.04 LTS中配置Java开发环境。 1. 安装Java 首先需要安装OpenJDK或Oracle JDK,建议使用OpenJDK。 在终端中输入以下命令进行安装: sudo apt update sudo apt install default-jdk 安装完成后,查看Java版本: java -version 如果…

    Java 2023年5月26日
    00
  • 使用Springboot封装一个自适配的数据单位转换工具类

    下面是使用Springboot封装一个自适配的数据单位转换工具类的完整攻略。 1. 确认需求和API设计 在开始实现之前,我们需要明确我们的需求,以及该工具类所提供的API接口。对于这个工具类而言,我们需要实现以下两个需求: 支持自适配输入输出单位(如输入以KB为单位,输出以MB或GB为单位)。 支持常见的数据单位(如B、KB、MB、GB、TB)。 根据上述…

    Java 2023年5月20日
    00
  • SpringBoot Bean花式注解方法示例下篇

    请听我详细讲解“SpringBoot Bean花式注解方法示例下篇”的完整攻略。 概述 本文主要介绍在Spring Boot项目中常用的Bean注解及其用法,包括@Component、@Service、@Repository、@Controller、@Configuration、@Bean等。 @Component注解 @Component是最常用的注解之一…

    Java 2023年6月3日
    00
  • js实现ctrl+v粘贴上传图片(兼容chrome、firefox、ie11)

    当用户需要上传图片时,常见的方式是通过点击上传按钮或将图片拖拽到上传区域进行上传。但是,这个过程可能会比较繁琐,用户需要先将图片从电脑上选择出来或者用截图工具进行截图,然后才能进行上传操作。 为了提高用户的上传体验,我们可以通过JS实现Ctrl+V粘贴上传图片的功能。具体实现过程如下: 获取粘贴板上的图片数据 首先,需要监听网页上的粘贴事件,在用户按下Ctr…

    Java 2023年6月15日
    00
  • Docker-利用dockerfile来搭建tomcat服务的方法

    Docker是一种容器化技术,可以使用Dockerfile文件来描述应用程序及其依赖项的构建过程,同时提供了简单且易于复制、移动、并在环境中部署的容器。 以下是搭建Tomcat服务的Dockerfile文件示例: # 基础镜像 FROM openjdk:8-jre-alpine # 设置Tomcat版本 ENV TOMCAT_MAJOR=8 \ TOMCAT…

    Java 2023年6月2日
    00
合作推广
合作推广
分享本页
返回顶部