Python之网络编程(粘包、粘包解决方案)
文章目录
- tcp粘包
- 第一种粘包
- 第二种粘包
- udp粘包
- 解决粘包现象
粘包现象是指发送方发送的若干数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。粘包现象只会在tcp中出现,udp中不会有,因为udp是基于包来传输信息的,就一个sendto()对应另一个recvfrom()
tcp粘包
第一种粘包
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据也很小,会合到一起发送,产生粘包)
服务端测试代码:
from socket import *
import subprocessip_port=('192.168.43.247',8080) #记录ip地址
back_log = 5 #端口数为5
buffer_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 #端口数为5
buffer_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 #端口数为5
buffer_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 #端口数为5
buffer_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之网络编程(粘包、粘包解决方案)相关推荐
- 健壮的网络编程IO函数-RIO包
RIO包 简介 Rio包即为Robust io函数包.包中函数是对Linux基本I/O函数的封装,使其更加健壮.高效,更适用于网络编程. 分析 Rio包由rio_t结构体和系列函数组成. 首先是两个不 ...
- Python高级网络编程系列之第一篇
在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...
- Python Socket网络编程(一)初识Socket和Socket初步使用
目录 前言 网络编程 实质 IP地址和端口 数据传输协议 协议 Socket 概念 套接字 socket对象方法 初步使用 功能 源码 运行结果 结语 前言 本系列博客是笔者学习Python Sock ...
- 用 Python 写网络编程(四)
本文首发于TesterHome社区,作者是资深游戏测试开发工程师陈子昂.用 Python 写网络编程共四篇,今天分享的是第四篇.原文链接:https://testerhome.com/topics/2 ...
- Python之网络编程(TCP套接字与UDP套接字)
文章目录 基于tcp的套接字 实现目标 tcp服务端源码 tcp客户端源码 tcp效果实现 基于udp的套接字 udp作用介绍 udp服务端源码 udp客户端源码 udp效果实现 用udp实现一个时间 ...
- Python Socket网络编程(二)局域网内和局域网与广域网的持续通信
目录 前言 IP地址 简介 公有IP 私有IP 局域网之间网络通信 前提 功能描述 源码 运行结果 局域网与广域网网络通信 前提 源码 结语 前言 本系列博客是笔者学习Python Socket的过程 ...
- python recv_python网络编程调用recv函数完整接收数据的三种方法
最近在使用python进行网络编程开发一个通用的tcpclient测试小工具.在使用socket进行网络编程中,如何判定对端发送一条报文是否接收完成,是进行socket网络开发必须要考虑的一个问题.这 ...
- 0x011.Python学习-网络编程、PortScan
Python3 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
- Python 异步网络编程实战
Python 异步网络编程实战 - songcser - 掘金小册 小册介绍 第一部分是对 Python 协程的讲解,从字节码开始简单讲解了 Python 虚拟机的执行过程,可以大体了解到 Pytho ...
- linux网络编程 no route to host 解决方案
linux网络编程 no route to host 解决方案 [整合资料] (2013-05-13 21:38:12) 转载▼ 标签: net iptables it 分类: Linux 参考资料 ...
最新文章
- 颜水成团队开源VOLO:无需额外数据,首次在ImageNet上达到87.1%的精度
- metasploit framework
- 邊做邊學 Internet Explorer 8:瞭解 IE8 相容性技術
- 音视频技术开发周刊 | 198
- Android之HttpClient 和HttpResponse 小结
- 社招 | 腾讯天天P图 定义视频新科技~base上海
- 项目中最困难的部分_微服务最难的部分是什么? 您的资料
- pytorch:admm
- 使用视图组件为ASP.NET Core创建侧面菜单
- java jquery ajax_[Java教程]jquery ajax 使用
- 好了好久时间,终于写成了第一个Python代码
- Matlab连接字符串的方法
- The NVIDIA driver on your system is too old (found version 9000).
- 计算机延时关机小程序,电脑自动关机小程序
- 使用阿里巴巴EasyExcel导出的excel打不开(无法打开文件)
- 负载均衡设备oracle,高可用的Oracle数据库负载均衡技术--深信服AD系列应用交付平台...
- 提高数据存储效率的七个技巧
- 2021寒假每日一题《数独检查》
- 外部联接(Outer Join)和笛卡尔积(Cartesian Product)
- ps——油漆字体效果
热门文章
- 用python编写一个求偶数阶乘的函数_一行Python代码写阶乘函数
- 富文本编辑vue-quill-editor文件上传
- 动态添加表格点击事件
- 基于android的高仿抖音,Android仿抖音列表效果
- c语言leg 10,Leg massaging device
- asp.net oracle连接数据库,ASP.NET连接Oracle数据库的步骤详解
- phantomjs debian不显示中文_Python 爬虫:Seleniumamp;PhantomJS 实例(一)
- mysql 函数事务_MySQL:函数和事务
- python socket模块 和pyqt_使用PyQt和Socket进行聊天编程[标准库]
- vm虚拟机win10无法复制文件_远程桌面无法复制粘贴传输文件解决办法