網絡通信是現代應用開發的核心基礎。Java 在 java.net 包中提供了豐富的 API,支援 TCP、UDP、多播以及新一代非阻塞 I/O(NIO)。本文將通過完整的代碼示例,系統梳理這四種通信模型的核心機制、進階優化方向及最佳實踐。
TCP 通信基於 Socket(客戶端)與 ServerSocket(服務端)。建立連接後,雙方通過輸入/輸出流進行數據交換。
public class SimpleTcpClient {
public static void main(String[] args) throws IOException {
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
BufferedReader console = null;
try {
socket = new Socket(InetAddress.getLocalHost(), 5678);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
console = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = console.readLine()) != null) {
out.println(line);
if ("end".equals(line)) break;
System.out.println("Server: " + in.readLine());
}
} finally {
closeQuietly(socket, in, out, console);
}
}
}
public class SimpleTcpServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(5678);
Socket client = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
out.println("Response: " + line);
if ("end".equals(line)) break;
}
}
}
}
缺點:單線程模型下,服務端同一時間只能處理一個客戶端請求,吞吐量極低。
為每個客戶端連接創建獨立線程,實現並發處理。
public class SmartTcpServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(5678)) {
while (true) {
Socket client = server.accept();
new Thread(new ServerHandler(client)).start();
}
}
}
}
class ServerHandler implements Runnable {
private final Socket socket;
public ServerHandler(Socket socket) { this.socket = socket; }
@Override
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
out.println("Response: " + line);
if ("end".equals(line)) break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
頻繁創建/銷毀 Socket 開銷大,參照數據庫連接池思想,預先建立連接池複用連接。
public class TcpConnectionPool {
private final Socket[] sockets;
private final boolean[] inUse;
private final InetAddress address;
private final int port;
public TcpConnectionPool(InetAddress address, int port, int poolSize) throws IOException {
this.address = address; this.port = port;
this.sockets = new Socket[poolSize];
this.inUse = new boolean[poolSize];
for (int i = 0; i < poolSize; i++) {
sockets[i] = new Socket(address, port);
}
}
public synchronized Socket borrow() throws Exception {
for (int i = 0; i < sockets.length; i++) {
if (!inUse[i]) { inUse[i] = true; return sockets[i]; }
}
throw new IllegalStateException("連接池耗盡");
}
public synchronized void release(Socket socket) {
for (int i = 0; i < sockets.length; i++) {
if (sockets[i] == socket) { inUse[i] = false; break; }
}
}
public void shutdown() {
for (Socket s : sockets) closeQuietly(s);
}
}
生產環境建議使用成熟框架(如 Apache Commons Pool、HikariCP 擴展)以獲得健壯的生命週期管理、空閒檢測、動態擴縮容等能力。
UDP 適用於實時性要求高、容忍丟包的場景(視頻流、即時遊戲)。核心類為 DatagramSocket 與 DatagramPacket。
public class UdpClient {
public static void main(String[] args) throws IOException {
InetAddress server = InetAddress.getLocalHost();
int port = 5678;
try (DatagramSocket socket = new DatagramSocket();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = console.readLine()) != null) {
byte[] data = line.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, server, port);
socket.send(packet);
if ("end".equals(line)) break;
}
}
}
}
public class UdpServer {
public static void main(String[] args) throws IOException {
try (DatagramSocket socket = new DatagramSocket(5678)) {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
socket.receive(packet);
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println(packet.getAddress().getHostAddress() + ": " + msg);
if ("end".equals(msg)) break;
packet.setLength(buffer.length); // 重置長度供下次接收
}
}
}
}
注意:UDP 無連接特性意味著客戶端發送數據時不會因服務端未啟動而拋出異常,需自行設計心跳或應答機制保證可達性。
多播使用 D 類 IP 地址(224.0.0.0 ~ 239.255.255.255),基於 UDP 實現一對多通信。核心類 MulticastSocket 提供 joinGroup/leaveGroup 管理組成員。
public class MulticastSender {
public static void main(String[] args) throws IOException {
InetAddress group = InetAddress.getByName("230.0.0.1");
int port = 5678;
try (MulticastSocket socket = new MulticastSocket();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = console.readLine()) != null) {
byte[] data = line.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, group, port);
socket.send(packet);
if ("end".equals(line)) break;
}
}
}
}
public class MulticastReceiver {
public static void main(String[] args) throws IOException {
int port = 5678;
InetAddress group = InetAddress.getByName("230.0.0.1");
try (MulticastSocket socket = new MulticastSocket(port)) {
socket.joinGroup(group);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
socket.receive(packet);
String msg = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到: " + msg);
if ("end".equals(msg)) break;
packet.setLength(buffer.length);
}
}
}
}
JDK 1.4 引入 NIO,核心三大組件:Channel(通道)、Buffer(緩衝區)、Selector(選擇器)。支持單線程管理成千上萬連接,是 Netty、MINA 等高性能網絡框架的基石。
public class NioClient {
public static void main(String[] args) throws IOException {
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("127.0.0.1", 5678));
channel.configureBlocking(true); // 演示阻塞模式
Charset charset = StandardCharsets.UTF_8;
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 發送 HTTP 請求
String request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
channel.write(charset.encode(request));
// 讀取響應
while (channel.read(buffer) != -1) {
buffer.flip();
System.out.print(charset.decode(buffer));
buffer.clear();
}
channel.close();
}
}
進階方向:生產級 NIO 應用應採用 Selector 實現單線程多路複用,結合 ByteBuffer 的零拷貝、直接內存等特性,並妥善處理半包/黏包、編解碼、空閒檢測等問題。推薦直接使用 Netty 等成熟框架。
| 模型 | 適用場景 | 優勢 | 劣勢 |
|---|---|---|---|
| TCP (BIO) | 低並發、強一致性交互(RPC、管理後台) | 編程簡單、可靠有序 | 線程資源消耗大,難以支撐高並發 |
| TCP (NIO/Netty) | 高並發、長連接(IM、推送、網關) | 單線程萬級連接、擴展性強 | 編程複雜度高,需處理黏包/半包 |
| UDP | 實時音視頻、遊戲同步、日誌收集 | 延遲低、無連接開銷、支持廣播/多播 | 不可靠、無序、需應用層保證可達性 |
| 多播 | 股票行情推送、集群發現、IPTV | 網絡層面一對多,節省帶寬 | 跨公網受限、不可靠、穿透 NAT 難 |
現代架構中,通常採用 Netty (NIO) 作為通用網絡框架底座,根據業務特性在應用層協議(HTTP/2、gRPC、WebSocket、自定義協議)上構建服務。