一、TCP 多線程通信
1.1 核心類
- 服務端:
ServerSocket(監聽)、Socket(連接)
- 客戶端:
Socket
1.2 服務端線程模型:One Thread Per Connection
while (true) {
Socket client = serverSocket.accept(); // 阻塞等待
new Thread(new ClientHandler(client)).start(); // 每連接一線程
}
1.3 完整代碼
ClientHandler.java(線程處理類)
public class ClientHandler implements Runnable {
private final Socket socket;
public ClientHandler(Socket socket) { this.socket = socket; }
@Override
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true)) {
String msg;
while ((msg = in.readLine()) != null) {
System.out.println("收到: " + msg);
// 業務處理...
out.println("Server Echo: " + msg);
}
} catch (IOException e) {
log.error("Client error", e);
} finally {
closeQuietly(socket);
}
}
}
TCPServer.java
public class TCPServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8888)) {
System.out.println("TCP Server started on 8888");
while (true) {
Socket client = server.accept();
System.out.println("新連接: " + client.getInetAddress());
new Thread(new ClientHandler(client)).start();
}
}
}
}
TCPClient.java
public class TCPClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 8888);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
out.println("Hello Server");
System.out.println("服務端回應: " + in.readLine());
}
}
}
二、UDP 多線程通信
2.1 核心類
DatagramSocket:發送/接收數據報
DatagramPacket:數據報封裝(數據、長度、目標 IP/端口)
2.2 服務端:單 Socket + 多線程處理
public class UDPServer {
public static void main(String[] args) throws IOException {
try (DatagramSocket socket = new DatagramSocket(8800)) {
byte[] buf = new byte[1024];
System.out.println("UDP Server started on 8800");
while (true) {
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet); // 阻塞接收
new Thread(new UDPHandler(socket, packet)).start();
}
}
}
}
class UDPHandler implements Runnable {
private final DatagramSocket socket;
private final DatagramPacket packet;
public UDPHandler(DatagramSocket socket, DatagramPacket packet) {
this.socket = socket; this.packet = packet;
}
@Override
public void run() {
String msg = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8);
System.out.println("收到: " + msg);
byte[] resp = "Server Received".getBytes(StandardCharsets.UTF_8);
DatagramPacket respPacket = new DatagramPacket(resp, resp.length, packet.getAddress(), packet.getPort());
try { socket.send(respPacket); } catch (IOException e) { log.error("Send failed", e); }
}
}
2.3 UDPClient.java
public class UDPClient {
public static void main(String[] args) throws IOException {
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress addr = InetAddress.getByName("localhost");
byte[] data = "Hello UDP".getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(data, data.length, addr, 8800);
socket.send(packet);
byte[] buf = new byte[1024];
DatagramPacket resp = new DatagramPacket(buf, buf.length);
socket.receive(resp);
System.out.println("服務端回應: " + new String(resp.getData(), 0, resp.getLength(), StandardCharsets.UTF_8));
}
}
}
三、關鍵差異對比
| 特性 | TCP | UDP |
| 連接 | 面向連接(三次握手) | 無連接 |
| 可靠性 | 可靠、有序、無重複 | 不可靠、無序、可能重複/丟失 |
| 流控/擁塞控制 | 有 | 無 |
| 數據單元 | 字節流 | 數據報 |
| 適用場景 | 文件傳輸、HTTP、郵件、即時通訊核心 | 視頻流、直播、DNS、SNMP、廣播 |
| 服務端 Socket | ServerSocket + 多 Socket | 單 DatagramSocket 共用 |
四、常見坑點與優化
| 問題 | 原因 | 解決 |
| TCP 粘包/拆包 | 流式傳輸無邊界 | 定長、分隔符、長度前綴(Netty LengthFieldBasedFrameDecoder) |
| 線程資源耗盡 | 大量連接創建線程 | 線程池、NIO 選擇器、Netty EventLoop |
| UDP 丟包無感知 | 協議特性 | 應用層 ACK/重傳/序列號(如 KCP、QUIC) |
| 編碼亂碼 | 未指定 Charset | 統一 StandardCharsets.UTF_8 |
| 端口佔用 | TIME_WAIT / 未釋放 | SO_REUSEADDR、優雅關閉 |
| BufferedReader.readLine() 死循環 | 對端未發送換行符 | 自定義協議解碼器、read(char[]) |
五、進階優化方向
- 線程池化:
ExecutorService pool = Executors.newFixedThreadPool(50); pool.execute(new ClientHandler(socket));
- NIO 非阻塞:
Selector + ServerSocketChannel/SocketChannel,單線程處理萬級連接。
- Netty 框架:生產級首選,封裝 NIO、內存管理、編解碼、心跳、SSL、HTTP/2。
- 對象傳輸:
ObjectOutputStream 序列化對象,或 Protobuf/JSON 高性能編解碼。
- 優雅關閉:註冊 ShutdownHook,關閉 ServerSocket、拒絕新連接、等待處理中任務完成。
六、總結
本文提供了可直接運行的 TCP/UDP 多線程通信完整代碼,涵蓋服務端線程模型、客戶端調用、核心 API 用法、協議差異、高頻坑點與生產級優化路線圖。建議先跑通 Demo,再逐步引入線程池、NIO、Netty 進階演進。