基础
NIO:Non-Blocking IO 非阻塞 IO,主要用于网络连接中非阻塞的读写,提供多路非阻塞式的高伸缩性网络 I/O 。异步 I/O 的一个优势在于,可以同时根据大量的输入和输出执行 I/O。同步程序常常需要轮询,或者创建很多线程处理大量的连接。使用异步 I/O,可以监听任何数量的通道上的事件,不用轮询也不用额外的线程。
Selector
选择器:是 Java NIO 中能够检测一到多个 NIO 通道,是多路复用器,能够监听通道是否为读写事件做好准备。因此一个单独的线程可以管理多个通道,从而管理多个网络连接。Selector 用来支持异步 I/O 操作(非阻塞I/O操作),Channel 必须处于非阻塞模式下(因此 FileChannel, Selector 不能一起使用)。Selector 是非阻塞 I/O 的核心,所有希望采用非阻塞 I/O 进行通信的通道,都应该注册到 Selector 对象。
SelectionKey
SelectionKey 包含监听的不同类型事件:
OP_READOP_WRITEOP_CONNECTOP_ACCEPT
常用 API
1 | public abstract class SelectionKey { |
常用 API
1 | public abstract class Selector implements Closeable { |
SelectableChannel 支持选择器的通道
表示可以支持多路复用的通道,支持阻塞和非阻塞模式。(默认情况下,所有的 Channel 都是阻塞的),需要设置为非阻塞模式,才能使用 NIO 特性。
1 | public abstract class SelectableChannel |
注册通道
通道必须处于非阻塞模式,可以监听多个事件。
1 | channel.configureBlocking(false); |
参考示例
1 | Selector selector = Selector.open(); |
SocketChannel
对应于 java.net.Socket 类。
创建 SocketChannel
默认创建一个阻塞 SocketChannel,两种方法的差异:
open()
后续还需要手动配置为非阻塞后,监听连接事件。open(SocketAddress remote)
创建并同步等待连接remote,直到连接成功后返回。不需要再手动连接。
支持的非阻塞事件
OP_CONNECTOP_READOP_WRITE
不支持 Accept 事件
常见 API
1 | public abstract class SocketChannel |
ServerSocketChannel
对应于 java.net.ServerSocket 类。
创建 ServerSocketChannel
创建 ServerSocketChannel 也很简单:ServerSocketChannel server = ServerSocketChannel.open();
支持的非阻塞事件
仅支持 OP_ACCEPT 事件。
常见 API
1 | public abstract class ServerSocketChannel |
客户端和服务端 TCP 通信流程
整个通信流程参考 TCP 通信流程,只是实现方式不一样。本例实现一个聊天室功能。
服务端初始化
- 创建
Selectorpublic static Selector open() throws IOException;,创建选择器。 - 创建
ServerSocketChannelpublic static ServerSocketChannel open() throws IOException;,创建服务端通道,并配置为非阻塞。 - 绑定
bindpublic final ServerSocketChannel bind(SocketAddress local){...},绑定指定地址和端口。 - 注册
Accept事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);,注册后服务端选择器开始监听客户端的连接。
服务端轮询监听并处理事件
selector.select(),阻塞等待。监听客户端的连接及输入,从这里可以看出 nio 并不完全是异步,还是会有阻塞。
客户端初始化
- 创建
Selectorpublic static Selector open() throws IOException;,创建选择器。 - 创建
SocketChannelpublic static SocketChannel open() throws IOException;,创建客户端通道,并配置为非阻塞。 - 注册
Connect事件socketChannel.register(selector, SelectionKey.OP_CONNECT);,注册事件后准备连接。 - 连接
connectpublic abstract boolean connect(SocketAddress remote) throws IOException;,连接服务器。
客户端轮询监听并处理事件
selector.select(),阻塞等待。监听服务端反馈和输入。
数据通信
- 服务端接受连接后监听客户端写入
服务端接受客户端连接后,拿到客户端SocketChannel,并同步注册Read事件,监听客户端输入。通过该通道读写缓冲区实现数据通信。 - 客户端监听服务端发送的消息
客户端接受到服务端连接响应后,客户端SocketChannel注册Read事件,监听该通道来自服务端的输入。
服务端和客户端关闭
客户端和服务端分别关闭 Selector, ServerSocketChannel, SocketChannel。
服务端存在的问题:客户端的
SocketChannel关闭后,服务端此通道并没有断开连接,并且该通道注册到选择器的读事件,会被反复触发。也就是说服务端select一直都会返回OP_READ,但是通道中读入缓冲区的数据实际总是为 -1,即并没有数据。为什么会反复触发?
1 | // 服务端打印信息,服务端 10000,客户端 62676 |
TCP 通信示例
本例实现一个聊天室功能。
服务端
1 | public class TestNIOTCPServer { |
客户端
1 | public class TestNIOTCPClient { |
DatagramChannel
[todo]
http://ifeve.com/datagram-channel/
http://blog.csdn.net/foart/article/details/47608475
http://www.365mini.com/page/java-nio-course-27.htm
网络编程第四版-425