什么是粘包現(xiàn)象以及socket收發(fā)消息原理
只有tcp才會出現(xiàn)粘包問題
解決粘包問題的核心思想:每次都會讀完收到的內(nèi)容
自定義報頭:
固定報頭長度,報頭內(nèi)容為數(shù)據(jù)長度
:
該模塊可以把一個類型存崖,如數(shù)字熬芜,轉(zhuǎn)成固定長度的bytes,這樣就可以把數(shù)據(jù)長度轉(zhuǎn)換成固定長度的bytes
用法:struct.pack('i',1111111111111)
但是之前我們了解過報頭的內(nèi)容并不止這一個,所以,我們可以用字典的形式作為報頭,字典內(nèi)容比如是文件名,md5值,數(shù)據(jù)長度,然后用json來序列化這個字典給他發(fā)送出去
這樣又會產(chǎn)生一個新的問題,,,,接收方因為無法確定字典的長度而又會粘包
所以就是:
首先通過struct發(fā)送轉(zhuǎn)換成bytes的字典的長度,,然后發(fā)送報頭字典,最后發(fā)送數(shù)據(jù)
練習(xí)
讓我們基于tcp先制作一個遠程執(zhí)行命令的程序(1:執(zhí)行錯誤命令 2:執(zhí)行dir 3:執(zhí)行tasklist(查看進程列表))
res.stdout.read()讀出的就是GBK編碼的枚荣,在接收端需要用
服務(wù)端
from socket import *
import json, struct, subprocess
server = socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
conn, addr = server.accept()
while True:
try:
cmd = conn.recv(1024)
res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = res.stdout.read()
stderr = res.stderr.read()
# 這是報頭字典
hand_dic={
'file_name':'a.txt',
'md5':'4f56ds4f65ds4f',
'data_size':len(stdout)+len(stderr)
}
#字典序列化后轉(zhuǎn)化為二進制
hand_json = json.dumps(hand_dic)
hand_bytes = hand_json.encode('utf-8')
#發(fā)報頭字典長度
hand_size=struct.pack('i',len(hand_bytes))
conn.send(hand_size)
#發(fā)報頭字典
conn.send(hand_bytes)
#發(fā)數(shù)據(jù)
conn.send(stdout + stderr)
except ConnectionResetError:
break
except ConnectionAbortedError:
break
conn.close()
server.close()
客戶端
from socket import *
import json, struct
client=socket()
client.connect(('127.0.0.1',8080))
while True:
cmd=input('::>').strip()
client.send(cmd.encode('utf-8'))
#接收報頭字典長度
hand_size=struct.unpack('i',client.recv(4))[0]
#接收報頭
hand_bytes=client.recv(hand_size)#收到bytes格式的json
hand_json=hand_bytes.decode('utf-8')#還原成json格式
hand=json.loads(hand_json)#反序列化
#數(shù)據(jù)長度
total_size=hand['data_size']
# 循環(huán)取值
data_size=0#已經(jīng)接收的長度
res=b''#用來存放每次取出的數(shù)據(jù)
while data_size<total_size:
data=client.recv(1024) #每次取1024字節(jié)
res+=data #取完后存于res
data_size+=len(data)#更新已經(jīng)取出的長度
print(res.decode('gbk'))#打印取出的數(shù)據(jù),注意!!!Windows默認gbk編碼的,所以這里要用gbk解碼
client.close()