所有網路通訊本質上解決兩個問題:
Socket 是「IP:Port」的程式碼抽象,提供標準 API 供應用層讀寫網路資料流。Java 在 java.net 套件提供兩大類:
Socket / ServerSocket — TCP 面向連線。DatagramSocket / DatagramPacket — UDP 無連線。public class TcpEchoServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8888)) {
System.out.println("Server started on port 8888");
while (true) {
Socket client = server.accept(); // 阻塞等待連線
new Thread(() -> handleClient(client)).start(); // 為每連線開線程
}
}
}
private static void handleClient(Socket socket) {
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("Received: " + line);
out.println("Echo: " + line); // 回射
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TcpEchoClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 8888);
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))) {
String input;
while ((input = console.readLine()) != null) {
out.println(input);
System.out.println("Server: " + in.readLine());
}
}
}
}
// 伺服器
public class UdpEchoServer {
public static void main(String[] args) throws IOException {
try (DatagramSocket socket = new DatagramSocket(9999)) {
byte[] buf = new byte[1024];
while (true) {
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet); // 阻塞接收
String msg = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8);
System.out.println("From " + packet.getAddress() + ":" + packet.getPort() + " - " + msg);
// 原路回覆
socket.send(new DatagramPacket(packet.getData(), packet.getLength(), packet.getAddress(), packet.getPort()));
}
}
}
}
// 客戶端
public class UdpEchoClient {
public static void main(String[] args) throws IOException {
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress addr = InetAddress.getByName("localhost");
byte[] buf = "Hello UDP".getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(buf, buf.length, addr, 9999);
socket.send(packet);
// 接收回覆
byte[] recv = new byte[1024];
DatagramPacket resp = new DatagramPacket(recv, recv.length);
socket.receive(resp);
System.out.println("Server reply: " + new String(resp.getData(), 0, resp.getLength(), StandardCharsets.UTF_8));
}
}
}
| 維度 | TCP (Socket) | UDP (DatagramSocket) |
|---|---|---|
| 連線建立 | 三次握手、有狀態 | 無連線、無狀態 |
| 可靠性 | 有序、無重複、無丟失(重傳、ACK) | 不保證,應用層自行處理 |
| 流量控制/擁塞控制 | 滑動窗口、慢啟動、快速恢復 | 無,需應用層實現 |
| 延遲 | 較高(握手、確認) | 極低 |
| 適用場景 | HTTP、RPC、資料庫、檔案傳輸、IM 長連線 | 直播推流、VoIP、遊戲狀態同步、DNS、SNMP |
Selector + Channel + Buffer,單線程輪詢多連線,Netty/Mina 基石。AsynchronousSocketChannel + CompletionHandler,Proactor 模式,Windows IOCP 加持。Thread.startVirtualThread(() -> { blockingSocketCode(); }),以同步阻塞寫法獲得 NIO 級併發,徹底簡化網路程式設計。public class VirtualThreadTcpServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8888)) {
System.out.println("Virtual Thread Server started");
while (true) {
Socket client = server.accept();
Thread.startVirtualThread(() -> handleClient(client)); // 輕量級虛擬執行緒
}
}
}
// handleClient 同 BIO 版本,無需修改
}
StandardCharsets.UTF_8,避免平台預設編碼不一致導致亂碼。try-with-resources 確保 Socket、流正確關閉,防止句柄洩漏。socket.shutdownOutput() 發送 FIN,對端 read() 返回 -1,雙向關閉更優雅。socket.setSoTimeout(ms) 避免讀取無限阻塞;socket.setSoKeepAlive(true) 偵測死連線。socket.setReceiveBufferSize()/setSendBufferSize() 配合 OS net.core.rmem_max 調大,提升大流量吞吐。SSLSocket/SSLServerSocket (TLS 1.2+),或接入 Netty + OpenSSL。掌握 Socket 基礎、協議特性與架構演進,並善用 JDK 21 虛擬執行緒,即能輕鬆構建高效穩定的網路應用。