TCP 為流式協議,無訊息邊界。發送端連續 write 多個訊息,接收端一次 read 可能讀到多個訊息粘在一起,或只讀到半個訊息。需應用層協議劃分邊界。
\n,但訊息內容不可含分隔符。協議格式:[2 bytes 長度][N bytes 體]。長度欄位固定 2 字節,表示後續訊息體字節數(最大 65535)。
class ReceiveThread extends Thread {
private final Socket socket;
private byte[] buffer = new byte[0]; // 累積緩衝
@Override public void run() {
try (InputStream in = socket.getInputStream()) {
while (true) {
// 1. 確保擁有至少 2 字節長度頭
if (buffer.length < 2) {
byte[] head = new byte[2 - buffer.length];
int read = in.read(head);
if (read < 0) continue;
buffer = concat(buffer, head, 0, read);
if (buffer.length < 2) continue;
}
// 2. 解析長度
int bodyLen = ((buffer[0] & 0xFF) << 8) | (buffer[1] & 0xFF);
// 3. 確保擁有完整訊息體
if (buffer.length - 2 < bodyLen) {
byte[] body = new byte[bodyLen + 2 - buffer.length];
int read = in.read(body);
if (read < 0) continue;
buffer = concat(buffer, body, 0, read);
if (buffer.length - 2 < bodyLen) continue;
}
// 4. 取出完整訊息
byte[] msg = Arrays.copyOfRange(buffer, 2, 2 + bodyLen);
System.out.println("收到: " + new String(msg, StandardCharsets.UTF_8));
// 5. 移除已處理資料,保留剩餘
buffer = Arrays.copyOfRange(buffer, 2 + bodyLen, buffer.length);
}
} catch (IOException e) { e.printStackTrace(); }
}
private byte[] concat(byte[] a, byte[] b, int off, int len) {
byte[] res = new byte[a.length + len];
System.arraycopy(a, 0, res, 0, a.length);
System.arraycopy(b, off, res, a.length, len);
return res;
}
}
伺服端同樣組裝 [長度][體] 回覆,確保客戶端能正確解析。
byte[] 累積緩衝,處理半包讀取。& 0xFF)。生產環境建議使用 Netty 等成熟框架,其 LengthFieldBasedFrameDecoder 內建黏包處理,更健壯且高效能。