accept() 阻塞等待客戶端連接getInputStream()/getOutputStream() 讀寫數據
public class EchoServer {
private static final int PORT = 8888;
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(PORT)) {
server.setReuseAddress(true);
System.out.println("服務端啟動,監聽端口: " + PORT);
ExecutorService pool = Executors.newCachedThreadPool(); // 簡單線程池
while (true) {
Socket client = server.accept(); // 阻塞
InetAddress addr = client.getInetAddress();
System.out.println("新連接: " + addr.getHostAddress() + ":" + client.getPort());
pool.execute(() -> handleClient(client, addr));
}
}
}
private static void handleClient(Socket socket, InetAddress addr) {
try (socket;
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true)) {
String line;
while ((line = in.readLine()) != null) {
System.out.println("收自 " + addr + ": " + line);
if ("bye".equalsIgnoreCase(line.trim())) {
out.println("Goodbye!");
break;
}
// 業務邏輯: 回聲 + 時間戳
out.println("Echo: " + line + " [" + Instant.now() + "]");
}
} catch (IOException e) {
System.err.println("客戶端 " + addr + " 異常斷開: " + e.getMessage());
} finally {
System.out.println("連接關閉: " + addr);
}
}
}
public class EchoClient {
private static final String HOST = "localhost";
private static final int PORT = 8888;
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket(HOST, PORT);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
System.out.println("已連接到服務端,輸入 'bye' 退出");
String input;
while ((input = console.readLine()) != null) {
out.println(input); // 發送
String response = in.readLine(); // 同步等待響應
if (response == null) {
System.out.println("服務端已關閉連接");
break;
}
System.out.println("服務端回覆: " + response);
if ("Goodbye!".equals(response)) break;
}
}
}
}
BufferedReader/PrintWriter + 指定 StandardCharsets.UTF_8,自動處理換行符InputStream/OutputStream 或 DataInputStream/DataOutputStream現象:發送 "ABC" "DEF",接收可能讀到 "AB" "CDEF" 或 "ABCDEF"。
解決方案:
\n、\r\n 或特殊字符 (示例採用 readLine() 隱含換行符)[4字節長度][消息體],最通用高效| 模型 | 適用場景 | Java 實現 |
|---|---|---|
| 每連接一線程 | 連接數 < 500、低併發 | new Thread() / CachedThreadPool |
| 固定線程池 | 連接數穩定、CPU 密集業務 | FixedThreadPool |
| NIO 非阻塞 (Reactor) | 高併發、長連接、IO 密集 | Selector / Netty / Mina |
| 虛擬線程 (Java 21+) | 高併發、代碼同步寫法 | Executors.newVirtualThreadPerTaskExecutor() |
try-with-resources 確保 Socket/Stream 關閉serverSocket.close() 觸發 accept() 拋出 SocketException,退出循環SO_LINGER / SO_TIMEOUT 避免僵死連接Magic + Length + Type + Payload + Checksum 二進制幀ByteToMessageDecoder / MessageToByteEncoder (Netty)SSLServerSocketFactory / SSLSocketFactory 加密傳輸Java Socket API 設計經典,掌握 阻塞 IO 基礎模型、字符/二進制協議選擇、半包粘包處理、線程模型權衡、資源安全釋放 五大核心,即可構建健壯的網絡應用。現代項目建議直接使用 Netty 或 Java 21 虛擬線程,專注業務邏輯而非底層 IO 複雜度。