文章目录

  • 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之网络编程(粘包、粘包解决方案)相关推荐

  1. 健壮的网络编程IO函数-RIO包

    RIO包 简介 Rio包即为Robust io函数包.包中函数是对Linux基本I/O函数的封装,使其更加健壮.高效,更适用于网络编程. 分析 Rio包由rio_t结构体和系列函数组成. 首先是两个不 ...

  2. Python高级网络编程系列之第一篇

    在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...

  3. Python Socket网络编程(一)初识Socket和Socket初步使用

    目录 前言 网络编程 实质 IP地址和端口 数据传输协议 协议 Socket 概念 套接字 socket对象方法 初步使用 功能 源码 运行结果 结语 前言 本系列博客是笔者学习Python Sock ...

  4. 用 Python 写网络编程(四)

    本文首发于TesterHome社区,作者是资深游戏测试开发工程师陈子昂.用 Python 写网络编程共四篇,今天分享的是第四篇.原文链接:https://testerhome.com/topics/2 ...

  5. Python之网络编程(TCP套接字与UDP套接字)

    文章目录 基于tcp的套接字 实现目标 tcp服务端源码 tcp客户端源码 tcp效果实现 基于udp的套接字 udp作用介绍 udp服务端源码 udp客户端源码 udp效果实现 用udp实现一个时间 ...

  6. Python Socket网络编程(二)局域网内和局域网与广域网的持续通信

    目录 前言 IP地址 简介 公有IP 私有IP 局域网之间网络通信 前提 功能描述 源码 运行结果 局域网与广域网网络通信 前提 源码 结语 前言 本系列博客是笔者学习Python Socket的过程 ...

  7. python recv_python网络编程调用recv函数完整接收数据的三种方法

    最近在使用python进行网络编程开发一个通用的tcpclient测试小工具.在使用socket进行网络编程中,如何判定对端发送一条报文是否接收完成,是进行socket网络开发必须要考虑的一个问题.这 ...

  8. 0x011.Python学习-网络编程、PortScan

    Python3 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...

  9. Python 异步网络编程实战

    Python 异步网络编程实战 - songcser - 掘金小册 小册介绍 第一部分是对 Python 协程的讲解,从字节码开始简单讲解了 Python 虚拟机的执行过程,可以大体了解到 Pytho ...

  10. linux网络编程 no route to host 解决方案

    linux网络编程 no route to host 解决方案 [整合资料] (2013-05-13 21:38:12) 转载▼ 标签: net iptables it 分类: Linux 参考资料 ...

最新文章

  1. 颜水成团队开源VOLO:无需额外数据,首次在ImageNet上达到87.1%的精度
  2. metasploit framework
  3. 邊做邊學 Internet Explorer 8:瞭解 IE8 相容性技術
  4. 音视频技术开发周刊 | 198
  5. Android之HttpClient 和HttpResponse 小结
  6. 社招 | 腾讯天天P图 定义视频新科技~base上海
  7. 项目中最困难的部分_微服务最难的部分是什么? 您的资料
  8. pytorch:admm
  9. 使用视图组件为ASP.NET Core创建侧面菜单
  10. java jquery ajax_[Java教程]jquery ajax 使用
  11. 好了好久时间,终于写成了第一个Python代码
  12. Matlab连接字符串的方法
  13. The NVIDIA driver on your system is too old (found version 9000).
  14. 计算机延时关机小程序,电脑自动关机小程序
  15. 使用阿里巴巴EasyExcel导出的excel打不开(无法打开文件)
  16. 负载均衡设备oracle,高可用的Oracle数据库负载均衡技术--深信服AD系列应用交付平台...
  17. 提高数据存储效率的七个技巧
  18. 2021寒假每日一题《数独检查》
  19. 外部联接(Outer Join)和笛卡尔积(Cartesian Product)
  20. ps——油漆字体效果

热门文章

  1. 用python编写一个求偶数阶乘的函数_一行Python代码写阶乘函数
  2. 富文本编辑vue-quill-editor文件上传
  3. 动态添加表格点击事件
  4. 基于android的高仿抖音,Android仿抖音列表效果
  5. c语言leg 10,Leg massaging device
  6. asp.net oracle连接数据库,ASP.NET连接Oracle数据库的步骤详解
  7. phantomjs debian不显示中文_Python 爬虫:Seleniumamp;PhantomJS 实例(一)
  8. mysql 函数事务_MySQL:函数和事务
  9. python socket模块 和pyqt_使用PyQt和Socket进行聊天编程[标准库]
  10. vm虚拟机win10无法复制文件_远程桌面无法复制粘贴传输文件解决办法