从JDK1.4开始,JDK提供了一套专门的类库支持非阻塞I/O,可以在java.nio包及其子包中找到相关的类和接口。由于这套API是新提供的I/O API,因此也叫New I/O,这就是JAVA NIO的由来。非阻塞IO API由3个主要部分组成:缓冲区(Buffers)、通道(Channels)和Selector
NIO服务端创建过程
打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的父管道
1
ServerSocketChannel acceptorSrv = ServerSocketChannel.open();
绑定监听端口,设置连接为非阻塞模式
1
2
3acceptorSrv.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"), port));
acceptorSrv.configureBlocking(false);创建Reactor线程,创建多路复用器并启动线程
1
2
3Selector selector = Selector.open();
new Thread(new ReactorTask()).start();将ServerSocketChannel注册到Reactor线程的多路复用器Selector上,监听ACCEPT事件
1
SelectionKey key = acceptorSvr.register(selector, SelectionKey.OP_ACCEPT, ioHandler);
多路复用器在线程run方法的无限循环体内轮询准备就绪的Key
1
2
3
4
5
6
7
8
9
10
11
12
13int num = selector.select();
Set selectedKeys = selector.selectKeys();
Iterator it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = (SelectionKey) in.next();
//...deal with I/O event...
}多路复用器监听到有新的客户端接入,处理新的接入请求,完成TCP三次握手,建立物理链路
1
SocketChannel channel = svrChannel.accept();
设置客户端链路的TCP参数
1
2
3channel.configureBlocking(false);
channel.socket().setReuseAddress(true);将新接入的客户端连接注册到Reactor线程的多路复用器上,监听读操作
1
SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ, ioHandler);
异步读取客户端请求消息到缓冲区
1
int readNum = channel.read(receiveeBuffer);
对ByteBuffer进行编解码,如果有半包消息指针Reset,继续读取后续的报文,将解码成功的消息封装成Task,投递到业务线程池中,进行业务逻辑操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Object message = null;
while(buffer.hasRemain()) {
byteBuffer.remark();
message = decode(byteBuffer);
if(message == null) {
byteBuffer.reset();
break;
}
messageLisk.add(message);
}将POJO对象encode成ByteBuffer,调用SocketChannel的异步write接口,将消息异步发送给客服端
1
socketChannel.write(buffer);