非阻塞通信(服務器端)

一.非阻塞
1.非阻塞:線程執行方法時,如果操作沒有就緒,就立即返回,不會一直等待操作就緒

2.java.nio提供非阻塞通信的類:
1)ServerSocketChannel:代替Server
2)SocketChannel:代替Socket
3)Selector:監控就緒事件
4)SelectionKey:註冊事件的句柄

3.服務器端程序使用多線程處理阻塞IO,雖然可以響應多用戶,但是存在侷限性
1)JVM會給每個線程分配獨立的資源,線程越多,系統開銷越大
2)容易造成死鎖
3)需要頻繁轉讓CPU使用

4.完成多個任務可以使用多線程分別處理,也可以讓單個線程以輪詢的工作方式處理就緒的任務

二.創建非阻塞服務器端程序
對於服務區端造成阻塞的原因有:
1)客戶端的連接
2)接收客戶端的數據
3)響應客戶端的數據
所以,可以在主線程中分別監控這些任務,當有任何一個就緒就執行

public class nio_demo2 {

    private Selector selector = null;
    private ServerSocketChannel serverSocketChannel = null;
    private int port = 6666;
    private Charset charset = Charset.forName("UTF-8");

    public nio_demo2() throws IOException {

        selector = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        System.out.println("server start...");
    }

    public void service() throws IOException {

        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //select():監控註冊的channel,當有需要處理的IO時,將對應的selectionKey加入到selected-key集合
        while(selector.select()>0){

            Set readyKey = selector.selectedKeys();
            Iterator it = readyKey.iterator();
            while(it.hasNext()){
                SelectionKey key = null;
                try{
                    key = (SelectionKey)it.next();
                    it.remove();

                    if(key.isAcceptable()){
                        ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                        SocketChannel socketChannel = ssc.accept();
                        System.out.println("client connection" +
                                socketChannel.socket().getInetAddress()+":"
                                +socketChannel.socket().getPort());
                        //設置爲非阻塞
                        socketChannel.configureBlocking(false);
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        //向selecctor註冊讀就緒事件和寫就緒時件,同時關聯buffer
                        socketChannel.register(selector,
                                SelectionKey.OP_READ|SelectionKey.OP_WRITE,buffer);
                    }

                    if(key.isReadable()){
                        receive(key);
                    }
                    if(key.isWritable()){
                        send(key);
                    }
                }catch (IOException e){
                    e.printStackTrace();
                    try{
                        if(key!=null){
                            key.cancel();
                            key.channel().close();
                        }
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }

                }

            }
        }
    }
    //發送,處理寫就緒事件
    public void send(SelectionKey key) throws IOException {
        //獲得關聯的附件
        ByteBuffer buffer = (ByteBuffer)key.attachment();
        SocketChannel socketChannel = (SocketChannel)key.channel();
        buffer.flip();
        String data = decode(buffer);
        if(data.indexOf("\n")==-1){
            return;
        }
        String outputData = data.substring(0,data.indexOf("\n")+1);
        System.out.println(outputData);
        ByteBuffer outputBuffer = encode("echo:"+outputData);
        //發送一行數據
        while(outputBuffer.hasRemaining()){
            socketChannel.write(outputBuffer);
        }
        //刪除已處理數據(讀寫操作同一個ByteBuffer)
        ByteBuffer temp = encode(outputData);
        buffer.position(temp.limit());
        buffer.compact();

        if(outputData.equals("end\n")){
            key.cancel();
            socketChannel.close();
            System.out.println("close coonnection");
        }
    }
    //接收,處理讀就緒事件
    public void receive(SelectionKey key) throws IOException {
        //獲取關聯的buffer
        ByteBuffer buffer = (ByteBuffer)key.attachment();
        //獲取關聯的channel
        SocketChannel socketChannel = (SocketChannel)key.channel();
        ByteBuffer readBuffer = ByteBuffer.allocate(32);
        socketChannel.read(readBuffer);
        //flip:將極限設置爲位置,將位置設置爲0
        readBuffer.flip();

        buffer.limit(buffer.capacity());
        buffer.put(readBuffer);
    }
    //解碼
    public String decode(ByteBuffer buffer){
        CharBuffer charBuffer = charset.decode(buffer);
        return charBuffer.toString();
    }
    //編碼
    public ByteBuffer encode(String str){
        return charset.encode(str);
    }

    public static void main(String[] args) throws IOException {
        nio_demo2 server = new nio_demo2();
        server.service();
    }
}
發佈了60 篇原創文章 · 獲贊 14 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章