Java NIO深入理解ServerSocketChannel

Java NIO 簡介

JAVA NIO有兩種解釋:一種叫非阻塞IO(Non-blocking I/O),另一種也叫新的IO(New I/O),其實是同一個概念。它是一種同步非阻塞的I/O模型,也是I/O多路複用的基礎,已經被越來越多地應用到大型應用服務器,成爲解決高併發與大量連接、I/O處理問題的有效方式。

NIO是一種基於通道和緩衝區的I/O方式,它可以使用Native函數庫直接分配堆外內存(區別於JVM的運行時數據區),然後通過一個存儲在java堆裏面的DirectByteBuffer對象作爲這塊內存的直接引用進行操作。這樣能在一些場景顯著提高性能,因爲避免了在Java堆和Native堆中來回複製數據。

Java NIO組件

NIO主要有三大核心部分:Channel(通道),Buffer(緩衝區), Selector(選擇器)。傳統IO是基於字節流和字符流進行操作(基於流),而NIO基於Channel和Buffer(緩衝區)進行操作,數據總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。Selector(選擇區)用於監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個線程可以監聽多個數據通道。

Buffer

Buffer(緩衝區)是一個用於存儲特定基本類型數據的容器。除了boolean外,其餘每種基本類型都有一個對應的buffer類。Buffer類的子類有ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer 。

Channel

Channel(通道)表示到實體,如硬件設備、文件、網絡套接字或可以執行一個或多個不同 I/O 操作(如讀取或寫入)的程序組件的開放的連接。Channel接口的常用實現類有FileChannel(對應文件IO)、DatagramChannel(對應UDP)、SocketChannel和ServerSocketChannel(對應TCP的客戶端和服務器端)。Channel和IO中的Stream(流)是差不多一個等級的。只不過Stream是單向的,譬如:InputStream, OutputStream.而Channel是雙向的,既可以用來進行讀操作,又可以用來進行寫操作。

Selector

Selector(選擇器)用於監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個的線程可以監聽多個數據通道。即用選擇器,藉助單一線程,就可對數量龐大的活動I/O通道實施監控和維護。

Java NIO的簡單實現

public class Demo1 {

    private static Integer port = 8080;

    // 通道管理器(Selector)
    private static Selector selector;

    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS, new LinkedTransferQueue<>(), new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        try {
            // 創建通道ServerSocketChannel
            ServerSocketChannel open = ServerSocketChannel.open();

            // 將通道設置爲非阻塞
            open.configureBlocking(false);

            // 綁定到指定的端口上
            open.bind(new InetSocketAddress(port));

            // 通道管理器(Selector)
            selector = Selector.open();

            /**
             * 將通道(Channel)註冊到通道管理器(Selector),併爲該通道註冊selectionKey.OP_ACCEPT事件
             * 註冊該事件後,當事件到達的時候,selector.select()會返回,
             * 如果事件沒有到達selector.select()會一直阻塞。
             */
            open.register(selector, SelectionKey.OP_ACCEPT);

            // 循環處理
            while (true) {
                /**
                 * 當註冊事件到達時,方法返回,否則該方法會一直阻塞
                 * 該Selector的select()方法將會返回大於0的整數,該整數值就表示該Selector上有多少個Channel具有可用的IO操作
                 */
                int select = selector.select();
                System.out.println("當前有 " + select + " 個channel可以操作");

                // 一個SelectionKey對應一個就緒的通道
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    // 獲取事件
                    SelectionKey key = iterator.next();

                    // 移除事件,避免重複處理
                    iterator.remove();

                    // 客戶端請求連接事件,接受客戶端連接就緒
                    if (key.isAcceptable()) {
                        accept(key);
                    } else if (key.isReadable()) {
                        // 監聽到讀事件,對讀事件進行處理
                        threadPoolExecutor.submit(new NioServerHandler(key));
                    }
                }

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

    /**
     * 處理客戶端連接成功事件
     *
     * @param key
     */
    public static void accept(SelectionKey key) {
        try {
            // 獲取客戶端連接通道
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);

            // 給通道設置讀事件,客戶端監聽到讀事件後,進行讀取操作
            sc.register(selector, SelectionKey.OP_READ);
            System.out.println("accept a client : " + sc.socket().getInetAddress().getHostName());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 監聽到讀事件,讀取客戶端發送過來的消息
     */
    public static class NioServerHandler implements Runnable {

        private SelectionKey selectionKey;

        public NioServerHandler(SelectionKey selectionKey) {
            this.selectionKey = selectionKey;
        }

        @Override
        public void run() {
            try {
                if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    // 從通道讀取數據到緩衝區
                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    // 輸出客戶端發送過來的消息
                    socketChannel.read(buffer);
                    buffer.flip();
                    System.out.println("收到客戶端" + socketChannel.socket().getInetAddress().getHostName() + "的數據:" + new String(buffer.array()));

                    //將數據添加到key中
                    ByteBuffer outBuffer = ByteBuffer.wrap(buffer.array());

                    // 將消息回送給客戶端
                    socketChannel.write(outBuffer);
                    selectionKey.cancel();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

原文地址 https://www.51csdn.cn/article/404.html

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