網絡編程是 Java 開發中不可或缺的技能,本文系統梳理了網絡通信的理論基礎、核心 API 以及 UDP/TCP 兩種協議的實戰代碼,幫助開發者建立完整的知識體系。
計算機網絡通信遵循特定規則,這就是網絡模型。主流有兩種:
| 層級 | 核心功能 | 數據單元 | 典型設備/協議 |
|---|---|---|---|
| 物理層 | 定義物理設備標準,傳輸比特流 | 比特 | 網線、光纖、中繼器 |
| 數據鏈路層 | MAC 地址封裝/解封裝 | 幀 | 交換機、網卡 |
| 網絡層 | IP 地址封裝/解封裝,路由選擇 | 數據包 | 路由器、IP 協議 |
| 傳輸層 | 端口號、分段傳輸、重組,提供 TCP/UDP | 段 | TCP(可靠)、UDP(高效) |
| 會話層 | 建立/管理/終止會話 | 數據 | RPC、SQL |
| 表示層 | 數據解釋、加密解密、壓縮解壓 | 數據 | JPEG、MPEG、SSL |
| 應用層 | 終端用戶應用協議 | 數據 | HTTP、FTP、DNS |
InetAddress 類操作。// 獲取本機地址
InetAddress local = InetAddress.getLocalHost();
// 根據主機名/域名獲取地址
InetAddress remote = InetAddress.getByName("www.example.com");
// 獲取 IP 字符串
String ip = remote.getHostAddress();
// 獲取主機名
String host = remote.getHostName();
Socket = IP 地址 + 端口號,是網絡通信的端點。Java 提供 java.net.Socket (TCP) 和 java.net.DatagramSocket (UDP)。
核心類:DatagramSocket(發送/接收端)、DatagramPacket(數據包)。
發送端流程:創建 Socket → 封裝數據包(數據、長度、目標 IP、端口) → send() → 關閉資源。
接收端流程:創建 Socket(綁定端口) → 準備空數據包 → receive() 阻塞等待 → 解析數據包 → 關閉資源。
// 發送端示例
DatagramSocket ds = new DatagramSocket();
byte[] data = "Hello UDP".getBytes();
InetAddress addr = InetAddress.getByName("192.168.1.100");
DatagramPacket dp = new DatagramPacket(data, data.length, addr, 8888);
ds.send(dp);
ds.close();
// 接收端示例
DatagramSocket ds = new DatagramSocket(8888);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp); // 阻塞
String msg = new String(dp.getData(), 0, dp.getLength());
System.out.println("來自 " + dp.getAddress().getHostAddress() + ": " + msg);
ds.close();
進階:UDP 聊天室(多線程) —— 發送線程處理鍵盤輸入,接收線程循環監聽,實現雙向通信。
核心類:ServerSocket(服務端)、Socket(客戶端/服務端連接對象)。通過「三次握手」建立連接,形成輸入/輸出流通道。
客戶端流程:`new Socket(ip, port)` → 獲取輸出流寫數據 / 輸入流讀回應 → 關閉。
服務端流程:`new ServerSocket(port)` → `accept()` 阻塞等待連接 → 獲取客戶端 Socket 的流進行通信 → 處理完關閉客戶端 Socket(服務端 Socket 通常不關閉)。
// 客戶端發送文件並接收反饋(解決阻塞問題)
Socket s = new Socket("192.168.1.100", 9999);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("photo.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1) bos.write(buf, 0, len);
bos.flush();
// 關鍵:通知服務端輸出流結束,避免雙方 read() 互相阻塞
s.shutdownOutput();
// 讀取服務端反饋
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
System.out.println("服務端: " + br.readLine());
bis.close(); s.close();
// 服務端多線程處理上傳
ServerSocket ss = new ServerSocket(9999);
while (true) {
Socket s = ss.accept();
new Thread(() -> {
try {
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
String fileName = System.currentTimeMillis() + ".jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1) bos.write(buf, 0, len);
bos.flush();
// 反饋
s.getOutputStream().write("上傳成功".getBytes());
bos.close(); s.close();
} catch (IOException e) { e.printStackTrace(); }
}).start();
}
現象:雙方都在 `read()`/`readLine()` 等待,程序掛起。
原因:流無終結標記,接收方不知道發送方何時結束。
方案:
socket.shutdownOutput(),接收方 `read()` 返回 -1,感知流結束。