1.Java NIO 由以下几个核心部分组成

  • Channels(通道)
  • Buffers(缓冲区)
  • Selectors(选择器)

虽然Java NIO 中除此之外还有很多类和组件,ChannelBufferSelector 构成了核心的API。其它组件,如PipeFileLock,只不过是与三个核心组件共同使用的工具类。

(1)Channel Buffer

基本上,所有的 IO操作在NIO 中都从一个Channel 开始。Channel 有点像 流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)_客户端

ChannelBuffer有好几种类型。

下面是JAVA NIO中的一些主要Channel的实现

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

正如你所看到的,这些通道涵盖了UDPTCP 网络IO,以及文件IO

与这些类一起的有一些有趣的接口,但为简单起见,我尽量在概述中不提到它们。本教程其它章节与它们相关的地方我会进行解释。

 

以下是Java NIO主要的Buffer实现

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, doublechar

Java NIO 还有个 MappedByteBuffer,用于表示内存映射文件, 我也不打算在概述中说明。

 

(2)Selector

  Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

这是在一个单线程中使用一个Selector处理3个Channel的图示:

Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)_服务器_02

要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

 

2. NIO使用代码示例:

java NIO服务端和客户端代码实现,为了更好地理解java NIO,下面贴出服务端和客户端的简单代码实现:

服务端:

  1 package com.himi.demo2;
  2 
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.nio.ByteBuffer;
  6 import java.nio.channels.SelectionKey;
  7 import java.nio.channels.Selector;
  8 import java.nio.channels.ServerSocketChannel;
  9 import java.nio.channels.SocketChannel;
 10 import java.util.Iterator;
 11 
 12 /**
 13  * NIO 服务端
 14  * 
 15  * @author hebao
 16  */
 17 public class NIOServer {
 18     // 通道管理器
 19     private Selector selector;
 20 
 21     /**
 22      * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
 23      * 
 24      * @param port
 25      *            绑定的端口号
 26      * @throws IOException
 27      */
 28     public void initServer(int port) throws IOException {
 29         // 获得一个ServerSocket通道
 30         ServerSocketChannel serverChannel = ServerSocketChannel.open();
 31         // 设置通道为非阻塞
 32         serverChannel.configureBlocking(false);
 33         // 将该通道对应的ServerSocket绑定到port端口
 34         serverChannel.socket().bind(new InetSocketAddress(port));
 35         // 获得一个通道管理器
 36         this.selector = Selector.open();
 37         // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
 38         // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
 39         serverChannel.register(selector, SelectionKey.OP_ACCEPT);
 40     }
 41 
 42     /**
 43      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
 44      * 
 45      * @throws IOException
 46      */
 47     @SuppressWarnings("unchecked")
 48     public void listen() throws IOException {
 49         System.out.println("服务端启动成功!");
 50         // 轮询访问selector
 51         while (true) {
 52             // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
 53             selector.select();
 54             // 获得selector中选中的项的迭代器,选中的项为注册的事件
 55             Iterator ite = this.selector.selectedKeys().iterator();
 56             while (ite.hasNext()) {
 57                 SelectionKey key = (SelectionKey) ite.next();
 58                 // 删除已选的key,以防重复处理
 59                 ite.remove();
 60                 // 客户端请求连接事件
 61                 if (key.isAcceptable()) {
 62                     ServerSocketChannel server = (ServerSocketChannel) key.channel();
 63                     // 获得和客户端连接的通道
 64                     SocketChannel channel = server.accept();
 65                     // 设置成非阻塞
 66                     channel.configureBlocking(false);
 67 
 68                     // 在这里可以给客户端发送信息哦
 69                     channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
 70                     // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
 71                     channel.register(this.selector, SelectionKey.OP_READ);
 72 
 73                     // 获得了可读的事件
 74                 } else if (key.isReadable()) {
 75                     read(key);
 76                 }
 77 
 78             }
 79 
 80         }
 81     }
 82 
 83     /**
 84      * 处理读取客户端发来的信息 的事件
 85      * 
 86      * @param key
 87      * @throws IOException
 88      */
 89     public void read(SelectionKey key) throws IOException {
 90         // 服务器可读取消息:得到事件发生的Socket通道
 91         SocketChannel channel = (SocketChannel) key.channel();
 92         // 创建读取的缓冲区
 93         ByteBuffer buffer = ByteBuffer.allocate(10);
 94         channel.read(buffer);
 95         byte[] data = buffer.array();
 96         String msg = new String(data).trim();
 97         System.out.println("服务端收到信息:" + msg);
 98         ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
 99         channel.write(outBuffer);// 将消息回送给客户端
100     }
101 
102     /**
103      * 启动服务端测试
104      * 
105      * @throws IOException
106      */
107     public static void main(String[] args) throws IOException {
108         NIOServer server = new NIOServer();
109         server.initServer(8000);
110         server.listen();
111     }
112 
113 }

 

