1. 程式人生 > >Java NIO 系列教程(四)

Java NIO 系列教程(四)

阻塞式與非阻塞式(主要針對網路程式設計而言)

  • 阻塞式:
    比如到你某個時候到A樓一層(假如是核心緩衝區)取快遞,但是你不知道快遞什麼時候過來,你又不能幹別的事,只能死等著。

  • 非阻塞式:
    還是等快遞的例子:你知道今天要來快遞,等快遞員給你打電話,在快遞員打電話之前你可以忙自己的事情,等接到電話後再過去取,這就是非阻塞式的。

為進一步提高IO的效率,使用了多執行緒來解決這個問題,但是執行緒的數量總是會因為CPU的切換到達負荷值。

這裡寫圖片描述

選擇器是用來監控通道的I/O的狀態,等Client的資料準備就緒,就通知Server來處理,這期間Server中的執行緒可以做其他的事情。

使用NIO完成網路通訊的三個核心

1)連線Channel
2)緩衝區Buffer
3)選擇器Select:用於監控SelectbleChannel的IO狀態。

阻塞式網路通訊實現

    @Test
    public void Server() throws IOException {
        // 獲取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        FileChannel fon = FileChannel.open(Paths.get("doc2"), StandardOpenOption.WRITE
); // 繫結埠號 ssChannel.bind(new InetSocketAddress(9989)); // 接受來自客戶端的請求 SocketChannel sChannel = ssChannel.accept(); // 建立緩衝區用於傳輸資料 ByteBuffer buf = ByteBuffer.allocate(1024); while (sChannel.read(buf) != -1) { buf.flip(); fon.write
(buf); buf.clear(); } String str = new String("傳輸完成"); buf.put(str.getBytes()); buf.flip(); sChannel.write(buf); sChannel.shutdownOutput(); ssChannel.close(); fon.close(); sChannel.close(); } @Test public void Client() throws IOException { // 獲取通道 SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9989)); // 關聯檔案,將檔案資料傳送到客戶端,並且儲存接收來自客戶端的資料 FileChannel fin = FileChannel.open(Paths.get("doc"), StandardOpenOption.READ, StandardOpenOption.WRITE); // 利用緩衝區儲存資料 ByteBuffer buf = ByteBuffer.allocate(1024); // 利用通道傳輸資料 while (fin.read(buf) != -1) { buf.flip(); sChannel.write(buf); buf.clear(); } int length = 0; while ((length = sChannel.read(buf)) != -1) { buf.flip(); System.out.println(new String(buf.array(), 0, length)); buf.clear(); } sChannel.shutdownOutput(); sChannel.close(); fin.close(); }

非阻塞式實現

@Test
    public void Client() throws IOException {
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9988));
        // 切換為非阻塞式的
        sChannel.configureBlocking(false);
        // 申請緩衝區
        ByteBuffer buf = ByteBuffer.allocate(1024);
        // 寫入日期
        buf.put(new Date().toString().getBytes());
        buf.flip();
        sChannel.write(buf);
        // 關閉緩衝區
        sChannel.close();
    }

    @Test
    public void Server() throws IOException {
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        // 繫結埠號
        ssChannel.bind(new InetSocketAddress(9988));
        // 切換為非阻塞式
        ssChannel.configureBlocking(false);
        // 構造選擇器
        Selector selector = Selector.open();
        // 將通道註冊到相應的選擇器上(後面的引數表示監控的通道的狀態:包含讀、寫、連線、接收)
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 通過選擇器輪迴的方式獲取選擇器上已經準備就緒的事件
        while (selector.select() > 0) {
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    SocketChannel scChannel = ssChannel.accept();
                    scChannel.configureBlocking(false);
                    // 監控讀就緒狀態
                    scChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    // 獲取當前選擇器上“讀就緒狀態”的通道
                    SocketChannel sChannel = (SocketChannel) selectionKey.channel();
                    // 讀取資料
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    int len = 0;
                    while ((len = sChannel.read(buf)) != -1) {
                        buf.flip();
                        System.out.println(new String(buf.array()));
                        buf.clear();
                    }
                }
                // 取消選擇鍵
                iterator.remove();
            }
        }
    }