一個完整的socket recv()案例,包括解決粘包、服務器主動推數據的問題


前言:

本文是針對socket長連接(涉及到服務器主動推數據),每個包頭的拼接算法和長度都不一樣,具體的包頭小伙伴們問自己公司的開發吧,本文只是提供思路。再啰嗦一句:recv到的包頭中數字進行某種運算后得到的就是開發定義的返回code。

粘包問題解決思路:

python中的socket recv()是阻塞接收的,所以不存在同時發送多個數據接收錯亂的情況。網上對於粘包有各種各樣的解決辦法,什么每次recv前sleep幾秒,保證緩存中有足夠數據在接收。還有說在recv()中加個wait的參數,保證每次都接受滿1024個數據長度。我覺得網上唯一正確的辦法是:先知道自己想要接收的數據的大小,然后用一個死循環接收完所有數據。

作者一開始是使用靜態拆包的方式,就是每次send后都recv一次,然后把每次recv的結果都拼在一起,然后再根據包頭中的包體長度拆出一個個完整包。這種方式有個缺點:如果后面的請求需要用到前面請求返回值做參數,那么就涼涼了。

這時候就需要用到動態拆包,send一個請求recv一個返回結果,然后解析出完整數據。但是實現的過程中發現幾個問題:

(1)返回數據的長度跟我從包頭里得到的長度不一致,我以為是粘包導致的接收不全,因為我后來加了個一樣的請求,就能接收全了。

(2)后來我在每個請求發送前都加了sleep(10),第16個請求接受全了,但是后面的數據直接報錯了。

(3)后來偶然一次,我發現能運行成功不報錯,但是在大部分時運行會報錯。

(4)然后發現send一個請求,返回了兩個完整的包。

結合上面的四個問題,我開始了漫長的尋求解答的過程,后來得高人解答,才發現了問題——服務器主動推了數據。現在想想上面4點可不就是服務器主動推數據的表現嘛。

 

下面直接上方法,同時解決粘包、服務器主動推數據的問題。

思路:send之后,先recv(8)包頭,得到包體大小,然后用死循環recv全。然后要用包頭算法對得到的返回數據進行判斷,進一步保證了這個包就是我需要的數據包。

 

 1 def sendData(self, data, t, respHeader):
 2         self.data = data
 3         self.t = t
 4         self.respHeader=respHeader
 5         self.s.send(data)  # 傳輸包裝好的請求
 6         total_data = bytes()
 7         header = 1
 8 
 9         for i in range(5):  #每次send,我規定它最多去recv5次,以防浪費時間
10             header_bytes = self.s.recv(8)  # 先接收包頭
11             body_len = struct.unpack("!2I", header_bytes)[0] #這是包體長度
12             header = struct.unpack("!2I", header_bytes)[1] #開發定義的用來做運算
13             print("應該接收的長度:", body_len)
14             while len(total_data) < body_len:
15                 total_data += self.s.recv(body_len)
16                 print("實際接收的長度:", len(total_data))
17             18             header_code = header & 0x3xxxxxxx  #做運算得到返回請求的code
19             print("header_code: " + str(header_code))
20             #如果算出來等於這個返回請求的頭或者返回error,跳出for循環,不再繼續recv
21             if header_code == int(respHeader) or header_code == 1: 
22                 print("over")
23                 break
24            else:
25             #如果接收的不是我需要的包頭,清空total_data,重新for循環從緩存中recv數據
26                 total_data = bytes()
27          if i >= 5:
28             print("返回數據有誤")

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM