失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Python之网络编程(粘包 粘包解决方案)

Python之网络编程(粘包 粘包解决方案)

时间:2020-04-18 02:36:41

相关推荐

Python之网络编程(粘包 粘包解决方案)

文章目录

tcp粘包第一种粘包第二种粘包udp粘包解决粘包现象

粘包现象是指发送方发送的若干数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。粘包现象只会在tcp中出现,udp中不会有,因为udp是基于包来传输信息的,就一个sendto()对应另一个recvfrom()

tcp粘包

第一种粘包

发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据也很小,会合到一起发送,产生粘包)

服务端测试代码:

from socket import *import subprocessip_port=('192.168.43.247',8080) #记录ip地址back_log = 5 #端口数为5buffer_size = 1024 #表示可以发送消息的大小为1024字节tcp_server= socket(AF_INET,SOCK_STREAM) #建立socket对象tcp_server.bind(ip_port) #绑定ip地址tcp_server.listen(back_log) #监听端口conn, addr= tcp_server.accept() #获取客户端链接#第一种粘包,能收的量太大了#会出现第一种粘包现象,因为可以收的数据远大于真实传来的数据,#tcp内的优化算法会把几个短的数据合成一个数据来发送#接收消息data1 = conn.recv(buffer_size) #都能收1024字节print('第一个消息:',data1.decode('utf-8'))data2 = conn.recv(buffer_size) print('第二个消息:',data2.decode('utf-8'))data3 = conn.recv(buffer_size)print('第三个消息:',data3.decode('utf-8'))conn.close() #关闭链接

客户端测试代码:

可以看到是一个简单的发送3条消息

from socket import *import subprocessip_port=('192.168.43.247',8080) #记录ip地址back_log = 5 #端口数为5buffer_size = 1024 #表示可以发送消息的大小为1024字节tcp_client= socket(AF_INET,SOCK_STREAM) #建立socket对象tcp_client.connect(ip_port) #连接服务端的地址#发送消息tcp_client.send('hello'.encode('utf-8'))tcp_client.send('world'.encode('utf-8'))tcp_client.send('view'.encode('utf-8'))

运行结果1:

可以看到,在客户端明明是发送了3次的三条消息合并成一条了

最简单的解决方案就是控制好接收消息的量,将其调小一点,也就是把服务端中的接收消息部分改成如下:

#解决方案,控制好能收的数据量data1 = conn.recv(5)print('第一个消息:',data1.decode('utf-8'))data2 = conn.recv(5) print('第二个消息:',data2.decode('utf-8'))data3 = conn.recv(5)print('第三个消息:',data3.decode('utf-8'))

运行结果2:

这样就正常得分成3条消息发送了

第二种粘包

接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再接收的时候还是从缓冲区拿上次遗留的数据,产生粘包现象)

测试代码与上文相同,只是将服务端中的接收消息部分改成如下:

#第二种粘包,能收的量太小了#出现第二种粘包现象,因为可以收的数据小于真实传来的数据,#所以tcp内部的优化算法会把上一段消息中没收完的部分,放到下一段消息里面data1 = conn.recv(1) #只能收1个字节print('第一个消息:',data1.decode('utf-8'))data2 = conn.recv(5) print('第二个消息:',data2.decode('utf-8'))data3 = conn.recv(1)print('第三个消息:',data3.decode('utf-8'))

运行结果3:

可以看到,上一次没有发送完的消息,会留到下一次发送的后面接着发送。也就是hello中第一次只接收了h,第二次接收5个字节,所以接着后面的ellow,最后再接收1个字节就是o

udp粘包

udp没有那种将短消息合成一条消息的优化算法,所以另外两条消息没有服务端接收,就直接丢失了;

所以udp不会出现粘包现象,因为udp发消息不管有没有人收,都能发出去,没人收的话消息就直接丢失了

也就是说,udp中一个sendto()对应一个recvfrom()

服务端测试代码:

from socket import *ip_port = ('192.168.43.247',8080)buffer_size = 1024#创建对象udp_server = socket(AF_INET,SOCK_DGRAM) #数据报,即udp协议udp_server.bind(ip_port)#接收消息data1,addr = udp_server.recvfrom(1024)print('第一个消息:',data1.decode('utf-8'))

客户端测试代码:

from socket import *ip_port = ('192.168.43.247',8080)buffer_size = 1024#创建对象udp_client = socket(AF_INET,SOCK_DGRAM) #数据报,即udp协议#发送消息udp_client.sendto('hello'.encode('utf-8'),ip_port)udp_client.sendto('world'.encode('utf-8'),ip_port)udp_client.sendto('view'.encode('utf-8'),ip_port)

运行结果:

可以看到,客户端发送的3条消息都非常短,但是服务端并没有将3条都合并成一条接收,而是严格地只接收了第一条消息hello

解决粘包现象

(1)问题的根源就是在于,接收端不知道发送端将要传送的字节流的长度;

(2)所以解决粘包就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知道;

(3)然后接收端来一个死循环接收完所有的数据

服务端测试代码:

from socket import *import subprocessip_port=('192.168.43.247',8080) #记录ip地址back_log = 5 #端口数为5buffer_size = 1024 #表示可以发送消息的大小为1024字节tcp_server= socket(AF_INET,SOCK_STREAM) #建立socket对象tcp_server.bind(ip_port) #绑定ip地址tcp_server.listen(back_log) #监听端口conn, addr= tcp_server.accept() #获取客户端链接,赋到conn中while True: #循环收发消息#解决粘包length = conn.recv(buffer_size) #接收对方消息的长度conn.send(b'ready')length = int(length.decode('utf-8')) #将长度解码,并转成数字型recv_size=0 #记录长度recv_msg=b'' #存放收到的数据while recv_size < length: #分多次收,如果长度小于数据长度就一直收r_m = conn.recv(buffer_size) #接收1024字节的消息recv_msg += r_m #将消息内容追加到存放数据的变量中recv_size += len(r_m) #再动态更新一下已收到的数据长度print('客户端发来的消息是:',recv_msg)conn.close() #关闭链接

客户端测试代码:

from socket import *import subprocessip_port=('192.168.43.247',8080) #记录ip地址back_log = 5 #端口数为5buffer_size = 1024 #表示可以发送消息的大小为1024字节tcp_client= socket(AF_INET,SOCK_STREAM) #建立socket对象tcp_client.connect(ip_port) #连接服务端的地址while True:#解决粘包msg = input('请输入您的消息:') #输入内容if not msg:continue #如果是空就回到循环if msg == 'quit':break #如果是quit就结束循环length = str(len(msg)) #统计消息的长度,因为发送的消息只能是字符串,所以要strtcp_client.send(length.encode('utf-8')) #将长度发送给服务端#接收服务端发来的指令,是否成功收到消息的长度信息server_ready = tcp_client.recv(buffer_size)#发送消息if server_ready == b'ready': #如果服务端返回一个二进制的'ready',则标明服务端收到了长度tcp_client.send(msg.encode('utf-8')) #则发送消息tcp_cliend.close()

运行结果:

可以看到,无论客户端发来多少消息,服务端都能全部接收,不会接收多了和接收少了,也就是没有粘包现象了

如果觉得《Python之网络编程(粘包 粘包解决方案)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。