NIO完成網絡通訊(一)

NIO:即非阻塞式IOhtml

 

視頻教程:   https://chuanke.baidu.com/v1982732-211322-1316084.html java

 

使用步驟:服務器

一、建立 ServerSocketChannel 和業務處理線程池。
二、綁定監聽端口,並配置爲非阻塞模式。
三、建立 多路複用器Selector,將以前建立的 ServerSocketChannel 註冊到 Selector 上,監聽 SelectionKey.OP_ACCEPT。
四、循環執行 Selector.select() 方法,輪詢就緒的 Channel。
五、Selector輪詢就緒的 Channel 時,若是是處於 OP_ACCEPT 狀態,說明是新的客戶端接入,調用 ServerSocketChannel.accept 接收新的客戶端。
六、設置新接入的 SocketChannel 爲非阻塞模式,並註冊到 Selector 上,監聽 OP_READ。
七、若是Selector輪詢的 Channel 狀態是 OP_READ,說明有新的就緒數據包須要讀取,則構造 ByteBuffer 對象,讀取數據。socket

 

先啓動服務端,不停監聽客戶端鏈接spa

 

服務端代碼.net

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 NIOServer {

    
    /*標識數字*/ 
    private  int flag = 0;  
    /*緩衝區大小*/ 
    private  int BLOCK = 4096;  
    /*接受數據緩衝區*/ 
    private  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
    /*發送數據緩衝區*/ 
    private  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
    //選擇器
    private Selector selector;
    
    public NIOServer(int port) throws IOException {
        //1. 獲取通道, 打開服務器套接字通道  
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
        //2. 切換到非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //3. 綁定鏈接的端口  
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        //4. 獲取選擇器  
        selector = Selector.open();
        //5. 通道註冊到選擇器上,指定監聽事件:接收。……等待鏈接     
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        System.out.println("Server Start----8888:"); 
    }
    
    //監聽
    public void listen() throws IOException {
        while(true) {
            //6. 循環獲取選擇器上已經「準備就緒」的事件,返回的int值表示有多少個通道在上一次select後發生了註冊事件
            int nKeys = selector.select();
            if(nKeys>0){
                //7. 獲取當前選擇器中全部註冊的選擇鍵(已就緒的監聽事件)
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while(iterator.hasNext()){
                    //8. 獲取「準備就緒」的事件
                    SelectionKey selectionKey = iterator.next();
                    //9. 判斷具體是什麼事件準備就緒,開始處理請求
                    handleKey(selectionKey);
                    iterator.remove();
                }
            } else {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    // 處理請求  
    private void handleKey(SelectionKey selectionKey) throws IOException {  
        // 接受請求  
        ServerSocketChannel server = null;  
        SocketChannel client = null;  
        String receiveText;  
        String sendText;  
        int count=0;  
        // 此選擇鍵的通道是否已準備好接受新的套接字鏈接。  
        if (selectionKey.isAcceptable()) {  
            // 返回爲之建立此選擇鍵的通道。  
            server = (ServerSocketChannel) selectionKey.channel();  
            //10. 若「接收就緒」,獲取客戶端鏈接
            client = server.accept();  
            //11. 切換到非阻塞模式
            client.configureBlocking(false);  
            //12. 通道註冊到選擇器上,指定監聽事件:讀 就緒
            client.register(selector, SelectionKey.OP_READ);  
        } else if (selectionKey.isReadable()) {  
            // 返回爲之建立此鍵的通道。  
            client = (SocketChannel) selectionKey.channel();  
            //將緩衝區清空以備下次讀取  
            receivebuffer.clear();  
            //讀取服務器發送來的數據到緩衝區中  
            count = client.read(receivebuffer);   
            if (count > 0) {  
                receiveText = new String(receivebuffer.array(),0,count);  
                System.out.println("服務器端接受客戶端數據--:"+receiveText);  
                client.register(selector, SelectionKey.OP_WRITE);  
            }  
        } else if (selectionKey.isWritable()) {  
            //將緩衝區清空以備下次寫入  
            sendbuffer.clear();  
            // 返回爲之建立此鍵的通道。  
            client = (SocketChannel) selectionKey.channel();  
            sendText="message from server--" + flag++;  
            //向緩衝區中輸入數據  
            sendbuffer.put(sendText.getBytes());  
             //將緩衝區各標誌復位,由於向裏面put了數據標誌被改變要想從中讀取數據發向服務器,就要復位  
            sendbuffer.flip();  
            //輸出到通道  
            client.write(sendbuffer);  
            System.out.println("服務器端向客戶端發送數據--:"+sendText);  
            client.register(selector, SelectionKey.OP_READ);  
        }  
    }  
    
    
    
    public static void main(String[] args) throws IOException {
         int port = 8888;  
         NIOServer server = new NIOServer(port);  
         server.listen();  
    }
    
}

 

心得:線程

一、第5步中,先把服務端通道註冊到選擇器上,該選擇器等待accept接收客戶端消息code

二、轉到第9步,處理請求,若是isAcceptable()=true,準備好接收客戶端了,獲取客戶端鏈接視頻

三、第12步,客戶端通道註冊到選擇器上,並指定監聽。而後,繼續走到listen()方法的循環體中,server

四、 此次循環中,上面已獲取客戶端鏈接,nKeys >0了,selectionKey 是監聽

五、又到第9步isReadable()=true,說明是讀事件,開始操做數據,通道讀取緩衝區

 

 

 

客戶端代碼

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.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOClient {

     /*標識數字*/ 
    private static int flag = 0;  
    /*緩衝區大小*/ 
    private static int BLOCK = 4096;  
    /*接受數據緩衝區*/ 
    private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
    /*發送數據緩衝區*/ 
    private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
    /*服務器端地址*/ 
    private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress("127.0.0.1", 8888);  
    
    public static void main(String[] args) throws IOException {  
        // TODO Auto-generated method stub  
        // 打開socket通道  
        SocketChannel socketChannel = SocketChannel.open();  
        // 設置爲非阻塞方式  
        socketChannel.configureBlocking(false);  
        // 打開選擇器  
        Selector selector = Selector.open();  
        // 註冊鏈接服務端socket動做  
        socketChannel.register(selector, SelectionKey.OP_CONNECT);  
        // 鏈接  
        socketChannel.connect(SERVER_ADDRESS);  
        // 分配緩衝區大小內存  
          
        Set<SelectionKey> selectionKeys;  
        Iterator<SelectionKey> iterator;  
        SelectionKey selectionKey;  
        SocketChannel client;  
        String receiveText;  
        String sendText;  
        int count=0;  
 
        while (true) {  
            //選擇一組鍵,其相應的通道已爲 I/O 操做準備就緒。  
            //此方法執行處於阻塞模式的選擇操做。  
            selector.select();  
            //返回此選擇器的已選擇鍵集。  
            selectionKeys = selector.selectedKeys();  
            //System.out.println(selectionKeys.size());  
            iterator = selectionKeys.iterator();  
            while (iterator.hasNext()) {  
                selectionKey = iterator.next();  
                if (selectionKey.isConnectable()) {  
                    System.out.println("client connect");  
                    client = (SocketChannel) selectionKey.channel();  
                    // 判斷此通道上是否正在進行鏈接操做。  
                    // 完成套接字通道的鏈接過程。  
                    if (client.isConnectionPending()) {  
                        client.finishConnect();  
                        System.out.println("完成鏈接!");  
                        sendbuffer.clear();  
                        sendbuffer.put("Hello,Server".getBytes());  
                        sendbuffer.flip();  
                        client.write(sendbuffer);  
                    }  
                    client.register(selector, SelectionKey.OP_READ);  
                } else if (selectionKey.isReadable()) {  
                    client = (SocketChannel) selectionKey.channel();  
                    //將緩衝區清空以備下次讀取  
                    receivebuffer.clear();  
                    //讀取服務器發送來的數據到緩衝區中  
                    count=client.read(receivebuffer);  
                    if(count>0){  
                        receiveText = new String( receivebuffer.array(),0,count);  
                        System.out.println("客戶端接受服務器端數據--:"+receiveText);  
                        client.register(selector, SelectionKey.OP_WRITE);  
                    }  
 
                } else if (selectionKey.isWritable()) {  
                    sendbuffer.clear();  
                    client = (SocketChannel) selectionKey.channel();  
                    sendText = "message from client--" + (flag++);  
                    sendbuffer.put(sendText.getBytes());  
                     //將緩衝區各標誌復位,由於向裏面put了數據標誌被改變要想從中讀取數據發向服務器,就要復位  
                    sendbuffer.flip();  
                    client.write(sendbuffer);  
                    System.out.println("客戶端向服務器端發送數據--:"+sendText);  
                    client.register(selector, SelectionKey.OP_READ);  
                }  
            }  
            selectionKeys.clear();  
        }  
    }  
    
    
}

 

 

 

 

 

 

運行效果:

 

相關文章
相關標籤/搜索