Java NIO Selector 演示
一、基本参数
1.属性
SelectionKey: 通过select方法监听通道事件,有事件发生时将该通道添加到SelectionKey集合并返回;遍历SelectionKey可获得对应通道
2.方法
select() 阻塞,直到至少有一个事件发生
select(time) 阻塞time毫秒数,没有事件就返回
wakeup() 唤醒
selectNow() 不阻塞,立即返回
3.流程
1.当 Client 连接时,ServerSocketChannel 得到 SocketChannel
2.SocketChannel 注册到 Selector 上,一个 Selector 可以注册多个 SocketChannel
3.注册后返回一个 SelectionKey 将 Selector 和 SocketChannel 关联
4.Selector 通过 select 方法监听事件 OP_READ OP_WRITE OP_CONNECT OP_ACCEPT
5.获取有事件发生的 SelectionKey ,通过channel方法获取对应 SocketChannel,进行业务处理
二、通信演示
1.服务端
package com.example.netty.io.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
/**
* Description: 非阻塞 IO 服务端
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2021-11-03 23:32
* @version: V1.0.0
*/
public class NIOServer {
public static void main(String[] args) throws Exception {
//创建serverSocketChannel -> serverSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//获取Selector
Selector selector = Selector.open();
//监听
serverSocketChannel.socket().bind(new InetSocketAddress(7800));
//设为非阻塞
serverSocketChannel.configureBlocking(false);
//把serverSocketChannel注册到selector 并将关心事件设为 OP_ACCEPT
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//循环等待客户端连接
while (true){
if (selector.select(1000)==0){
//System.out.println("服务器等待了 1S 没有事件发生");
continue;
}
/**
* 1.如果返回>0 就获取相关的 selectionKeySet
* 2.selector.selectedKeys() 返回关心事件的集合
* 3.通过 selectionKeySet 反向获取通道
*/
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeySet.iterator();
while (iterator.hasNext()){
SelectionKey k = iterator.next();
//根据事件类型做处理
if(k.isAcceptable()){
//如果是连接请求,就为客户端生成一个SocketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
//把serverSocketChannel注册到selector 并将关心事件设为 OP_READ 同时关联一个 Buffer
socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
System.out.println("客户端连接成功,生成了一个 SocketChannel : " + socketChannel.hashCode());
}
if (k.isReadable()){
//通过 selectionKeySet 反向获取通道
SocketChannel channel = (SocketChannel)k.channel();
//获取到该Channel关联的Buffer
ByteBuffer buffer = (ByteBuffer)k.attachment();
int read = channel.read(buffer);
String message = new String(buffer.array(), StandardCharsets.UTF_8);
System.out.println("From Client Message : " + message.trim());
if (read==-1){
channel.close();
}
}
//手动从集合中移除当前的 SelectionKey 防止重复操作
iterator.remove();
}
}
}
}
2.客户端
package com.example.netty.io.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
/**
* Description: 非阻塞 IO 客户端
*
* @Author: zhx & moon hongxu_1234@163.com
* @Date: 2021-11-04 0:09
* @version: V1.0.0
*/
public class NIOClient {
public static void main(String[] args) throws IOException {
//得到一个网络通道
SocketChannel socketChannel = SocketChannel.open();
//设置非阻塞
socketChannel.configureBlocking(false);
//设置服务地址和端口
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",7800);
//连接服务器
if(!socketChannel.connect(inetSocketAddress)){
while (!socketChannel.finishConnect()){
System.out.println("因为连接需要时间 客户端不会阻塞 可以处理其他工作...");
}
}
//如果连接成功,就发送数据
String str = "hello moon";
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8));
//将 buffer 数据 写入 channel
socketChannel.write(buffer);
//改变通道状态
buffer.clear();
buffer.flip();
//使代码停住
System.in.read();
//关闭连接
//socketChannel.close();
}
}