問題的根源在于,接收端不知道發送端將要傳送的字節流的長度,所以解決粘包的方法就是圍繞,如何讓發送端在發送數據前,把自己將要發送的字節流總大小讓接收端知曉,然后接收端來一個死循環接收完所有數據。
簡單的解決方法
這種方法的缺陷是程序的運行速度遠快于網絡傳輸速度,所以在發送一段字節前,先用send去發送該字節流長度,這種方式會放大網絡延遲帶來的性能損耗。
高級一點的解決方法
為字節流加上自定義固定長度報頭,報頭中包含字節流長度,然后一次send到對端,對端在接收時,先從緩存中取出定長的報頭,然后再取真實數據
struct模塊
該模塊可以把一個類型,如數字,轉成固定長度的bytes
importjson,struct
#假設通過客戶端上傳1T:1073741824000的文件a.txt
#為避免粘包,必須自定制報頭
header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'}#1T數據,文件路徑和md5值
#為了該報頭能傳送,需要序列化并且轉為bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8')#序列化并轉成bytes,用于傳輸
#為了讓客戶端知道報頭的長度,用struck將報頭長度這個數字轉成固定長度:4個字節
head_len_bytes=struct.pack('i',len(head_bytes))#這4個字節里只包含了一個數字,該數字是報頭的長度
#客戶端開始發送
conn.send(head_len_bytes)#先發報頭的長度,4個bytes
conn.send(head_bytes)#再發報頭的字節格式
conn.sendall(文件內容)#然后發真實內容的字節格式
#服務端開始接收
head_len_bytes=s.recv(4)#先收報頭4個bytes,得到報頭長度的字節格式
x=struct.unpack('i',head_len_bytes)[0]#提取報頭的長度
head_bytes=s.recv(x)#按照報頭長度x,收取報頭的bytes格式
header=json.loads(json.dumps(header))#提取報頭
#最后根據報頭的內容提取真實的數據,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)
我們可以把報頭做成字典,字典里包含將要發送的真實數據的詳細信息,然后json序列化,然后用struck將序列化后的數據長度打包成4個字節(4個字節足夠用了)
發送時:
先發報頭長度
再編碼報頭內容然后發送
最后發真實內容
接收時:
先收報頭長度,用struct取出來
根據取出的長度收取報頭內容,然后解碼,反序列化
從反序列化的結果中取出待取數據的詳細信息,然后去取真實的數據內容
以上內容為大家介紹了python粘包解決方法,希望對大家有所幫助,如果想要了解更多Python相關知識,請關注IT培訓機構:千鋒教育。