1. 程式人生 > >Socket中粘包的坑的處理

Socket中粘包的坑的處理

經常在網上看到很多類似於粘包的問題以及處理,但是因為這個跟自己本身的工作內容關係不大,所以也就沒怎麼了解。結果今天就被坑了一大把。

起因

最近由於有個投屏的軟體需要模擬多個客戶端進行連線投屏,所以用java寫了個客戶端模擬多個同時進行併發同屏的情況。而他的協議過程是這樣子的。當你傳送開始投屏的命令一樣,接收端也就是我們所說的服務端會回覆幾條socket的資訊(這個是重點),但是因為我們是模擬客戶端的,所以並不是所有的資料都是我關心的。只有其中一條關於回覆的埠的資訊是需要接收處理。所以就有了坑的問題了。

問題

在測試的過程中發現,經常是有概率的出現模擬了4個客戶端進行併發時服務端只回復了3條關於埠的資料,總會有概率的少了一兩條。那問題總得定位到底是程式碼處理的問題,還是說是機器本身就沒有收到對應的包呢。
以下是wireshark抓包的結果
這裡寫圖片描述

實際證明是有收到這個回覆包的, 那麼就證明確實是程式碼的問題了。那程式碼問題在哪 我們看看出錯的程式碼

 byte srcArray[] = new byte[1024];
 int count = socket.getInputStream().read(srcArray);
 byte[] msgLenArray = new byte[4];
 System.arraycopy(srcArray, 0, msgLenArray, 0, 4);
 int msgLen = (MsgUtils.bytesToInt2(msgLenArray));
 ...
   if (temp[9] == 1
) { switch (temp[10]) { case 101: Msg.CMsgConnectResp cMsgConnectResp = Msg.CMsgConnectResp.parseFrom(msg); log.append("收到指令型別:" + (temp[9]) + ", 指令號:" + temp[10] + "連線結果:" + cMsgConnectResp.getResultType() + ", 接收端版本:" + cMsgConnectResp.getReceiverVersion() + ", 支援最大視窗數量:"
+ cMsgConnectResp.getMaxWindowCount() + "\n"); ...

程式碼實際很簡單就是宣告一個byte的陣列,每次讀1024個位元組。再來將讀取到的資料進行解析,表面上看起來沒什麼問題。

可是細想一個問題。就是剛才我們提到的,當我們進行投屏的時候,服務端是會連續回覆幾個socket的資料的,然而我們一下子讀了那麼1024個位元組的資料,就會把幾個資料包都讀到一起,那如果幸運的話,回覆埠的資料先到了,嗯 沒問題,那如果是其他資料型別先到了,會發生什麼事情呢。。很悲劇。資料包會被丟棄掉。

解決

既然遇到這個問題,就肯定有解決的方法。下來看看解決的方式

 byte srcArray[] = new byte[11];
 socket.getInputStream().read(srcArray);
 byte[] msgLenArray = new byte[4];
 System.arraycopy(srcArray, 0, msgLenArray, 0, 4);
 int msgLen = (MsgUtils.bytesToInt2(msgLenArray));
 if (msgLen > 0) {
     byte contentByte[] = new byte[msgLen];
     socket.getInputStream().read(contentByte);
     System.out.println(len[9]);
     System.out.println(len[10]);
     if (len[9] == 1) {

     }

以上就能夠完美的解決問題了,我們每次讀取都讀11個位元組。因為協議頭的長度就是11個位元組。再來根據協議頭裡面的body的長度我們再去讀對應的長度。這樣子就避免了讀取的時候盲目的讀取1024個長度的問題了。