NIO
基础概念
标准的
Channel(通道)
FileChannel: 可以向文件读写数据SocketChanel: 以TCP 来向网络连接的两端读写数据ServerSocketChanel: 能够监听客户端发起的TCP 连接,并为每个TCP 连接创建一个新的SocketChannel DatagramChannel: 以UDP 协议来向网络连接的两端读写数据
Buffer(缓冲区)
Buffer,故名思意,缓冲区,实际上是一个容器,是一个连续数组。
上图描述了客户端向服务端发送数据,然后服务端接收数据的全部过程。客户端发送数据时,必须先将数据存入
Selector(选择器)
与
Selector selector = Selector.open(); // 创建 Selector
SelectionKey key = channel.register(selector, Selectionkey.OP_READ); // 将通道注册到 Selector 上
// register 的第二个参数指明监听哪些事件,可选的值有:
// Connect - 连接就绪
// Accept - 接收就绪
// Read - 读就绪
// Write - 写就绪
// 与之对应的检测 Channel 中什么事件或操作已经就绪的函数为:
selectionKey.isConnectable(); // 是否连接就绪
selectionKey.isAcceptable(); // 是否接收就绪
selectionKey.isReadable(); // 是否读就绪
selectionKey.isWritable(); // 是否写就绪
Java NIO 代码示例
下面结合示例代码,进一步理解
// 创建 Selector
Selector selector = Selector.open();
// 创建 ServerSocketChannel 并绑定到指定端口
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress("127.0.0.1", 1234));
// 设置 ServerSocketChannel 为 non-blocking
server.configureBlocking(false);
// 将 server channel 注册到 selector 并设置监听 OP_ACCEPT 事件
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// selector 被 select() 阻塞
// select() 会把注册的事件添加到 SelectionKeys (只增不减)
if (selector.select() == 0) {
continue;
}
// 获得 SelectionKey 集合, 每一个 Selectionkey 对应着一个已注册的 Channel
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isAcceptable()) {
// 获取与 client 相连的 SocketChannel
SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
// 同样设置为 non-blocking
channel.configureBlocking(false);
// 这里可以向 client 发送信息
channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息!").getBytes()));
// 将此 channel 的 OP_READ 事件注册到 selector
channel.register(this.selector, SelectionKey.OP_READ);
}
// 如果 channel 可读
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
// 创建读缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
}
// 由于 select() 对 SelectionKey 集合只增不减 这里需手动移除 key
keys.remove(key);
}
}