首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Socket 通訊處理黏包問題:長度前綴協議實戰

     編輯:Java 相關知識

什麼是黏包?

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;
    }
}

伺服端回覆邏輯

伺服端同樣組裝 [長度][體] 回覆,確保客戶端能正確解析。

關鍵點總結

  1. 使用 byte[] 累積緩衝,處理半包讀取。
  2. 長度解析需處理無號字節(& 0xFF)。
  3. 迴圈內持續讀取直到湊齊完整訊息。
  4. 處理完畢後切片移除已消費資料,避免記憶體洩漏。

進階建議

生產環境建議使用 Netty 等成熟框架,其 LengthFieldBasedFrameDecoder 內建黏包處理,更健壯且高效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved