黏包问题的成因与解决方案

黏包问题的成因与解决方案,第1张

概述一、黏包成因tcp协议的拆包机制当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度面向流的通信特点和Nagle算法TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。可靠黏包的tcp协议:tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。总结:黏包有两种:一种是因为发送数据包时,每次发送的包小,因为系统进行优化算法,就将两次的包放在一起发送,减少了资源的重复占用。多次发送会经历多次网络延迟,一起发送会减少网络延迟的次数。因此在发送小数据时会将两次数据一起发送,而客户端接收时,则会一并接收。#即出现多次send会出现黏包第二种是因为接收数据时,又多次接收,第一次接收的数据量小,导致数据还没接收完,就停下了,剩余的数据会缓存在内存中,然后等到下次接收时和下一波数据一起接收。二、黏包的解决方案1,问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。#_*_coding:utf-8_*_import socket,subprocessip_port=('127.0.0.1',8080)s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind(ip_port)s.listen(5)while True:conn,addr=s.accept()print('客户端',addr)while True:msg=conn.recv(1024)if not msg:breakres=subprocess.Popen(msg.decode('utf-8'),shell=True,stdin=subprocess.PIPE,stderr=subprocess.PIPE,stdout=subprocess.PIPE)err=res.stderr.read()if err:ret=errelse:ret=res.stdout.read()data_length=len(ret)conn.send(str(data_length).encode('utf-8'))data=conn.recv(1024).decode('utf-8')if data == 'recv_ready':conn.sendall(ret)conn.close()服务端server#_*_coding:utf-8_*_import socket,times=socket.socket(socket.AF_INET,socket.SOCK_STREAM)res=s.connect_ex(('127.0.0.1',8080))while True:msg=input('>>: ').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8'))length=int(s.recv(1024).decode('utf-8'))s.send('recv_ready'.encode('utf-8'))send_size=0recv_size=0data=b''while recv_size < length:data+=s.recv(1024)recv_size+=len(data)print(data.decode('utf-8'))客户端client2.使用time模块,在每次send的时候加入一个time.sleep(0.01),这种方法可以有效地隔开两次send,断开系统的优化,此种方法虽然可以解决黏包问题,但是会造成发送数据时间长import socketsk = socket.socket()sk.bind(('127.0.0.1',8090))sk.listen()conn,addr = sk.accept()ret1 = conn.recv(12)print(ret1)ret2 = conn.recv(12) #ret3 = conn.recv(12) #print(ret2)print(ret3)conn.close()sk.close()serverimport socketsk = socket.socket()sk.connect(('127.0.0.1',8090))sk.send(b'hello')import timetime.sleep(0.01)sk.send(b'egg')sk.close()client3,先读取文件的大小,然后将文件的大小发送给接收端,这样接收端就可以以文件大小来写入数据。import jsonimport socketimport structsk =socket.socket()#创建一个socket对象sk.bind(('127.0.0.1',8080))#绑定本地ip地址与端口sk.listen()#开启监听buffer =1024 #设置buffer值大小conn,addr =sk.accept()#等待客户端连接服务端,得到地址与双共工通道head_len=conn.recv(4)#接收用struck将数字转长度为4的byteshead_len =struct.unpack('i',head_len)[0]#调用struct模块来解包,得到原来的数字(数字为报头的长度)json_head =conn.recv(head_len).decode('utf-8')#接收json序列化的报头进行解码head =json.loads(json_head)#将json序列化的报头进行反序列化filesize =head['filesize']#拿到head字典中键filesize所对应的值print(filesize)#打印filesizewith open(r'dir%s'%head['filename'],'wb')as f:#dir文件名,拿到文件的路径,以wb模式打开while filesize:#当filesize(文件内剩余内容的大小)有值时if filesize >=buffer:#如果filesize>= buffer值,buffer值是设定的一次接收多少字节的内容print(filesize) #打印filesize大小content =conn.recv(buffer)#接收buffer值大小的内容f.write(content)#写入文件filesize -=buffer#原来的文件大小减去接收的内容,等于剩余文件的大小else:#如果文件剩余的内容大小<buffer设定的大小,就全部接收content =conn.recv(filesize)f.write(content)filesize =0print('=====>',len(content))print(filesize)print('服务器端')conn.close()sk.close()serverimport structimport osimport jsonimport socketsk =socket.socketsk.connect(('127.0.0.1',8090))buffer =1024head ={'filepath':r'D:DocumentsoCam','filename':r'test.mp4','filesize':None}#定义一个报头file_path =os.path.join(head['filepath'],head['filename'])#将文件名与文件路径加载进目录中filesize = os.path.getsize(file_path)#得到目录中文件的大小head['filesize'] =filesize#将文件大小赋值回列表中json_head =json.dumps(head)#将head字典序列化bytes_head =json_head.encode('utf-8')#将序列化之后的字典进行解码head_len =len(bytes_head)#计算转码之后字典的长度pack_len =struct.pack('i',head_len)#调用struct模块将长度转换成长度为4的bytes类型sk.send(pack_len)#发送pack_lensk.send(bytes_head)#发送bytes_headwith open(file_path,'rb')as f:while filesize:print(filesize)if filesize>=buffer:content =f.read(buffer)print('====>',len(content))sk.send(content)filesize-=bufferelse:content =f.read(filesize)sk.send(content)filesize=0sk.close()client   为什么会出现黏包问题?首先只有在TCP协议中才会出现黏包现象是因为TCP协议是面向流的协议在发送的数据传输的过程中海油缓存机制来避免数据丢失因为在连续发送小数据的时候、以及接收大小不符的时候都容易出现黏包现象本质还是因为我们在接收数据的时候不知道发送的数据的长短 解决黏包问题在传输大量数据之前先告诉数据量的大小。4,使用struct解决黏包 import socket,struct,jsonimport subprocessphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加phone.bind(('127.0.0.1',8080))phone.listen(5)while True:conn,addr=phone.accept()while Tr 一、黏包成因tcp协议的拆包机制面向流的通信特点和Nagle算法

