服務端:
package org.example; 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.nio.channels.spi.SelectorProvider; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NIOServer { static ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) { try { // 開啟一個選擇器 Selector selector = SelectorProvider.provider().openSelector(); // 開啟一個伺服器套接字通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); // 繫結到指定埠 serverChannel.socket().bind(new InetSocketAddress(8081)); // 將伺服器通道註冊到選擇器上,並指定關注的事件為接收連線 serverChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("伺服器已啟動,監聽埠8080..."); while (true) { // 選擇器等待就緒的通道 selector.select(); // 獲取就緒的選鍵集合 Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); keyIterator.remove(); // 檢查選鍵的狀態並進行相應的處理 if (key.isAcceptable()) { handleAccept(key); } else if (key.isReadable()) { processMsg(key); } else if (key.isWritable()) { // 通常這裡不會處理寫就緒,因為寫操作通常由應用邏輯觸發 } else if (key.isConnectable()) { // 這是客戶端連線的情況,對於伺服器來說通常不會遇到 } } } } catch (IOException e) { e.printStackTrace(); System.err.println("伺服器發生異常,已關閉。"); } finally { executorService.shutdown(); } } private static void handleAccept(SelectionKey key) throws IOException { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel clientChannel = serverChannel.accept(); clientChannel.configureBlocking(false); // 註冊到選擇器並關注讀事件 clientChannel.register(key.selector(), SelectionKey.OP_READ); System.out.println("接受到新的客戶端連線:" + clientChannel.getRemoteAddress()); } private static void sendData(SocketChannel socketChannel, String message) { ByteBuffer buffer = ByteBuffer.wrap(message.getBytes()); while (buffer.hasRemaining()) { try { socketChannel.write(buffer); } catch (IOException e) { throw new RuntimeException(e); } } } public static void processMsg(SelectionKey key) { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(2); StringBuilder sb = new StringBuilder(); try { while (true) { int bytesRead = clientChannel.read(buffer); if (bytesRead == -1) { // 客戶端關閉連線 clientChannel.close(); break; } else if (bytesRead == 0) { // 訊息讀取結束 break; } buffer.flip(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); sb.append(new String(bytes, StandardCharsets.UTF_8)); buffer.clear(); } if (!clientChannel.isOpen()) return; executorService.submit(() -> { System.out.println("收到訊息:" + sb); sendData(clientChannel, "abcdaaaaaaaaaaaaaaabbbbbbbbccccccccccccccccccccccccccccd"); }); } catch (Exception e) { // 發生讀錯誤,關閉連線並列印錯誤資訊 System.err.println("讀錯誤:" + e.getMessage()); try { clientChannel.close(); } catch (IOException ex) { ex.printStackTrace(); } } } }
客戶端:
package org.example; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOClient { public static void main(String[] args) { // 伺服器的地址和埠 String serverAddress = "localhost"; int serverPort = 8081; try { // 開啟一個套接字通道 SocketChannel socketChannel = SocketChannel.open(); // 配置為非阻塞模式 socketChannel.configureBlocking(false); // 嘗試連線到伺服器 if (socketChannel.connect(new InetSocketAddress(serverAddress, serverPort))) { // 連線成功,可以直接傳送資料(但在非阻塞模式下,這通常不會發生) sendData(socketChannel, "Hello, Server!"); } else { // 連線可能需要時間,等待連線完成(在實際應用中,這通常是在一個迴圈中完成的) while (!socketChannel.finishConnect()) { // 可以在這裡執行其他任務,比如更新使用者介面等 // 但在這個簡單的例子中,我們只是空轉等待連線完成 } // 連線完成,傳送資料 for (int index = 1; index <= 5; index++) { sendData(socketChannel, "Hello, Server!"); } } // 讀取伺服器的響應(在非阻塞模式下,這可能需要多次嘗試) StringBuilder sb = new StringBuilder(); ByteBuffer buffer = ByteBuffer.allocate(1024); while (socketChannel.read(buffer) <= 0) { // 如果沒有資料可讀,可以在這裡執行其他任務 // 但在這個簡單的例子中,我們只是空轉等待資料到來 } // 伺服器傳送過來的所有資料 buffer.flip(); String response = new String(buffer.array(), 0, buffer.limit()); sb.append(response); while (socketChannel.read(buffer) > 0) { buffer.flip(); response = new String(buffer.array(), 0, buffer.limit()); sb.append(response); } System.out.println("Received from server: " + sb); // 關閉連線 socketChannel.close(); } catch (Exception e) { e.printStackTrace(); System.err.println("Client failed to connect to server or read response."); } System.out.println("finish"); } private static void sendData(SocketChannel socketChannel, String message) throws Exception { ByteBuffer buffer = ByteBuffer.wrap(message.getBytes()); while (buffer.hasRemaining()) { socketChannel.write(buffer); } } }
執行效果: