Python:粘包问题
粘包現(xiàn)象
1 、tcp有粘包及udp無粘包
- TCP 是面向連接的,面向流的可靠協(xié)議;發(fā)送端為了將多個發(fā)往接收端的包,更有效的發(fā)到對方,使用了優(yōu)化方法(Nagle算法),將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù),
合并成一個大的數(shù)據(jù)塊,然后進行封包。這樣,接收端,就難于分辨出來了,必須提供科學(xué)的拆包機制面向流的通信是無消息保護邊界的。 - UDP(用戶數(shù)據(jù)報協(xié)議)是無連接的,面向消息的,提供高效率服務(wù)。不會使用塊的合并優(yōu)化算法,, 由于UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩沖區(qū))采用了鏈?zhǔn)浇Y(jié)構(gòu)來
記錄每一個到達的UDP包,在每個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對于接收端來說,就容易進行區(qū)分處理了。 即面向消息的通信是有消息保護邊界的。
注:tcp是基于數(shù)據(jù)流的,于是收發(fā)的消息不能為空,這就需要在客戶端和服務(wù)端都添加空消息的處理機制,防止程序卡住,
而udp是基于數(shù)據(jù)報的,即便是你輸入的是空內(nèi)容(直接回車),那也不是空消息,udp協(xié)議會幫你封裝上消息頭
2 、產(chǎn)生原因:
1、接收端不知道消息的界限,不知道一次提取多少字節(jié)數(shù)據(jù)
2、TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一個TCP段。若連續(xù)幾次需要send的數(shù)據(jù)都很少,
通常TCP會根據(jù)優(yōu)化算法(Nagle算法)把這些數(shù)據(jù)合成一個TCP段后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)
產(chǎn)生粘包場景:(1)發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包(發(fā)送數(shù)據(jù)時間間隔很短,數(shù)據(jù)了很小,會合到一起,產(chǎn)生粘包)
(2)接收方不及時接收緩沖區(qū)的包,造成多個包接收(客戶端發(fā)送了一段數(shù)據(jù),服務(wù)端只收了一小部分,服務(wù)端下次再收的時候還是從緩沖區(qū)拿上次遺留的數(shù)據(jù),產(chǎn)生粘包)
3 、解決方案
為字節(jié)流加上自定義固定長度報頭,報頭中包含字節(jié)流長度,然后一次send到對端,對端在接收時,先從緩存中取出定長的報頭,然后再取真實數(shù)據(jù)
struct 模塊
服務(wù)端:
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' import subprocess import socket import struct import json phone= socket.socket(socket.AF_INET ,socket.SOCK_STREAM ) phone.bind(('127.0.0.1',8080)) phone.listen(5) while True :conn,client=phone.accept()while True :try:cmd = conn.recv(1024)if len(cmd) == 0: break# 遠程執(zhí)行命令obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, # 解碼stdout=subprocess.PIPE, # 正確信息stderr=subprocess.PIPE # 錯誤信息)stdout = obj.stdout.read()stderr = obj.stderr.read()#先制作報頭head_dic= {'filename':'a.txt','total_size':len(stdout)+len(stderr),'hash':'asdf165485221'}head_json = json.dumps(head_dic)head_bytes= head_json.encode('utf-8')#1、先把報頭的長度打包成四個bytes,然后發(fā)送conn.send(struct.pack('i',len(head_bytes)))#2、發(fā)送報頭conn.send(head_bytes)#3、發(fā)送真實數(shù)據(jù)conn.send(stdout )conn.send(stderr)except ConnectionResetError:breakconn.close()phone.close()客戶端:
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:579817333 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' import struct import socket import json phone= socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone. connect(('127.0.0.1',8080)) while True :msg = input('<<<')if msg == 0:continue#phone.send(msg.encode('utf-8'))phone.send(bytes(msg,encoding='utf-8'))#1、先收4個字節(jié),該4個字節(jié)包含報頭的長度 解包header_len=struct .unpack('i',phone.recv(4))[0]#2、通過報頭長度,再接受報頭內(nèi)容header_bytes=phone.recv(header_len) #通過報頭長度,拿到bytes內(nèi)容#從報頭中解析出想要的內(nèi)容header_json=header_bytes .decode('utf-8') #報頭內(nèi)容解碼得到字符串類型header_dic=json .loads(header_json) #反序列化得到字典print(header_dic)total_size = header_dic['total_size']#3、再收真實的數(shù)據(jù)recv_size =0 #初始值長度res=b'' #接收的具體值while recv_size< total_size:data= phone.recv(1024)res+=data # 拼接具體的值recv_size += len(data) #累加長度print(res.decode('gbk')) #收到的信息用GBK解碼 phone.close()總結(jié)
以上是生活随笔為你收集整理的Python:粘包问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python语言防坑小技巧
- 下一篇: Python :给类或者类的对象添加打印