NIO學習筆記

組成

  • CHANNEL:
  • Buffer:java

    • Capacity:容量

      最大容量是下標減一編程

    • Position:位置
    • Limit:上線
    • Mark:標記

相關API

Buffer

Buffer的使用--數據變化過程,數據讀寫方式。
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述安全

Selector--選擇器/多路複用器

  • 做用--IO就緒選擇
  • 地位--NIO網絡編程的基礎

Selector使用

  • 4個核心API:

在這裏插入圖片描述

  • SelectionKey

SelectionKey的四種就緒狀態常量:Connect、Accept、Read、Write。
有價值的屬性:經過SelectionKey集合獲取當前的Channel,Select對象、該Channel已就緒事件集合和所關心事件集合。服務器

NIO實現步驟:

  • 建立Selector
  • 建立ServerSocketChannel,並綁定監聽端口
  • 將Channel設置爲非阻塞模式
  • 將Channel註冊到Selector上,監聽鏈接事件
  • 循環調用Selector的select方法,檢測就緒狀況
  • 調用selectedKeys方法獲取就緒channel集合
  • 判斷就緒事件種類,調用相應的業務處理方法
  • 根據業務須要,決定是否再次註冊監聽事件,重複執行第三步操做

NIO聊天室代碼示例

  • NIO Server
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO服務器端
 */
public class NioServer {

    /**
     * 啓動
     */
    public void start() throws IOException {
        /**
         * 1. 建立Selector
         */
        Selector selector = Selector.open();

        /**
         * 2. 經過ServerSocketChannel建立channel通道
         */
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        /**
         * 3. 爲channel通道綁定監聽端口
         */
        serverSocketChannel.bind(new InetSocketAddress(8000));

        /**
         * 4. **設置channel爲非阻塞模式**
         */
        serverSocketChannel.configureBlocking(false);

        /**
         * 5. 將channel註冊到selector上,監聽鏈接事件
         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服務器啓動成功!");

        /**
         * 6. 循環等待新接入的鏈接
         */
        for (;;) { // while(true) c for;;
            /**
             * TODO 獲取可用channel數量
             */
            int readyChannels = selector.select();

            /**
             * TODO 爲何要這樣!!?
             */
            if (readyChannels == 0) continue;

            /**
             * 獲取可用channel的集合
             */
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            Iterator iterator = selectionKeys.iterator();

            while (iterator.hasNext()) {
                /**
                 * selectionKey實例
                 */
                SelectionKey selectionKey = (SelectionKey) iterator.next();

                /**
                 * **移除Set中的當前selectionKey**
                 */
                iterator.remove();

                /**
                 * 7. 根據就緒狀態,調用對應方法處理業務邏輯
                 */
                /**
                 * 若是是 接入事件
                 */
                if (selectionKey.isAcceptable()) {
                    acceptHandler(serverSocketChannel, selector);
                }

                /**
                 * 若是是 可讀事件
                 */
                if (selectionKey.isReadable()) {
                    readHandler(selectionKey, selector);
                }
            }
        }
    }

    /**
     * 接入事件處理器
     */
    private void acceptHandler(ServerSocketChannel serverSocketChannel,
                               Selector selector)
            throws IOException {
        /**
         * 若是要是接入事件,建立socketChannel
         */
        SocketChannel socketChannel = serverSocketChannel.accept();

        /**
         * 將socketChannel設置爲非阻塞工做模式
         */
        socketChannel.configureBlocking(false);

        /**
         * 將channel註冊到selector上,監聽 可讀事件
         */
        socketChannel.register(selector, SelectionKey.OP_READ);

        /**
         * 回覆客戶端提示信息
         */
        socketChannel.write(Charset.forName("UTF-8")
                .encode("你與聊天室裏其餘人都不是朋友關係,請注意隱私安全"));
    }

    /**
     * 可讀事件處理器
     */
    private void readHandler(SelectionKey selectionKey, Selector selector)
            throws IOException {
        /**
         * 要從 selectionKey 中獲取到已經就緒的channel
         */
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

        /**
         * 建立buffer
         */
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        /**
         * 循環讀取客戶端請求信息
         */
        String request = "";
        while (socketChannel.read(byteBuffer) > 0) {
            /**
             * 切換buffer爲讀模式
             */
            byteBuffer.flip();

            /**
             * 讀取buffer中的內容
             */
            request += Charset.forName("UTF-8").decode(byteBuffer);
        }

        /**
         * 將channel再次註冊到selector上,監聽他的可讀事件
         */
        socketChannel.register(selector, SelectionKey.OP_READ);

        /**
         * 將客戶端發送的請求信息 廣播給其餘客戶端
         */
        if (request.length() > 0) {
            // 廣播給其餘客戶端
            broadCast(selector, socketChannel, request);
        }
    }

    /**
     * 廣播給其餘客戶端
     */
    private void broadCast(Selector selector,
                           SocketChannel sourceChannel, String request) {
        /**
         * 獲取到全部已接入的客戶端channel
         */
        Set<SelectionKey> selectionKeySet = selector.keys();

        /**
         * 循環向全部channel廣播信息
         */
        selectionKeySet.forEach(selectionKey -> {
            Channel targetChannel = selectionKey.channel();

            // 剔除發消息的客戶端
            if (targetChannel instanceof SocketChannel
                    && targetChannel != sourceChannel) {
                try {
                    // 將信息發送到targetChannel客戶端
                    ((SocketChannel) targetChannel).write(
                            Charset.forName("UTF-8").encode(request));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 主方法
     * @param args
     */
    public static void main(String[] args) throws IOException {
        new NioServer().start();
    }

}
  • NIO Client
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;

/**
 * NIO客戶端
 */
public class NioClient {

    /**
     * 啓動
     */
    public void start(String nickname) throws IOException {
        /**
         * 鏈接服務器端
         */
        SocketChannel socketChannel = SocketChannel.open(
                new InetSocketAddress("127.0.0.1", 8000));

        /**
         * 接收服務器端響應
         */
        // 新開線程,專門負責來接收服務器端的響應數據
        // selector , socketChannel , 註冊
        Selector selector = Selector.open();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        new Thread(new NioClientHandler(selector)).start();

        /**
         * 向服務器端發送數據
         */
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String request = scanner.nextLine();
            if (request != null && request.length() > 0) {
                socketChannel.write(
                        Charset.forName("UTF-8")
                                .encode(nickname + " : " + request));
            }
        }

    }

    public static void main(String[] args) throws IOException {
//        new NioClient().start();
    }

}
  • A/B/C Client
import java.io.IOException;

public class AClient {

    public static void main(String[] args)
            throws IOException {
        new NioClient().start("A/B/C Client");
    }

}
相關文章
相關標籤/搜索