客户端:

  1 package com.himi.demo2;
  2 
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.nio.ByteBuffer;
  6 import java.nio.channels.SelectionKey;
  7 import java.nio.channels.Selector;
  8 import java.nio.channels.SocketChannel;
  9 import java.util.Iterator;
 10 
 11 /**
 12  * NIO客户端
 13  * 
 14  * @author hebao
 15  */
 16 public class NIOClient {
 17     // 通道管理器
 18     private Selector selector;
 19 
 20     /**
 21      * 获得一个Socket通道,并对该通道做一些初始化的工作
 22      * 
 23      * @param ip
 24      *            连接的服务器的ip
 25      * @param port
 26      *            连接的服务器的端口号
 27      * @throws IOException
 28      */
 29     public void initClient(String ip, int port) throws IOException {
 30         // 获得一个Socket通道
 31         SocketChannel channel = SocketChannel.open();
 32         // 设置通道为非阻塞
 33         channel.configureBlocking(false);
 34         // 获得一个通道管理器
 35         this.selector = Selector.open();
 36 
 37         // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
 38         // 用channel.finishConnect();才能完成连接
 39         channel.connect(new InetSocketAddress(ip, port));
 40         // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
 41         channel.register(selector, SelectionKey.OP_CONNECT);
 42     }
 43 
 44     /**
 45      * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
 46      * 
 47      * @throws IOException
 48      */
 49     @SuppressWarnings("unchecked")
 50     public void listen() throws IOException {
 51         // 轮询访问selector
 52         while (true) {
 53             selector.select();
 54             // 获得selector中选中的项的迭代器
 55             Iterator ite = this.selector.selectedKeys().iterator();
 56             while (ite.hasNext()) {
 57                 SelectionKey key = (SelectionKey) ite.next();
 58                 // 删除已选的key,以防重复处理
 59                 ite.remove();
 60                 // 连接事件发生
 61                 if (key.isConnectable()) {
 62                     SocketChannel channel = (SocketChannel) key.channel();
 63                     // 如果正在连接,则完成连接
 64                     if (channel.isConnectionPending()) {
 65                         channel.finishConnect();
 66 
 67                     }
 68                     // 设置成非阻塞
 69                     channel.configureBlocking(false);
 70 
 71                     // 在这里可以给服务端发送信息哦
 72                     channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
 73                     // 在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
 74                     channel.register(this.selector, SelectionKey.OP_READ);
 75 
 76                     // 获得了可读的事件
 77                 } else if (key.isReadable()) {
 78                     read(key);
 79                 }
 80 
 81             }
 82 
 83         }
 84     }
 85 
 86     /**
 87      * 处理读取服务端发来的信息 的事件
 88      * 
 89      * @param key
 90      * @throws IOException
 91      */
 92     public void read(SelectionKey key) throws IOException {
 93         // 和服务端的read方法一样
 94     }
 95 
 96     /**
 97      * 启动客户端测试
 98      * 
 99      * @throws IOException
100      */
101     public static void main(String[] args) throws IOException {
102         NIOClient client = new NIOClient();
103         client.initClient("localhost", 8000);
104         client.listen();
105     }
106 
107 }