tcp粘包問題緣由及解決辦法

1.粘包概念及產生緣由

1.1粘包概念:

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

1.2粘包緣由:

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

  • 發送端緣由: 因爲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模塊轉化與讀取都是對字節進行操做!算法

相關文章
相關標籤/搜索