UDP(User Datagram Protocol)無連接、不可靠、有邊界,適合即時性要求高、容忍丟包場景(視頻流、DNS、遊戲同步)。本文給出完整的 UDP Echo 服務端與帶超時重傳的客戶端實現,並分析單線程模型局限。
| TCP | UDP |
|---|---|
ServerSocket / Socket | DatagramSocket(客戶端/服務端通用) |
流式 InputStream/OutputStream | 數據報 DatagramPacket(含數據、地址、端口) |
| 連接建立三次握手 | 無連接,直接發送 |
| 保證有序、無重複、無丟失 | 不保證,應用層自行處理 |
public class UDPEchoServer {
private static final int ECHOMAX = 255;
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(5500);
DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX);
while (true) {
socket.receive(packet); // 阻塞等待數據報
System.out.println("Client: " + packet.getAddress() + ":" + packet.getPort());
socket.send(packet); // 原樣返回
packet.setLength(ECHOMAX); // 重置長度,防止緩衝區收縮
}
}
}
關鍵點:packet.setLength(ECHOMAX) 必須在每次 send 後調用,因 receive 會將 length 設置為實際接收長度,下次發送會只發送該長度數據。
public class UDPEchoClientTimeout {
private static final int TIMEOUT = 3000; // 3 秒超時
private static final int MAXTRIES = 5; // 最大重試 5 次
public static void main(String[] args) throws IOException {
InetAddress server = InetAddress.getByName("127.0.0.1");
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(TIMEOUT); // 設置接收超時
byte[] data = "Hi, World".getBytes();
DatagramPacket sendPkt = new DatagramPacket(data, data.length, server, 5500);
DatagramPacket recvPkt = new DatagramPacket(new byte[data.length], data.length);
int tries = 0;
boolean received = false;
do {
socket.send(sendPkt);
try {
socket.receive(recvPkt);
if (!recvPkt.getAddress().equals(server))
throw new IOException("Unknown source");
received = true;
} catch (InterruptedIOException e) {
tries++;
System.out.println("Timed out, " + (MAXTRIES - tries) + " more tries...");
}
} while (!received && tries < MAXTRIES);
if (received) System.out.println("Received: " + new String(recvPkt.getData()));
else System.out.println("No response -- giving up.");
socket.close();
}
}
socket.setSoTimeout(ms):設置 receive() 阻塞上限,超時拋 InterruptedIOException(SocketTimeoutException 父類)。TIMEOUT *= 2 避免擁塞崩潰。| 局限 | 影響 | 解決方案 |
|---|---|---|
| 串行處理 | 高併發下請求排隊、延遲飆升 | 線程池 + 工作隊列;或 Netty/UDP NIO 事件循環 |
| 無背壓 | 突發流量直接丟包/ OOM | 應用層限流(Token Bucket)、內核 SO_RCVBUF 調大 |
| 無會話識別 | 難以關聯請求響應 | 自定義協議頭加 requestId,客戶端維护映射表 |
UDP 編程核心是應用層承擔可靠性責任:超時重傳、序列號去重、流控擁塞控制。理解 DatagramSocket/DatagramPacket 雙元組模型,配合 setSoTimeout 實現基礎重傳,是掌握高性能網絡編程的必修課。