python 套接字 struck_Python socket粘包问题(最终解决办法)
套接字:
就是將傳輸層以下的協(xié)議封裝成子接口
對于應(yīng)用程序來說只需調(diào)用套接字的接口,寫出的程序自然是遵循tcp或udp協(xié)議的
實(shí)現(xiàn)第一個功能個:
實(shí)現(xiàn):通過客戶端向服務(wù)端發(fā)送命令,調(diào)取windows下面的cmd窗口,將服務(wù)端執(zhí)行命令的結(jié)構(gòu),返回并顯示在
客戶端窗口上。
subprocess:
1.可以將執(zhí)行結(jié)果返回
2.返回值是bytes類型
(基于這兩點(diǎn),可以應(yīng)用在server端,將服務(wù)端的返回直接以bytes的格式直接send給客戶端,
實(shí)現(xiàn)在客戶端的顯示)
問題1:粘包問題
粘包問題:實(shí)際是由TCP協(xié)議在傳輸數(shù)據(jù)時的2大特性來的
TCP協(xié)議又叫流式協(xié)議,在數(shù)據(jù)傳輸上,只要客戶端發(fā)送相應(yīng)的數(shù)據(jù)請求,
服務(wù)端會將數(shù)據(jù)像流水一樣不斷的send給客戶端
基于這個特點(diǎn),就會存在一個問題,當(dāng)客戶端給服務(wù)端發(fā)送一條命令,服務(wù)端成功接收并將命令的
結(jié)果返回到客戶端的時候,由于客戶端recv()的數(shù)量限制,可以一次不能完全取出,
這個時候就會存在,下次輸入命令客戶端首先拿到的返回值就是上次殘留的沒有收完的數(shù)據(jù)
基于粘包問題的解決思路就是:
發(fā)數(shù)據(jù)之前先把報頭發(fā)給對方,讓對方先知道要收的報頭的長度,后面再傳數(shù)據(jù)文件
自定義報頭:
為甚么要自定義報頭:
因?yàn)閟truck path(‘i’,3443242)
1.’i‘:類型不同,后面數(shù)字的長度大小也不同,大小是有限的(當(dāng)超出范圍時會報錯)
2.因?yàn)閳箢^里面含有的內(nèi)容可能不僅僅只有total_siz還有filename、hash等等,知識單純的把total_size
當(dāng)做報頭傳入不合理,所以我們要自定義報頭
Server端配置
from socket import *
importsocket,subprocess,struct,json
server=socket.socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)whileTrue:
conn,client=server.accept()print(client)whileTrue:try:
cmd= conn.recv(1024)if len(cmd) == 0: breakobj=subprocess.Popen(
cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out=obj.stdout.read()
err=obj.stderr.read()#制作報頭
header_dic={'filename':'a.txt','total_size':len(out)+len(err),'hash':'abc32i5o24'}#對包頭進(jìn)行序列化
header_json=json.dumps(header_dic) #字符串格式
header_bytes=header_json.encode('utf-8')#1.先發(fā)型報頭的長度 len(header_bytes) struck為固定的4個字節(jié)
conn.send(struct.pack('i',len(header_bytes)))#2.發(fā)送報頭
conn.send(header_bytes)#3.發(fā)送真是數(shù)據(jù)
conn.send(out)
conn.send(err)exceptConnectionResetError:breakconn.close()
server.close()
Client端配置
from socket import *
importsocket,struct,json
client=socket.socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))whileTrue:
cmd=input('輸入你要操作的命令:')
client.send(cmd.encode('utf-8'))if len(cmd) == 0:continue
#1.先收報頭的四個字節(jié),首先拿到報頭傳來的長度-》bytes
header=client.recv(4) #i類型足夠了 header為bytes類型
header_size=struct.unpack('i',header)[0] #拿到元祖形式,取第一個就是整個報頭的長度 print(header_size) ?#為報頭的長度值
#2.再收報頭(對應(yīng)服務(wù)端的conn.send(header_bytes))
header_bytes=client.recv(header_size) #根據(jù)報頭的固定長度去收接收
#3.解析包頭(就是將header_bytes文件先解碼成json格式)
header_str=header_bytes.decode('utf-8')
header_dic=json.loads(header_str)print(header_dic)
total_size=header_dic['total_size']print(total_size)
recv_size=0 #定義一個初始的接收變量為0,只是個計數(shù)變量,為了統(tǒng)計與總的total_size的len大小
res=b''
while recv_size
recv_data=client.recv(1024) #每次傳過來的recv_data是bytes類型
res+=recv_data
recv_size+=len(recv_data) #循環(huán)增加每次接收值的長度#cmd=client.recv(1024)
print(res.decode('gbk'))
client.close()
粘包問題的最終解決方案,分析:
服務(wù)端:
目的為了自定義報頭(報頭中不僅包含長度,可能還有文件名等信息)
subprocess
...1.制作報頭(字典形式)
header_dic={'filename':'a.txt','total_size':len(out)+len(err),'hash':'abc32i5o24'}2.通過json將報頭序列化再encode為bytes類型
header_json=json.dumps(header_dic) #字符串類型
header_bytes=header_json.encode('utf-8') #bytes類型
3.發(fā)送報頭的長度,struck目的是固定封裝好的報頭為4個字節(jié)長度 =====對應(yīng)客戶端剛開始 header=client.recv(4)
struck:1.將數(shù)字轉(zhuǎn)為bytes類型,保證發(fā)送過去的是bytes類型 2.固定4個字節(jié)
第一次發(fā):conn.send(struck.pack('i',len(header_bytes)))
先傳給客戶端固定了收的時候報頭的長度,不至于報頭和其他內(nèi)容粘在一起4.發(fā)送報頭 =======對應(yīng)客戶端接收報頭 header_bytes=client.recv(header_size)
第二次發(fā):conn.send(header_bytes)5.發(fā)送真實(shí)的數(shù)據(jù)
conn.send(out)
conn.send(err)
客戶端:(bytes--int)1.通過服務(wù)端返回的字節(jié),拿到報頭的的長度
header=client.recv(4) #header是4個bytes字節(jié)
header_size=struck.unpack('i',header)[0] #字節(jié)頭-拿到int大小
2.再收報頭
header_bytes=client.recv(header_size) #bytes類型
3.解析報頭(1.先把內(nèi)存中存放的bytes類型,用decode('utf-8')解碼為字符串)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json) #json反序列化出字典格式 (對應(yīng)server第2步)
print(header_dic)4.拿到字典中的報頭大小
total_size=header_dic['total_size']print(total_size)
struck功能輔助理解:所以客戶端剛開始接收4 大小是足夠把報頭接收完的。
#struck工鞥理解:importstruct#將整型轉(zhuǎn)成bytes
res=struct.pack('i',1232435436)print(res,len(res)) #res:為bytes類型,還是固定長度4(一般情況已經(jīng)可以包含很多)
#將bytes轉(zhuǎn)成整型
aa=struct.unpack('i',res)print(aa,aa[0],type(aa)) #
print(len(res)) #4 一般情況多數(shù)bytes 4個長度足夠了
"""結(jié)果
b'\xecxuI' 4
(1232435436,) 1232435436
4"""
總結(jié)
以上是生活随笔為你收集整理的python 套接字 struck_Python socket粘包问题(最终解决办法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle Blob保存方式,关于文件
- 下一篇: java 双等号(==) 与equals