如同餐廳點餐:客戶端 (食客) 發起請求 → 服務端 (廚房) 處理 → 響應結果。核心在於 **「請求-響應」協議與通信通道 (Socket) 的建立**。
| 特性 | TCP (Socket/ServerSocket) | UDP (DatagramSocket) |
|---|---|---|
| 連接性 | 面向連接、三次握手 | 無連接、數據報 |
| 可靠性 | 有序、無丟失、無重複、流量控制 | 不可靠、無序、可能丟包 |
| 適用場景 | HTTP、RPC、數據庫、文件傳輸、即時通訊核心 | 視頻流、DNS、SNMP、廣播、高併發非關鍵數據 |
| Java 類 | Socket, ServerSocket | DatagramSocket, DatagramPacket |
**本文聚焦 TCP Socket (流式套接字)。**
InetAddress.getByName("example.com")。
// 客戶端
Socket socket = new Socket("host", port);
InputStream in = socket.getInputStream(); // 讀取響應
OutputStream out = socket.getOutputStream(); // 發送請求
// 服務端
ServerSocket server = new ServerSocket(port);
while (true) {
Socket client = server.accept(); // 阻塞等待連接
// 處理 client...
}
public class FileClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 1001);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
out.println("RemoteFileServer.java"); // 請求文件名
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
}
}
}
public class FileServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(1001)) {
System.out.println("Server started on 1001");
while (true) {
try (Socket client = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true);
BufferedReader fileIn = new BufferedReader(new FileReader(in.readLine()))) {
String line;
while ((line = fileIn.readLine()) != null) {
out.println(line);
}
} catch (IOException e) {
System.err.println("Client handling error: " + e);
}
}
}
}
}
public class ThreadedFileServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(1001, 50)) { // backlog=50
while (true) {
Socket client = server.accept();
new Thread(new ClientHandler(client)).start();
}
}
}
static class ClientHandler implements Runnable {
private final Socket socket;
ClientHandler(Socket socket) { this.socket = socket; }
public void run() {
try (Socket s = socket;
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
BufferedReader fileIn = new BufferedReader(new FileReader(in.readLine()))) {
String line;
while ((line = fileIn.readLine()) != null) out.println(line);
} catch (IOException e) {
System.err.println("Handler error: " + e);
}
}
}
}
缺點:線程創建/銷毀開銷大、無上限易導致 OOM、上下文切換頻繁。
public class PooledFileServer {
private static final ExecutorService POOL = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(1001)) {
while (true) {
Socket client = server.accept();
POOL.execute(new ClientHandler(client)); // 復用線程
}
}
}
// ClientHandler 同多線程版
}
優化點:固定線程數、隊列緩衝突發、拒絕策略 (CallerRunsPolicy/DiscardOldestPolicy)。
SocketChannel、ServerSocketChannel、FileChannel — 雙向、可非阻塞。ByteBuffer (位置/限制/容量) — 數據容器。
public class NioServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(1001));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
for (SelectionKey key : selector.selectedKeys()) {
selector.selectedKeys().remove(key);
if (key.isAcceptable()) accept(key);
else if (key.isReadable()) read(key);
else if (key.isWritable()) write(key);
}
}
}
// accept/read/write 實現略...
}
優勢:單線程支撐萬級連接 (C10K)、無線程切換、內存可控。Netty、Mina、gRPC、Jetty、Undertow** 均基於 NIO 構建。
| 場景 | 推薦模型 | 關鍵注意 |
|---|---|---|
| 低併發、簡單交互 | BIO 線程池 | 超時設置、資源關閉 |
| 高併發、長連接 (IM、推送) | NIO (Netty) | 粘包/拆包、心跳、背壓 |
| 文件傳輸、大數據流 | NIO FileChannel + Mmap / Zero-Copy | transferTo/transferFrom |
| 微服務 RPC | gRPC (HTTP/2 + Protobuf) / Dubbo (Netty) | 序列化、負載均衡、熔斷 |
read() 可能不足請求長度,需循環讀取直到滿足協議長度。socket.shutdownOutput() 發送 FIN,對端讀到 -1 關閉輸入,雙向關閉。socket.setSoTimeout(ms) (讀超時)、selector.select(timeout)、連接空閒檢測。socket.setSendBufferSize()/setReceiveBufferSize()、ByteBuffer.allocateDirect() (堆外內存、零拷貝)。SSLSocketFactory / SSLServerSocketFactory 啟用 TLS、雙向認證。Java Socket 從 BIO 到 NIO,再到 Netty/gRPC 等高性能框架,核心始終是 **「通信協議設計 + I/O 模型選擇 + 併發控制 + 資源管理」**。理解 TCP 流特性、掌握 Reactor 模式、熟悉主流框架原理,是構建高性能、穩健網絡應用的必經之路。**現代新項目首選 Netty/gRPC,避免手寫 NIO;維護舊系統時,線程池 + BIO 仍是性價比最高的選擇。**