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模塊轉化與讀取都是對字節進行操作!
