tcp粘包問題原因及解決辦法


1.粘包概念及產生原因

1.1粘包概念:

  • TCP粘包是指發送方發送的若干包數據到接收方接收時粘成一包,從接收緩沖區看,后一包數據的頭緊接着前一包數據的尾。
  • 粘包可能由發送方造成,也可能由接收方造成。
  • 只有TCP有粘包現象,UDP永遠不會粘包
  • 粘包不一定會發生

1.2粘包原因:

所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。

  • 發送端原因: 由於TCP協議本身的機制(面向連接的可靠地協議-三次握手機制)客戶端與服務器會維持一個連接(Channel),數據在連接不斷開的情況下,可以持續不斷地將多個數據包發往服務器,但是如果發送的網絡數據包太小,那么他本身會啟用Nagle算法(可配置是否啟用)對較小的數據包進行合並(基於此,TCP的網絡延遲要UDP的高些)然后再發送(超時或者包大小足夠)。那么這樣的話,服務器在接收到消息(數據流)的時候就無法區分哪些數據包是客戶端自己分開發送的,這樣產生了粘包.
  • 接收端原因: 服務器在接收到數據庫后,放到緩沖區中,如果消息沒有被及時從緩存區取走,下次在取數據的時候可能就會出現一次取出多個數據包的情況,造成粘包現象。

2. tcp粘包解決辦法

  • 在每次使用tcp協議發送數據流時,在開頭標記一個數據流長度信息,並固定該報文長度(自定義協議).在客戶端接收數據時先接收該長度字節數據,判斷客戶端發送數據流長度,並只接收該長度字節數據,就可以實現拆包,完美解決tcp粘包問題.
#struct模塊介紹
#該模塊可以把一個類型,如數字,轉成固定長度為4的bytes類型
import struct
res = struct.pack('i',12345)	#i表示整數int
print(res,len(res),type(res))  #長度是4

res2 = struct.pack('i',12345111)
print(res,len(res),type(res2))  #長度也是4

unpack_res =struct.unpack('i',res2)
print(unpack_res)  #(12345111,)
print(unpack_res[0]) #12345111

###################客戶端client###################
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
sock=socket.socket()	
sock.connect(('127.0.0.1', 13459))	

content1='我好'.encode('utf-8')		#要發送消息
content2='他也好'.encode('utf-8')

con1_len=struct.pack('i',len(content1))	# 計算要發送消息(字節)的長度,並使用struct模塊轉化為長度為4的字節b'\x06\x00\x00\x00'
sock.send(con1_len)						#先把這個4字節的報文發送
sock.send(content1)						#發送內容

con2_len=struct.pack('i',len(content2))
sock.send(con2_len)
sock.send(content2)


sock.close()
###################服務端server###################
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import struct
import socket

sock = socket.socket()		#買手機
sock.bind(('127.0.0.1', 13459))		#插卡
sock.listen(10)  	#開機(同時最大連接10)

conn, addr = sock.accept()		#(受)與cilent端connect(攻)對應.
msg = conn.recv(4)				#首先接收4個字節(4個字節由client端struct模塊轉化)
len_msg= struct.unpack('i',msg)	#struct模塊讀取報文,判斷跟隨數據長度.返回值是一個元祖(6,)
size_msg=len_msg[0]					#取值判斷跟隨數據長度
msg = conn.recv(size_msg)			#接收報文讀取長度字節
print(msg.decode('utf-8'))			#解碼輸出

msg=conn.recv(4)
len_msg=struct.unpack('i',msg)
size_msg=len_msg[0]
msg = conn.recv(size_msg)
print(msg.decode('utf-8'))


conn.close()
sock.close()

!struct模塊轉化與讀取都是對字節進行操作!


免責聲明!

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



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