Java NIO水平触发机制

背景: 在使用NIO通信过程中,新手往往会出现,通道关闭了或者socketChannel已经停止写数据,为什么客户端还在一直触发READ事件?

  1. 简单编写一个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 selectionKeys = selector.selectedKeys();
            Iterator 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中的数据

  1. 分享两个术语:
  • 水平触发(level-triggered,也被称为条件触发)LT: 只要满足条件,就触发一个事件(只要有数据没有被获取,内核就不断通知你)
  • 边缘触发(edge-triggered)ET: 每当状态变化时,触发一个事件。

通过上面的试验结论: server没有读取通道里的数据或者通道数据没有全部读取完,操作系统会不断的通知selector,selector每次select的时候就一直有读事件产生。

你可能感兴趣的:(Java NIO水平触发机制)