总结:

黏包有两种:

二、黏包的解决方案

1,问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

=(,8080=1s.bind(ip_port)
s.Listen(
5<span >)

<span >while<span > True:
conn,addr=<span >s.accept()
<span >print(<span >'<span >客户端<span >'<span >,addr)
<span >while<span > True:
msg=conn.recv(1024<span >)
<span >if <span >not msg:<span >break<span >
res=subprocess.Popen(msg.decode(<span >'<span >utf-8<span >'),shell=<span >True,\
stdin=<span >subprocess.PIPE,\
stderr=<span >subprocess.PIPE,\
stdout=<span >subprocess.PIPE)
err=<span >res.stderr.read()
<span >if<span > err:
ret=<span >err
<span >else<span >:
ret=<span >res.stdout.read()
data_length=<span >len(ret)
conn.send(str(data_length).encode(<span >'<span >utf-8<span >'<span >))
data=conn.recv(1024).decode(<span >'<span >utf-8<span >'<span >)
<span >if data == <span >'<span >recv_ready<span >'<span >:
conn.sendall(ret)
conn.close()

服务端

==s.connect_ex((,8080<span >while<span > True:
msg
=input(<span >'
<span >>>:
<span >'
<span >).strip()
<span >if
len(msg) == 0:<span >continue

<span >if
msg == <span >'
<span >quit
<span >'
:<span >break
<span >

s.send(msg.encode(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;utf-8</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;))length</span>=int(s.recv(1024).decode(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;utf-8</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;))s.send(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;recv_ready</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span>.encode(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;utf-8</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;))send_size</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;0recv_size</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;0data</span>=b<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;''</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;while</span> recv_size <<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt; length:    data</span>+=s.recv(1024<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;)    recv_size</span>+=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;len(data)</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;print</span>(data.decode(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;utf-8</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;))

客户端

2.使用time模块,在每次send的时候加入一个time.sleep(0.01),这种方法可以有效地隔开两次send,断开系统的优化,此种方法虽然可以解决黏包问题,但是会造成发送数据时间长

=,8090conn,addr =<span > sk.accept()
ret1
= conn.recv(12<span >)
<span >print
<span >(ret1)
ret2
= conn.recv(12) <span >#
ret3 = conn.recv(12) <span >#
<span >print
<span >(ret2)
<span >print
<span >(ret3)
conn.close()
sk.close()

sk =<span > socket.socket()
sk.connect((
<span >'
<span >127.0.0.1
<span >'
,8090<span >))

sk.send(b<span >'<span >hello<span >'<span >)
<span >import<span > time
time.sleep(0.01<span >)
sk.send(b<span >'<span >egg<span >'<span >)

sk.close()

=socket.socket()sk.bind((,8080))sk.Listen()buffer =1024 conn,addr =sk.accept()head_len=conn.recv(4)head_len =struct.unpack(,head_len)[0]Json_head =conn.recv(head_len).decode()head =Json.loads(Json_head)filesize =head[](filesize)with open(r%head[],)as f: filesize: filesize >=buffer:= buffer值,buffer值是设定的一次接收多少字节的内容 (filesize) content =conn.recv(buffer) f.write(content) filesize -=buffer : content ==((=,8090=1024={:r:r:None}file_path =os.path.join(head[],head[])filesize = os.path.getsize(file_path)head[] =filesizeJson_head =Json.dumps(head)bytes_head =Json_head.encode()head_len =len(bytes_head)pack_len =struct.pack(,head_len)sk.send(pack_len)sk.send(bytes_head)with open(file_path, filesize>==(-===为什么会出现黏包问题?

首先只有在TCP协议中才会出现黏包现象

是因为TCP协议是面向流的协议

在发送的数据传输的过程中海油缓存机制来避免数据丢失

因为在连续发送小数据的时候、以及接收大小不符的时候都容易出现黏包现象

本质还是因为我们在接收数据的时候不知道发送的数据的长短

解决黏包问题

在传输大量数据之前先告诉数据量的大小。

4,使用struct解决黏包 

=1) ,8080phone.Listen(5<span >)

<span >while<span > True:
conn,addr=<span >phone.accept()
<span >while<span > True:
cmd=conn.recv(1024<span >)
<span >if <span >not cmd:<span >break
<span >print(<span >'<span >cmd: %s<span >' %<span >cmd)

    res</span>=subprocess.Popen(cmd.decode(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;utf-8</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;),shell</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;True,stdout</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;subprocess.PIPE,stderr</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;subprocess.PIPE)    err</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;res.stderr.read()    </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;print</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;(err)    </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;if</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt; err:        back_msg</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;err    </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;else</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;:        back_msg</span>=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;res.stdout.read()    conn.send(struct.pack(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;i</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span>,len(back_msg))) <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;#</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;先发back_msg的长度</span>    conn.sendall(back_msg) <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;#</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;在发真实的<a href="https://www.jb51.cc/tag/neirong/" target="_blank" >内容</a></span>

<span >
conn.close()

s=<span >socket.socket(socket.AF_INET,8080<span >))

<span >while<span > True:
msg=input(<span >'<span >>>: <span >'<span >).strip()
<span >if len(msg) == 0:<span >continue
<span >if msg == <span >'<span >quit<span >':<span >break<span >

s.send(msg.encode(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;utf-8</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;))l</span>=s.recv(4<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;)x</span>=struct.unpack(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;i</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;,l)[0]</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;print</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;(type(x),x)</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;#</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt; print(struct.unpack('I',l))</span>r_s=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;0data</span>=b<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;''</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;while</span> r_s <<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt; x:    r_d</span>=s.recv(1024<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;)    data</span>+=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;r_d    r_s</span>+=<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000"&gt;len(r_d)</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;#</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt; print(data.decode('utf-8'))</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff"&gt;print</span>(data.decode(<span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;gbk</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000"&gt;'</span>)) <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;#</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #008000"&gt;<a href="https://m.jb51.cc/tag/windows/" target="_blank" >windows</a><a href="https://www.jb51.cc/tag/mo/" target="_blank" >默</a>认gbk编码</span></pre>
总结

以上是内存溢出为你收集整理的黏包问题的成因与解决方案全部内容,希望文章能够帮你解决黏包问题的成因与解决方案所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1208002.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存