美文网首页
Java NIO水平触发机制

Java NIO水平触发机制

作者: bclz | 来源:发表于2019-11-29 13:48 被阅读0次

    背景: 在使用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<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事件。

    \color{red}{根本原因:}server没有读取socketChannel中的数据

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

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

    相关文章

      网友评论

          本文标题:Java NIO水平触发机制

          本文链接:https://www.haomeiwen.com/subject/muywwctx.html