Socket 是網絡編程的基石。本文提供兩個層次的完整示例:入門級單線程 Echo 模型與進階多線程生產級模型,涵蓋客戶端/服務端雙向代碼、關鍵 API 解析、常見坑點與擴展方向。
public class EchoServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(60000)) {
System.out.println("Server started on 60000");
while (true) {
Socket client = server.accept(); // 阻塞等待連接
System.out.println("Client: " + client.getInetAddress());
try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
System.out.println("Received: " + line);
out.println("--->" + line); // 回显
}
}
}
}
}
}
public class EchoClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 60000);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = console.readLine()) != null) {
out.println(line);
System.out.println("Server echo: " + in.readLine());
if ("bye#".equals(line)) break;
}
}
}
}
PrintWriter(..., true) 啟用自動刷新,每次 println 即發送。readLine/println)。public class ThreadedServer {
static final int PORT = 12345;
public static void main(String[] args) {
try (ServerSocket server = new ServerSocket(PORT)) {
System.out.println("Server started on " + PORT);
while (true) {
Socket client = server.accept();
new Thread(new Handler(client)).start(); // 每連接啟動一線程
}
} catch (IOException e) { e.printStackTrace(); }
}
static class Handler implements Runnable {
private final Socket socket;
Handler(Socket socket) { this.socket = socket; }
public void run() {
try (DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {
String msg = in.readUTF(); // 必須配對 writeUTF
System.out.println("Client: " + msg);
// 模擬業務處理
String reply = "OK: " + msg;
out.writeUTF(reply);
} catch (IOException e) {
System.err.println("Handler error: " + e.getMessage());
} finally {
try { socket.close(); } catch (IOException ignored) {}
}
}
}
}
public class ThreadedClient {
static final String HOST = "localhost";
static final int PORT = 12345;
public static void main(String[] args) throws IOException, InterruptedException {
while (true) {
try (Socket socket = new Socket(HOST, PORT);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream());
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
System.out.print("Send: ");
String send = console.readLine();
out.writeUTF(send);
String reply = in.readUTF();
System.out.println("Reply: " + reply);
if ("OK".equals(reply)) {
System.out.println("Closing...");
Thread.sleep(500);
break;
}
}
}
}
}
| 場景 | 推薦流 | 注意事項 |
|---|---|---|
| 文本行協議 | BufferedReader/PrintWriter + readLine/println | 自動處理換行,跨平台兼容 |
| 二進制/定長協議 | DataInputStream/DataOutputStream + readUTF/writeUTF | 雙端必須配對,否則 EOFException |
| 高性能/大數據 | NIO (Selector/Channel) 或 Netty | 非阻塞、零拷貝、可擴展至萬級連接 |
new DataInputStream(new BufferedInputStream(socket.getInputStream())) — 先緩衍再數據流。try-with-resources。readLine 另一方用 writeUTF → 直接拋 EOFException。單線程阻塞 IO (Demo)
↓
多線程阻塞 IO (本文 ThreadedServer) — 適合百級併發
↓
線程池 + 阻塞 IO — 控制線程數,避免 OOM
↓
NIO Selector (非阻塞) — 千級併發,單線程輪詢
↓
Netty / Mina (成熟 NIO 框架) — 生產首選,解決黏包/半包、內存管理、編解碼
掌握 Socket 基礎,是理解 HTTP、RPC、微服務通信的前提。建議先跑通本文兩個 Demo,再嘗試加入心跳檢測、長度頭解碼器、優雅關閉等工程化細節。