java select模型_Java NIO —Selector编程模型总结

本文详细介绍了NIO中的Selector组件,包括其作用、维护的集合以及NIO编程模型。Selector用于轮询多个Channel,当有事件发生时进行处理。文章通过服务端和客户端的例子,展示了如何注册Channel到Selector并监听特定事件,如OP_ACCEPT和OP_CONNECT。
摘要由CSDN通过智能技术生成

前言

NIO编程一直不太熟悉,一方面没有系统地学习过,另一方面NIO编程确实有些复杂,一下没有用了过阵子容易忘记,所以,写篇小博客就很有必要,这里来总结一下selector组件用法,Buffer和Channel有空补上。

Selector

一张你可能熟悉的图(IO多路复用)

de8fba0339eff982017f3e7265cd88b4.png

1. 什么是selector? 有什么用?

selector(选择器),IO多路复用的组件, 和它直接关联的组件是Channel, 它的作用就是不断的轮询绑定在他身上的channel。一旦有通道发生了它感兴趣的事件,接下来处理该事件。

2. selector维护的set集合

selector维护了三个set集合,里面封装的是 SelectionKey,用它来获取channel。

2.1 key set

全集,每当channel通过register方法注册进选择器时,会把包含Channel自己信息的key添加到这个全集中来,注册的信息就会以SelectionKey的封装形式保存在这个集合中, 用这个SelectionKey来获取channel。

2.2 selected key set

感兴趣的key的集合,每次遍历selected key时我们会执行这行代码:Set selectionKeys = selector.selectedKeys();

2.3 cannelled key set

一般会在客户端主动断开连接的时候使用它,代表原来感兴趣的事件,现在不感兴趣了,下一次轮询,进行select()本集合中的SelectionKey会从key set中移除, 意味着它所关联的channel将会被选择器丢弃掉,不再进行监听。

NIO编程模型

1. 服务端创建代表服务端的Channel,绑定好端口,设置成非阻塞的通道 并且初始化选择器,然后开始轮询绑定在自己身上的通道,此时的通道只有一个ServerSocketChannel,而选择器只关心ServerSocketChannel上发生的OP_ACCEPT事件,而又没有客户端来链接 所以他被阻塞在了select()

// 服务端

// 获取服务端的SerSokcetChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

// 配置成非阻塞的

serverSocketChannel.configureBlocking(false);

// 从通道中获取服务端的对象

ServerSocket serverSocket = serverSocketChannel.socket();

serverSocket.bind(new InetSocketAddress(1234));

// 创建选择器

Selector selector = Selector.open();

// 把通到注册到选择器上

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {

// 阻塞式等待 channel上有事件发生

int select = selector.select();

}

复制代码

select 方法概要

select(long); // 设置超时时间,在这个时间范围内轮询关心的事件(如上面的OP_ACCEPT事件)

selectNow(); // 立即返回,不阻塞

select(); // 阻塞轮询复制代码第一步, cannelled-key中的每一个元素会从全集key set中剔除,表示这些可以关联的通道不会被注册

第二步操作系统帮我们轮询每一个通道是否有选择器关心的事情发生,对于一条准备就绪的channel(发生事件通道),他至少会发生下面两件事之一:它的key会被添加进selected-key-set中,来标识它将被选中,进而处理

如果它的key,已经存在于selected-key-set集合中了,下一步就是它的 read-operation将被更新

第三步: 如果在轮询时发现了有任何key被放置在了cannelled-key-set中,重复第一步,不再注册它关联的通道

2.

客户端创建代表自己的SocketChannel, 创建选择器,把自己感兴趣的事件注册在上面,如下代码, 初始化自己,SocketChannel, 把客户端的通道注册进选择器,并告诉选择器SocketChannel的感兴趣事件是OP_CONNECT连接事件

// 获取客户端的通道

SocketChannel socketChannel = SocketChannel.open();

socketChannel.configureBlocking(false);

Selector selector = Selector.open();

// 把客户端的通道注册进选择器

socketChannel.register(selector, SelectionKey.OP_CONNECT);

// 连接客户端, 执行完这行代码后, 服务端就能就收到通知

socketChannel.connect(new InetSocketAddress("localhost", 1234));

while (true) {

int number = selector.select(); // 选择器阻塞式的等待 Channel上发生它关心的事件

System.out.println(" 发生了感兴趣的事件: " + number);

Set keySet = selector.selectedKeys();

// 验证

for (SelectionKey selectionKey : keySet) {

SocketChannel client = null;

if (selectionKey.isConnectable()) {

// 强转成 有连接事件发生的Channel

client = (SocketChannel) selectionKey.channel();

// 完成连接

if (client.isConnectionPending()) {

client.finishConnect();

ByteBuffer byteBuffer = ByteBuffer.allocate(512);

byteBuffer.put((LocalDate.now() + "连接成功").getBytes());

byteBuffer.flip();

client.write(byteBuffer);

}

}

复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值