一、使用Java NIO完成網路通信的三個核心 1.通道(Channel):負責連接 java.nio.channels.Channel 介面: |--SelectableChannel |--SocketChannel |--ServerSocketChannel |--DatagramChann ...
一、使用Java NIO完成網路通信的三個核心
1.通道(Channel):負責連接
java.nio.channels.Channel 介面:
|--SelectableChannel
|--SocketChannel
|--ServerSocketChannel
|--DatagramChannel
|--Pipe.SinkChannel
|--Pipe.SourceChannel
2.緩衝區(buffer):負責數據存取
3.選擇器(Selector):是SelectableChannel 的多路復用器,用來檢測SelectableChannel的IO狀態
案例:使用非阻塞式實現簡單的群聊天系統
一、實現客戶端
1 public static void main(String[] args) throws Exception { 2 SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989)); 3 4 //2. 切換非阻塞模式 5 sChannel.configureBlocking(false); 6 7 //3. 分配指定大小的緩衝區 8 ByteBuffer buf = ByteBuffer.allocate(1024); 9 10 //4. 發送數據給服務端 11 Scanner scan = new Scanner(System.in); 12 13 while (scan.hasNext()) { 14 String str = scan.next(); 15 buf.put((new Date().toString() + "\n" + str).getBytes()); 16 buf.flip(); 17 sChannel.write(buf); 18 buf.clear(); 19 } 20 21 //5. 關閉通道 22 sChannel.close(); 23 }
二、實現服務端
@Test public void server() { ServerSocketChannel ssChannel = null; try { ssChannel = ServerSocketChannel.open(); //配置非阻塞 ssChannel.configureBlocking(false); //綁定連接 ssChannel.bind(new InetSocketAddress(8989)); Selector selector = Selector.open(); //將通道註冊到監聽器中,並且制定監聽器的監聽模式為“接受” ssChannel.register(selector, SelectionKey.OP_ACCEPT); //輪詢的選擇已經就緒的事件 while (selector.select() > 0) { //獲取當前監聽 Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) { //獲取準備就緒的事件 SelectionKey sk = it.next(); if (sk.isAcceptable()) { //如果接受就緒,則獲取客戶端的連接 SocketChannel clientChannel = ssChannel.accept(); //同樣配置成非阻塞式 clientChannel.configureBlocking(false); //把客戶端的連接註冊到選擇器上 clientChannel.register(selector, SelectionKey.OP_READ); } else if (sk.isReadable()) { //如果讀取就緒,則獲取讀取的通道 SocketChannel socketChannel = (SocketChannel) sk.channel(); //配置成非阻塞模式 socketChannel.configureBlocking(false); //讀取數據 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); int len = 0; while ((len = socketChannel.read(byteBuffer)) > 0) { byteBuffer.flip(); System.out.println(new String(byteBuffer.array(), 0, len)); byteBuffer.clear(); } } it.remove(); } } } catch (IOException e) { e.printStackTrace(); } finally { if(ssChannel!=null){ try { ssChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
註意:這裡服務端用到的org.junit.Test;這個包方便測試,客戶端因為需要讀取輸入所以寫在Main函數中(@Test方法中測試出來好像不能讀取輸入)
需要下載包的地址如下:
鏈接:https://pan.baidu.com/s/14ZHHOnAD3ldNVcA3pmCoJQ
提取碼:uqd9
DatagramChannel(UDP)的使用方法(和上個案例大同小異)
public static void main(String args[]) { DatagramChannel datagramChannel = null; try { datagramChannel = DatagramChannel.open(); datagramChannel.configureBlocking(false); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { String str = scanner.next(); byteBuffer.put(str.getBytes()); byteBuffer.flip(); datagramChannel.send(byteBuffer, new InetSocketAddress("127.0.0.1", 9897)); byteBuffer.clear(); } } catch (IOException e) { e.printStackTrace(); } finally { if(datagramChannel!=null){ try { datagramChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server(){ DatagramChannel datagramChannel = null; try { datagramChannel=DatagramChannel.open(); datagramChannel.bind(new InetSocketAddress(9897)); datagramChannel.configureBlocking(false); Selector selector = Selector.open(); datagramChannel.register(selector, SelectionKey.OP_READ); while(selector.select()>0){ Iterator<SelectionKey> st=selector.selectedKeys().iterator(); while(st.hasNext()){ SelectionKey sk=st.next(); if(sk.isReadable()){ ByteBuffer btf=ByteBuffer.allocate(1024); datagramChannel.receive(btf); btf.flip(); System.out.println(new String(btf.array(),0,btf.limit())); btf.clear(); } } st.remove(); } } catch (IOException e) { e.printStackTrace(); } finally { if(datagramChannel!=null){ try { datagramChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } }
Pipe簡介
pipe是兩個線程之間單項數據連接,Pipe有兩個數據通道,Sign通道負責寫入,Source通道負責讀取。
案例如下:
@Test public void test() throws Exception { Pipe pipe = Pipe.open(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.put("hello world".getBytes()); Pipe.SinkChannel sinkChannel=pipe.sink(); byteBuffer.flip(); sinkChannel.write(byteBuffer); //讀取 Pipe.SourceChannel sourceChannel =pipe.source(); byteBuffer.flip(); int len=sourceChannel.read(byteBuffer); System.out.println("sourceChanel:"+new String(byteBuffer.array(),0,len)); byteBuffer.clear(); sinkChannel.close(); sourceChannel.close(); }
謝謝瀏覽,如有問題直接評論,我會及時更改我的錯誤。