在傳輸資料訊息時因為tcp協議使用了優化方法(nagle演算法),將多次間隔較小且資料量小的資料,合併成乙個大的資料塊,然後進行封包,這樣,接收端就難於分辨出來了,所以會產生粘包效果。
在這種情況下我們需要製作乙個報頭來告訴接收端我們要傳送的資料的長度,來方便接收端接收。
第一步:製作固定長度的報頭
header_dic =
# 字典方便儲存資料
header_json = json.dumps(header_dic)
# 把字典轉換成js格式(字串型別)
header_bytes = header_json.encode(
'gbk'
)
(在這裡我們基於上次模擬ssh遠端執行命令的**上改動)
這個用字典存放我們傳送資料的長度外加一些其他以後可能會用到的其他內容。因為字典無法用encode()轉換成位元組,所以我們要用json模組把字典轉換成js格式。
第二步:把報頭打包傳送長度
conn.send(struct.pack(
'i',
len(header_bytes)
))
第三步:傳送報頭
conn.send(header_bytes)
第四步:傳送需要給接收端的資料
conn.send(stdout)
conn.send(stdeer)
這樣結合之前的模擬ssh遠端模擬命令之後的總**:
1、服務端
import socket
import subprocess
import struct # 製作報頭的模組
import json # 轉換資料格式(序列化)
phone = socket.socket(socket.af_inet, socket.sock_stream)
phone.setsockopt(socket.sol_socket, socket.so_reuseaddr,1)
# 修復埠被占用的問題。
phone.bind(
('127.0.0.1'
,8848))
phone.listen(5)
# 監聽,掛起連線數
while
true
: conn, client = phone.accept(
)# 建立連線
while
true
:try
:# 收命令
cmd = conn.recv(
8096
)# 長度足夠收取命令
# 執行命令、拿到結果
obj = subprocess.popen(cmd.decode(
'gbk'
), shell=
true
, stdout=subprocess.pipe,
# 存放正確的通道
stderr=subprocess.pipe)
# 存放錯誤的通道
stdout = obj.stdout.read(
)# 把裡面的內容讀出來放在這裡
stdeer = obj.stderr.read(
)# 把命令結果給客戶端
# 第一步:製作固定長度的報頭
header_dic =
# 字典方便儲存資料
header_json = json.dumps(header_dic)
# 把字典轉換成js格式(字串型別)
header_bytes = header_json.encode(
'gbk'
)# 第二步:先傳送報頭的長度
conn.send(struct.pack(
'i',
len(header_bytes)))
# 第三步:再發報頭
conn.send(header_bytes)
# 第四步:傳送真實資料
conn.send(stdout)
conn.send(stdeer)
except connectionreseterror as err:
break
conn.close(
)phone.close(
)
2、客戶端(接收端)
import socket
import struct
import json
phone = socket.socket(socket.af_inet, socket.sock_stream)
phone.connect(
('127.0.0.1'
,8848))
while
true
:# 發命令
cmd =
input
('>>:'
).strip()if
not cmd:
continue
phone.send(cmd.encode(
"gbk"))
# 拿到命令結果
# 第一步:先收取報頭的長度
obj = phone.recv(4)
header_size = struct.unpack(
'i', obj)[0
]# 第二步:再收報頭
header_bytes = phone.recv(header_size)
# 第三步:從報頭中間解析出對真是資料的描述資訊
header_json = header_bytes.decode(
'gbk'
) header_dic = json.loads(header_json)
print
(header_dic)
total_size = header_dic[
'total_size'
]# 第三步:接受真實的資料
recv_size =
0 recv_data = b''
while recv_size < total_size:
res = phone.recv(
1024
) recv_data += res
recv_size +=
len(res)
print
(recv_data.decode(
"gbk"))
phone.close(
)
在客戶端第三步中的迴圈就是確定避免了1024這個坑,這樣可以一直迴圈到收完乙個命令得出的全部資料(在資料量大於1024時)。 解決粘包問題
要知道!所謂粘包問題主要還是因為接收方不知道訊息之間的界限,不知道一次性提取多少位元組的資料所造成的。要解決粘包不外乎乙個條件,就是知道取多少,讓對方事先知道我們要發多少位元組的資料,然後造乙個容器,每次規定的往容器裡面倒水,當水快滿的時候,量出最後一勺還需要多少勺多少的水,最後把這水不多不少的往容...
解決粘包問題
注意注意注意 res subprocess.popen cmd.decode utf 8 shell true,stderr subprocess.pipe,stdout subprocess.pipe 的結果的編碼是以當前所在的系統為準的,如果是windows,那麼res.stdout.read ...
Python Day33 粘包問題及粘包解決方案
tcp粘包問題 python 粘包指的是資料與資料之間沒有明確的分界線,導致不能正確讀取資料!tcp協議也稱之為流式協議 udp稱為資料報協議 應用程式無法直接操作硬體,應用程式想要傳送資料則必須將資料交給作業系統,而作業系統需要需要同時為所有應用程式提供資料傳輸服務,也就意味著,作業系統不可能立馬...