1. 程式人生 > >java nio之selector

java nio之selector

管理器 row open pack 初始 選擇 非阻塞 註意 管道

  一、selector簡介:選擇器提供選擇執行已經就緒的任務的能力.從底層來看,Selector提供了詢問通道是否已經準備好執行每個I/O操作的能力。Selector 允許一個單一的線程來操作多個 Channel。僅用單個線程來處理多個Channels的好處是,只需要更少的線程來處理通道。事實上,可以只用一個線程處理所有的通道,這樣會大量的減少線程之間上下文切換的開銷。

  二、選擇器的創建以及使用

  1)創建 

Selector selector = Selector.open();

  2)註冊選擇器(Channel這裏不介紹)

socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ)

  註意:一個通道註冊到選擇器中,必須是非阻塞的。

  3)註冊模式有4種

SelectionKey.OP_CONNECT 
SelectionKey.OP_ACCEPT 
SelectionKey.OP_READ 
SelectionKey.OP_WRITE

  4)SelectionKey的使用

  在選擇其中會存在多個選擇鍵SelectionKey,每一個選擇鍵的類型可能不一樣,所以我們這裏需要判定是哪一種類型

selector.selectedKeys() //獲取所有選擇鍵
selectionKey.isConnectable() //是否是連接選擇鍵 selectionKey.isReadable() //讀取 selectionKey.isWritable() //寫入 selectionKey.isAcceptable() //接收

  獲取對應的選擇鍵過後可以強轉成對應的通信管道。(示例)

SocketChannel channel = (SocketChannel) selectionKey.channel();

  三、聊天室的基本寫法(基本使用都在裏面)

  1)客戶端

package com.troy.nio.application;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class Client { //選擇器 private static Selector selector; //通信管道 private static SocketChannel socketChannel; public static void main(String[] args) { try { clientInit(); listen(); //發送數據 while (true) { Thread.sleep(1000); socketChannel.write(ByteBuffer.wrap(("hello server!").getBytes())); } } catch (Exception e) { e.printStackTrace(); } } //初始化選擇器和發送數據 private static void clientInit() throws Exception { //打開一個通道管理器 selector = Selector.open(); //獲取一個通信管道 socketChannel = SocketChannel.open(); //設置對應的發送地址和端口 socketChannel.connect(new InetSocketAddress("localhost",9000)); //設置非阻塞 socketChannel.configureBlocking(false); //註冊一個寫入事件 socketChannel.register(selector, SelectionKey.OP_READ); } //監聽服務器返回的數據 private static void listen() throws Exception { Runnable runnable = () -> { while (true) { try { //這裏會一直阻塞,直到事件過來 selector.select(); //在選擇器中獲取對應的註冊事件 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { //註冊事件 SelectionKey selectionKey = iterator.next(); iterator.remove(); //判斷是否是讀事件 if (selectionKey.isReadable()) { //獲取對應通信管道,並處理層數據 SocketChannel channel = (SocketChannel) selectionKey.channel(); //一次性讀取數據量,這裏應該做循環,我這裏方便沒有做 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); channel.read(byteBuffer); byteBuffer.flip(); System.out.println(new String(byteBuffer.array()).trim()); } } } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } }; new Thread(runnable).start(); } }

  2)服務端

package com.troy.nio.application;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class Server {

    //選擇器
    private static Selector selector;
    //服務端通信管道
    private static ServerSocketChannel channel;
    public static void main(String[] args) {
        try {
            serverInit();
            listen();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //初始化
    private static void serverInit() throws IOException {
        //打開一個選擇器
        selector = Selector.open();
        //打開一個服務端通信管道
        channel = ServerSocketChannel.open();
        //設置接收端口
        channel.socket().bind(new InetSocketAddress(9000));
        //設置非阻塞
        channel.configureBlocking(false);
        //註冊接收事件
        channel.register(selector, SelectionKey.OP_ACCEPT);
    }

    //監聽
    private static void listen() throws IOException {
        while (true) {
            //形成阻塞事件,接口完成後進行下一步
            selector.select();
            //獲取選擇器中的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                //判斷是否是接受事件
                if (selectionKey.isAcceptable()) {
                    SocketChannel socketChannel = channel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector,SelectionKey.OP_READ);
                }
                //是否是可讀事件
                if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    //這裏的目的是當這個服務端一直存在,因為讀取數據存在異常,直接處理掉,下一個客戶端景來可以繼續接受
                    try {
                        socketChannel.read(byteBuffer);
                    } catch (Exception e) {
                        continue;
                    }
                    byteBuffer.flip();
                    System.out.println(new String(byteBuffer.array()).trim());
                    socketChannel.write(ByteBuffer.wrap("hello client!".getBytes()));
                }
            }
        }
    }
}

java nio之selector