【對線面試官】Java NIO

  • 2021 年 1 月 11 日
  • 筆記

服務端:

public class NoBlockServer {

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

        // 1.獲取通道
        ServerSocketChannel server = ServerSocketChannel.open();

        // 2.切換成非阻塞模式
        server.configureBlocking(false);

        // 3. 綁定連接
        server.bind(new InetSocketAddress(6666));

        // 4. 獲取選擇器
        Selector selector = Selector.open();

        // 4.1將通道註冊到選擇器上,指定接收「監聽通道」事件
        server.register(selector, SelectionKey.OP_ACCEPT);

        // 5. 輪訓地獲取選擇器上已「就緒」的事件--->只要select()>0,說明已就緒
        while (selector.select() > 0) {
            // 6. 獲取當前選擇器所有註冊的「選擇鍵」(已就緒的監聽事件)
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

            // 7. 獲取已「就緒」的事件,(不同的事件做不同的事)
            while (iterator.hasNext()) {

                SelectionKey selectionKey = iterator.next();

                // 接收事件就緒
                if (selectionKey.isAcceptable()) {

                    // 8. 獲取客戶端的鏈接
                    SocketChannel client = server.accept();

                    // 8.1 切換成非阻塞狀態
                    client.configureBlocking(false);

                    // 8.2 註冊到選擇器上-->拿到客戶端的連接為了讀取通道的數據(監聽讀就緒事件)
                    client.register(selector, SelectionKey.OP_READ);

                } else if (selectionKey.isReadable()) { // 讀事件就緒

                    // 9. 獲取當前選擇器讀就緒狀態的通道
                    SocketChannel client = (SocketChannel) selectionKey.channel();

                    // 9.1讀取數據
                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    // 9.2得到文件通道,將客戶端傳遞過來的圖片寫到本地項目下(寫模式、沒有則創建)
                    FileChannel outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

                    while (client.read(buffer) > 0) {
                        // 在讀之前都要切換成讀模式
                        buffer.flip();

                        outChannel.write(buffer);

                        // 讀完切換成寫模式,能讓管道繼續讀取文件的數據
                        buffer.clear();
                    }
                }
                // 10. 取消選擇鍵(已經處理過的事件,就應該取消掉了)
                iterator.remove();
            }
        }

    }
}

客戶端:

public class NoBlockClient {

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

        // 1. 獲取通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 6666));

        // 1.1切換成非阻塞模式
        socketChannel.configureBlocking(false);

        // 1.2獲取選擇器
        Selector selector = Selector.open();

        // 1.3將通道註冊到選擇器中,獲取服務端返回的數據
        socketChannel.register(selector, SelectionKey.OP_READ);

        // 2. 發送一張圖片給服務端吧
        FileChannel fileChannel = FileChannel.open(Paths.get("X:\\Users\\ozc\\Desktop\\面試造火箭\\1.png"), StandardOpenOption.READ);

        // 3.要使用NIO,有了Channel,就必然要有Buffer,Buffer是與數據打交道的呢
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 4.讀取本地文件(圖片),發送到服務器
        while (fileChannel.read(buffer) != -1) {

            // 在讀之前都要切換成讀模式
            buffer.flip();

            socketChannel.write(buffer);

            // 讀完切換成寫模式,能讓管道繼續讀取文件的數據
            buffer.clear();
        }


        // 5. 輪訓地獲取選擇器上已「就緒」的事件--->只要select()>0,說明已就緒
        while (selector.select() > 0) {
            // 6. 獲取當前選擇器所有註冊的「選擇鍵」(已就緒的監聽事件)
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

            // 7. 獲取已「就緒」的事件,(不同的事件做不同的事)
            while (iterator.hasNext()) {

                SelectionKey selectionKey = iterator.next();

                // 8. 讀事件就緒
                if (selectionKey.isReadable()) {

                    // 8.1得到對應的通道
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    ByteBuffer responseBuffer = ByteBuffer.allocate(1024);

                    // 9. 知道服務端要返迴響應的數據給客戶端,客戶端在這裡接收
                    int readBytes = channel.read(responseBuffer);

                    if (readBytes > 0) {
                        // 切換讀模式
                        responseBuffer.flip();
                        System.out.println(new String(responseBuffer.array(), 0, readBytes));
                    }
                }

                // 10. 取消選擇鍵(已經處理過的事件,就應該取消掉了)
                iterator.remove();
            }
        }
    }

}

文章以純面試的角度去講解,所以有很多的細節是未鋪墊的。

鑒於很多同學反饋沒看懂【對線面試官】系列,基礎相關的知識我確實寫過文章講解過啦,但有的同學就是不愛去翻。

為了讓大家有更好的體驗,我把基礎文章也找出來(重要的知識點我還整理過電子書,比如說像多線程、集合這種面試必考的早就已經轉成PDF格式啦)

我把這些上傳到網盤,你們有需要直接下載就好了。做到這份上了,不會還想白嫖吧點贊轉發又不用錢。

鏈接://pan.baidu.com/s/1pQTuKBYsHLsUR5ORRAnwFg 密碼:3wom

歡迎關注我的微信公眾號【Java3y】來聊聊Java面試