客戶端/伺服器端通訊程式碼:
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 NetworkNIOServer { // 通道管理器 private Selector selector; public void initServer(String host, int port) throws IOException { // 獲得一個ServerSocket通道,相當於傳統socket中的ServerSocket ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 設定通道為非阻塞 serverChannel.configureBlocking(false); // 將該通道對應的ServerSocket繫結到port埠,ip預設就是本機的ip serverChannel.socket().bind(new InetSocketAddress(host, port)); // 獲得一個通道管理器 this.selector = Selector.open(); // 將通道管理器和該通道繫結,併為該通道註冊SelectionKey.OP_ACCEPT事件, // 註冊該事件後,當該事件到達時selector.select()會返回, // 如果該事件沒到達selector.select()會一直阻塞 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException { // 輪詢訪問selector,select方法會阻塞 @SuppressWarnings("unused") int count = 0; while ((count = selector.select()) > 0) { // 獲得selector中選中的項的迭代器,選中的項為註冊的事件 Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { // 獲取選中的KEY SelectionKey key = (SelectionKey) ite.next(); // 刪除已選的key,以防重複處理 ite.remove(); handler(key); } } } private void handler(SelectionKey selectionKey) throws IOException { if (selectionKey.isAcceptable()) { // 客戶端請求連線事件 handlerAccept(selectionKey); } else if (selectionKey.isReadable()) { // 獲得可讀的事件 handelerRead(selectionKey); } } private void handelerRead(SelectionKey selectionKey) throws IOException { // 伺服器可讀取訊息:得到事件發生的Socket通道(其實就是這個請求對應的通道) SocketChannel channel = (SocketChannel) selectionKey.channel(); // 建立讀取的緩衝區 ByteBuffer buffer = ByteBuffer.allocate(1024); int read = channel.read(buffer); if (read <= 0) { System.out.println("沒有讀取到資料,客戶端關閉。"); selectionKey.cancel(); } else { byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("服務端收到資訊:" + msg); // 回寫資料 ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes()); channel.write(outBuffer); } } private void handlerAccept(SelectionKey selectionKey) throws IOException { ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel(); // 獲得和客戶端連線的通道 SocketChannel channel = server.accept(); // 設定成非阻塞 channel.configureBlocking(false); System.out.println("新的客戶端連線"); // 在和客戶端連線成功之後,為了可以接收到客戶端的資訊,需要給通道設定讀的許可權。 channel.register(this.selector, SelectionKey.OP_READ); } public static void main(String[] args) throws IOException { NetworkNIOServer server = new NetworkNIOServer(); server.initServer("0.0.0.0",8000); server.listen(); }}
import java.io.IOException;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;public class NetworkNIOClient { public static void main(String[] args) { // 獲得一個SocketChannel通道 try (SocketChannel socketChannel = SocketChannel.open()) { // 連線服務端socket SocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8000); socketChannel.connect(socketAddress); // ByteBuffer buffer = ByteBuffer.allocate(1024); int sendCount = 0; // 這裡最好使用selector處理 這裡只是為了寫的簡單 while (sendCount < 1) { // 把position移動到最前面,同時把limit調整為capacity buffer.clear(); // 向服務端傳送訊息 buffer.put(("current time : " + System.currentTimeMillis()).getBytes()); // 當呼叫flip()方法之後,limit的值會改變成position的值,而position被置0。 buffer.flip(); socketChannel.write(buffer); buffer.clear(); // 從服務端讀取訊息 int readLenth = socketChannel.read(buffer); // 讀取模式 buffer.flip(); byte[] bytes = new byte[readLenth]; buffer.get(bytes); System.out.println(new String(bytes, "UTF-8")); buffer.clear(); sendCount++; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // 客戶端主動關閉 socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } }}
TCP通訊過程三次握手——連線客戶端127.0.0.1:2254====>連線====>伺服器端127.0.0.1:8080
第一次,客戶端傳送, SYN=1, seq=x, ack=0
可以看到:SYN=1,seq=XXX,ack(Acknowledgment number) = 0
第二次,伺服器端傳送, SYN=1, ACK=1, seq=y, ack=x+1
可以看到:SYN=1, ACK=1, seq=y, ack=x+1
第三次,客戶端傳送, ACK=1, seq= x+1, ack=y+1
可以看到:ACK=1, seq= x+1, ack=y+1
通訊——傳送+確認第一次,客戶端傳送資料給伺服器端
第二次,伺服器端確認接收資訊
第三次,伺服器端傳送資料給客戶端
第四次,客戶端確認接收資訊
最新評論