博客 / 詳情

返回

Java Nio Example

selector模型

使用一個線程去監控多個IO請求,如果哪一個IO數據準備完畢後就通知相應的線程來處理

select模型,它的基本原理是採用輪詢和遍歷的方式。也就是説,在客户端操作服務器時,會創建三種文件描述符,簡稱FD。分別是writefds(寫描述符)、readfds(讀描述符)和 exceptfds(異常描述符)

image.png

demo,讓主線程監聽IO事件然後進行處理

server

public class NioServerExample {

    public static void main(String[] args) throws IOException, InterruptedException {

        Selector selector = getSelector();
        listen(selector);
    }

    public static Selector getSelector() throws IOException {
        Selector selector = Selector.open();

        //創建可選通道,設置非阻塞
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);

        //綁定通道到指定端口
        ServerSocket socket = serverSocketChannel.socket();
        socket.bind(new InetSocketAddress(8080));

        //向selector註冊IO事件,首先註冊SelectionKey.OP_ACCEPT讓server accept監聽
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        return selector;
    }

    public static void listen(Selector selector) throws IOException, InterruptedException {
        while (selector.select() > 0) {
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            if (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                //判斷IO事件類型進行處理
                process(selector, key);
                iterator.remove();
            }
        }
    }

    private static void process(Selector selector, SelectionKey key) throws IOException, InterruptedException {
        if (key.isAcceptable()) {
            System.out.println(Thread.currentThread().getName()+" =>事件類型 accept");
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel channel = server.accept();
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            System.out.println(Thread.currentThread().getName()+" =>事件類型 read");
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(500);
            int len = channel.read(byteBuffer);
            if (len > 0) {
                String content = new String(byteBuffer.array(), 0, len);
                System.out.println(content);
                channel.register(selector, SelectionKey.OP_WRITE);
            }
            byteBuffer.clear();
        } else if (key.isWritable()) {
            System.out.println(Thread.currentThread().getName()+" =>事件類型 write");
            SocketChannel channel = (SocketChannel) key.channel();
            String str = "client fuck you I am Nio Server";
            channel.write(ByteBuffer.wrap(str.getBytes()));
            channel.register(selector, SelectionKey.OP_READ);
//            channel.close(); //向客户端發送數據後管別此通道連接

        }
    }
}

client

public class NioClientExample0 {

    public static void main(String[] args) throws IOException, InterruptedException {
        Selector selector = Selector.open();
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_WRITE);

        String info = null;
        Scanner scanner = new Scanner(System.in);
        ByteBuffer byteBuffer0 = ByteBuffer.allocate(100);
        ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
        while (true) {
            int len = socketChannel.read(byteBuffer1);
            System.out.println(new String(byteBuffer1.array(), 0, len));
            byteBuffer1.clear();


            info = scanner.next();
            byteBuffer0.put(info.getBytes());
            byteBuffer0.flip();
            socketChannel.write(byteBuffer0);
            byteBuffer0.clear();
        }

    }

}

參考

image.png

舉例如下,開啓服務端和一個客户端後,用客户端向服務端發送一條數據

image.png

當服務端接收到數據並回寫給客户端後,又被註冊上讀事件,準備接收數據
image.png

這時再開啓一個客户端,會發現服務端沒有一直等待讀事件的IO,而是轉身處理accept接收新的連接去了
image.png

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.