NIO网络编程
2020-03-08 本文已影响0人
蓬莱辰
1. NIO请求过程:
- 服务端:Selector 组件,循环检测注册事件就绪情况,(负责管理客户端与服务端建立的连接,监听注册到其上的事件);
首先服务端的 Selector 注册建立连接事件, - 客户端发送建立连接请求;
- Selector监听到后,启动建立连接事件处理器 Acceptor Handler;
- Acceptor Handler 创建与客户端连接;
- 服务端 Acceptor Handler 响应客户端建立连接请求;
- 服务端 Acceptor Handler 向selector 注册连接可读事件;
- 客户端发送数据请求;
- Selector 监听到后,启动读写处理器 Read&Write Handler;
- 读写处理器处理客户端读写业务;
- 响应客户端请求;
-
注册连接可读事件。
image.png
2. 相比BIO,NIO的优点:
2.1. 基于非阻塞式IO模型;
2.2. 弹性伸缩能力强:不是多个线程,而是一个线程处理所有请求,服务器端的线程数与客户端的接入数不是1:1;
2.3. 单线程节省资源:线程的频繁创建和销毁,线程上下文之间的额切换。
3. Java中的实现
3.1 Channel 通道
特点:
- 双向性:读写可用一个channel;
- 非阻塞性;
- 操作唯一性。
Channel实现: - 文件类:FileChannel;
- UDP类:DatagramChannel ;
- TCP 类:ServerSocketChannel / SocketChannel
3.2 ByteBuffer
作用:读写Channel中的数据;
实质:一块内存;
Capacity:容量
Position:位置,max:capacity-1
Limit:上限;
Mark:标记,存储position位置
/*
* 创建一个新的 ByteBuffer
* position:0
* limit:10
* capacity:10
*/
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
/*
* 向byteBuffer中写入三个字节
* position:3
* limit:10
* capacity:10
*/
byteBuffer.put("abc".getBytes(StandardCharsets.UTF_8));
/*
* 将byteBuffer从写模式切换到读模式
* position:0
* limit:3 最多能读取的数量
* capacity:10
*/
byteBuffer.flip();
// 读取一个字节
byteBuffer.get();
// 调用mark方法,记录当前position的位置
byteBuffer.mark();
/**
* 先调用get方法,读取下一个字节,position=2,
* 调用reset方法,position重置到mark位置
*/
byteBuffer.get();
byteBuffer.reset();
// 将所有属性重置
byteBuffer.clear();
3.3 Selector 多路复用器
// 创建selector
Selector selector = Selector.open();
// 将通道channel注册到selector上,监听连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 获取发生就绪事件的(可用)channel集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
4. NIO编程实现步骤
- 创建Selector;
- 创建ServerSocketChannel,并绑定监听端口;
- 将channel设置为非阻塞模式;
- 将channel注册到Selector上,并监听连接事件;
- 循环调用Selector的select方法,检测就绪情况;
int readyChannelNum = selector.select();
- 调用Selector的selectedKeys方法获取就绪channel集合;
- 判断就绪事件的种类(连接事件 or 读事件),调用业务处理方法;
- 根据业务需求判断是否再次注册监听事件,重复第3步操作;
5.实战
简单聊天室功能:
源码:https://github.com/sliumhh/nio-bio