【網絡編程】還在爲編寫NIO煩惱?那是因爲你沒有摸清脈絡

阻塞I/O


當read沒有數據時,將阻塞一直等待有數據爲止,同時需要爲每一個socket創建一個線程,耗費資源。


多路複用


現在只要搜NIO相關的知識,都會告訴你使用了IO多路複用技術?那麼什麼是IO多路複用?多路是什麼多路?複用又是複用了什麼內容?

多路:多個Socket連接,即是NIO網絡編程中的多個channel。

複用:複用是指複用一個線程對多路Socket連接的狀態進行監控,實現對多個I/O流的管理能力。




1、NIO被稱爲非阻塞式IO就是selector的緣故,在阻塞IO中會一直等待read有數據到來,而在NIO中根據事件來開始流的讀寫,無須一直等待。更牛的是NIO一樣可以使用阻塞模式,通過configureBlocking進行配置。


2、NIO的核心就是事件註冊和事件輪詢,通過Selector實現。


反應器模式




看完圖是不是有點迷惑這個不是與觀察者一樣嗎? 當事件發生改變,就通知所有需要知道事件的人。其實反應器模式和觀察者模式最大的不同在於一個是單事件源(觀察者模式),一個是多事件源(反應器模式)。


實戰


程序一:NIO服務端代碼



public class MyServer {

    Selector selector;

    ServerSocketChannel serverSocketChannel;

    ByteBuffer bb = ByteBuffer.allocate(200);

    public static void main(String[] args) throws IOException {
        new MyServer().init();
    }

    public void init() throws IOException {
        /// 第一步:打開selector
        selector = Selector.open();

        /// 第二步:打開serverSocketChannel通道,並設置爲非阻塞模式
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);

        /// 第三步: 將serverSocketChannel註冊爲可以隨時接受連接
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        handler();
    }

    public void handler() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        // 阻塞模式 ,等待新的事件發生
                        selector.select();

                        Iterator<SelectionKey> itor = selector.selectedKeys().iterator();
                        while (itor.hasNext()) {
                            SelectionKey sk = itor.next();
                            itor.remove(); // 防止重複處理註冊的事件

                            // 已經準備好接受連接 - 肯定是針對服務端纔有
                            if (sk.isAcceptable()) {
                                ServerSocketChannel ss = (ServerSocketChannel) sk.channel();
                                SocketChannel ssc = ss.accept();// 獲取一個連接上來的socketChannel
                                ssc.configureBlocking(false); // 設置通道不爲阻塞模式
                                ssc.register(selector, SelectionKey.OP_READ);
                            } else
                            // 準備好讀取數據
                            if (sk.isReadable()) {
                                SocketChannel ss = null;
                                try {
                                    ss = (SocketChannel) sk.channel();
                                    int i = ss.read(bb);
                                    if(i != -1) {
                                        bb.flip();
                                        System.out.println(new String(bb.array(), 0, bb.limit()));
                                        System.out.println("數據讀入完成");
                                        bb.clear();
                                    }else {
                                        ss.close();
                                    }

                                } catch (Exception e) {

                                }
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章