背景: 在使用NIO通信过程中,新手往往会出现,通道关闭了或者socketChannel已经停止写数据,为什么客户端还在一直触发READ事件?
- 简单编写一个nio服务端和客户端通信的例子
public class SelectorExample {
//服务端
public static void main(String[] args) throws IOException {
//1.创建ServerSocket,绑定端口
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(6666));
serverSocketChannel.configureBlocking(false); //非阻塞
//2.创建selector,serverSocket注册selector,关心事件(接受连接准备就绪)
Selector selector= Selector.open();
//关心interest事件为accept
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
//3. select获取事件发生个数,0表示无事件
//等待连接,阻塞1000ms
if(selector.select(1000)==0){
// System.out.println("1000 ms current no connect...");
continue;
}
//4.获取发生事件的key列表
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isAcceptable()){
//
dealAcceptEvent(serverSocketChannel,selector);
}else if(key.isReadable()){
dealReadEvent(key);
}else{
System.out.println("no interest event...");
}
iterator.remove();
}
}
}
private static void dealReadEvent(SelectionKey key) throws IOException {
System.out.println("READ......");
//通过key反向获取到socketChannel
SocketChannel socketChannel=(SocketChannel)key.channel();
//得到与channel关联的数据
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
try {
//处理读事件,这个位置暂时注掉,证明nio是条件触发
// int read = socketChannel.read(byteBuffer);
// if(read>0){
// System.out.println(new String(byteBuffer.array()));
// }
}catch (Exception e){
System.out.println(socketChannel.hashCode()+ "socket channel closed...");
key.cancel();
socketChannel.close();
}
}
private static void dealAcceptEvent(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {
System.out.println("accept...");
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("socket连接...生成SocketChannel:"+socketChannel.hashCode());
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ,ByteBuffer.allocate(1024));
}
//客户端
public static class SocketClient{
public static void main(String[] args) throws IOException, InterruptedException {
SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("localhost",6666));
socketChannel.configureBlocking(false);
// if(!socketChannel.connect(new InetSocketAddress("localhost",6666))){
// while (!socketChannel.finishConnect()){
// System.out.println("等待完成连接...");
// }
// }
socketChannel.write(ByteBuffer.wrap("测试...123".getBytes("utf-8")));
Thread.sleep(Integer.MAX_VALUE);
}
}
}
在server代码中读事件逻辑,我这里暂且注掉,不去读取socketchannel中的数据,运行server和client,发现client明明只write一次,server一直在触发READ事件。
server没有读取socketChannel中的数据
- 分享两个术语:
- 水平触发(level-triggered,也被称为条件触发)LT: 只要满足条件,就触发一个事件(只要有数据没有被获取,内核就不断通知你)
- 边缘触发(edge-triggered)ET: 每当状态变化时,触发一个事件。
通过上面的试验结论: server没有读取通道里的数据或者通道数据没有全部读取完,操作系统会不断的通知selector,selector每次select的时候就一直有读事件产生。
网友评论