TCP(Transmission Control Protocol)提供面向連接、可靠、有序的字節流服務,是絕大多數業務協議(HTTP、MySQL、Redis、Dubbo...)的傳輸基石。本文給出最小化的 TCP Echo 服務端與客戶端實現,解析 ServerSocket/Socket 核心流程,並指出單線程模型局限。
| 類 | 角色 | 關鍵方法 |
|---|---|---|
ServerSocket | 服務端監聽套接字 | accept() 阻塞等待客戶端連接,返回 Socket |
Socket | 雙端通信端點 | getInputStream()/getOutputStream()、close() |
InetAddress | IP 地址封裝 | getByName(host)、getHostAddress() |
public class TCPEchoServer {
private static final int BUFSIZE = 32;
public static void main(String[] args) throws IOException {
ServerSocket servSock = new ServerSocket(5500);
byte[] recvBuf = new byte[BUFSIZE];
while (true) {
Socket clntSock = servSock.accept(); // 阻塞等待連接
System.out.println("Client: " + clntSock.getRemoteSocketAddress());
try (InputStream in = clntSock.getInputStream();
OutputStream out = clntSock.getOutputStream()) {
int recvMsgSize;
while ((recvMsgSize = in.read(recvBuf)) != -1) { // 讀到流末尾 -1
out.write(recvBuf, 0, recvMsgSize); // 原樣回寫
}
} // try-with-resources 自動關閉流與 Socket
}
}
}
new ServerSocket(port):綁定端口,進入 LISTEN 狀態。accept():阻塞直到三次握手完成,返回已連接 Socket。InputStream 讀取客戶端數據,OutputStream 發送響應。read() 返回 -1 表示客戶端關閉輸出流(發送 FIN),服務端隨後關閉連接。public class TCPEchoClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 5500);
byte[] data = "Hi, World".getBytes();
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream()) {
out.write(data); // 發送
out.flush(); // 確保發送
int total = 0, recv;
while (total < data.length) { // 確保讀滿預期長度
recv = in.read(data, total, data.length - total);
if (recv == -1) throw new SocketException("Server closed prematurely");
total += recv;
}
System.out.println("Received: " + new String(data));
}
}
}
read() 可能分多次返回,必須循環讀滿預期長度或用分隔符/長度前綴協議幀解碼。setTcpNoDelay(true))。read() 返回 -1 非異常,屬正常關閉流程;連接重置拋 SocketException。| 局限 | 後果 | 演進方向 |
|---|---|---|
| 串行 accept/處理 | 併發能力 = 1,延遲隨排隊線性增長 | 線程池:ExecutorService 提交任務;或 CompletableFuture |
| 阻塞 IO | 線程數 = 連接數,C10K 問題 | NIO 選擇器:Selector + 非阻塞 SocketChannel;或 Netty 事件循環 |
| 無協議幀 | 粘包導致業務解析錯誤 | 定義協議:長度前綴 / 分隔符 / 固定長度;Netty LengthFieldBasedFrameDecoder |
| 無優雅關閉 | 強制關閉導致數據丟失 | 關閉鉤子 + 空閒檢測 + 協議層心跳 |
Boss Group (1) Worker Group (N)
│ │
▼ ▼
ServerSocketChannel Selector Loop
│ │
├── accept() ──────────► │
│ ├── register(OP_READ)
│ ├── read() → decode → business → encode → write()
│ └── close() on error/idle
這正是 Netty「主從 Reactor 多線程模型」的核心思想。
TCP 編程入口雖簡單(ServerSocket/Socket + 流),但可靠傳輸 ≠ 簡單業務:粘包拆包、背壓流控、連接生命周期、優雅關閉、TLS 加密、協議設計,每一項都是深水區。掌握阻塞 IO 基礎模型,是理解 NIO、Netty、gRPC 等高性能框架的必經之路。