網絡編程是後端開發的核心技能。Java 在 JDK 中原生提供了對 TCP/UDP 協議的完整支持,其中 TCP(傳輸控制協議)以其可靠、面向連接、字節流特性,成為構建分布式系統、即時通訊、微服務 RPC 的基石。本文從協議基礎、連接建立流程、ServerSocket/Socket API 實戰,帶你快速掌握 Java TCP 編程精髓。
{IP地址, 端口}。一個連接由 {本地IP, 本地端口, 遠程IP, 遠程端口} 唯一確定。TCP 編程中,端點抽象為 Socket(套接字)。按角色分為:
ServerSocket,處於 LISTEN 狀態,調用 accept() 阻塞等待客戶端連接。Socket 發起連接;服務端 accept() 返回的新 Socket 代表具體連接,雙方通過這兩個主動套接字進行全雙工通信。ServerSocket 綁定端口,進入循環 accept()。Socket(host, port),發起三次握手。accept() 返回,創建新 Socket 處理該連接(通常派發線程池)。InputStream/OutputStream 進行數據交換。
示例功能:服務端接收客戶端文本行,轉大寫返回,收到 bye 斷開連接。
public class TcpServer implements Runnable {
private final ServerSocket serverSocket;
public TcpServer(int port) throws IOException {
this.serverSocket = new ServerSocket(port); // 創建被動套接字,綁定端口
}
@Override
public void run() {
System.out.println("Server started on port " + serverSocket.getLocalPort());
while (true) {
try {
// 1. 阻塞等待客戶端連接
Socket clientSocket = serverSocket.accept();
// 2. 派發新線程處理(生產環境建議使用線程池)
new Thread(new ClientHandler(clientSocket)).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
static class ClientHandler implements Runnable {
private final Socket socket;
ClientHandler(Socket socket) {
this.socket = Objects.requireNonNull(socket);
}
@Override
public void run() {
// try-with-resources 確保 Socket 關閉
try (Socket s = socket;
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8), true)) {
String line;
while ((line = in.readLine()) != null) {
if ("bye".equalsIgnoreCase(line)) break;
out.println(line.toUpperCase(Locale.ENGLISH)); // 處理並返回
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ServerSocket.accept():阻塞直到新連接到達,返回代表該連接的 Socket。BufferedReader.readLine():按行讀取,依賴換行符 \n 作為邊界(簡易應用層協議)。PrintWriter(..., true):開啟自動刷新,println 後立即發送。public class TcpClient implements Runnable {
private final Socket socket;
public TcpClient(String host, int port) throws IOException {
this.socket = new Socket(host, port); // 主動發起連接
}
@Override
public void run() {
try (Socket s = socket;
PrintWriter out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8), true);
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8));
Console console = System.console()) {
String line;
while ((line = console.readLine()) != null) {
if ("bye".equalsIgnoreCase(line)) break;
out.println(line); // 發送
System.out.println(in.readLine()); // 接收回顯
}
out.println("bye"); // 通知服務端關閉
} catch (IOException e) {
e.printStackTrace();
}
}
}
阻塞模式雖簡單,但存在 C10K 問題(線程模型限制併發)。JDK 提供了 ServerSocketChannel/SocketChannel 支持非阻塞 I/O (NIO),結合 Selector 可實現單線程處理萬級連接。下一篇將深入講解 ServerSocket 與 Socket 的初始化參數、超時設置、緩衝區調優及 NIO 非阻塞編程模型。
ServerSocket 負責偵聽,Socket 負責通信。StandardCharsets.UTF_8 避免編碼亂碼,使用 try-with-resources 保證資源釋放。