NIO编程一直不太熟悉,一方面没有系统地学习过,另外一方面NIO编程确实有些复杂,一下没有用了过阵子容易忘记,因此,写篇小博客就颇有必要,这里来总结一下selector组件用法,Buffer和Channel有空补上。java
一张你可能熟悉的图(IO多路复用)编程
selector(选择器),IO多路复用的组件, 和它直接关联的组件是Channel, 它的做用就是不断的轮询绑定在他身上的channel。一旦有通道发生了它感兴趣的事件,接下来处理该事件。
socket
selector维护了三个set集合,里面封装的是 SelectionKey,用它来获取channel。学习
2.1 key set
spa
全集,每当channel经过register方法注册进选择器时,会把包含Channel本身信息的key添加到这个全集中来,注册的信息就会以SelectionKey的封装形式保存在这个集合中, 用这个SelectionKey来获取channel。操作系统
2.2 selected key setcode
感兴趣的key的集合,每次遍历selected key时咱们会执行这行代码:Set<SelectionKey> selectionKeys = selector.selectedKeys();
cdn
2.3 cannelled key setserver
通常会在客户端主动断开链接的时候使用它,表明原来感兴趣的事件,如今不感兴趣了,下一次轮询,进行select()
本集合中的SelectionKey会从key set中移除, 意味着它所关联的channel将会被选择器丢弃掉,再也不进行监听。
对象
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(); // 阻塞轮询复制代码
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<SelectionKey> 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);
}
}
复制代码