《Python》网络编程之黏包
黏包
一、黏包现象
同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。
server端
import socket sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen() conn, addr = sk.accept()
conn.send(b'hello,')
conn.send(b'world')
conn.close()
client端
import socket sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) ret1 = sk.recv(1024)
print(ret1, len(ret1)) # b'hello,world' 11
ret2 = sk.recv(1024)
print(ret2, len(ret2)) # b'' 0
sk.close()
注意:只有TCP有粘包现象,UDP永远不会粘包
二、黏包成因
1、合包现象:
数据很短
时间间隔短
2、拆包现象:
大数据会发生拆分
不会一次性的全部发送到对方
对方在接收的时候很可能没用办法一次性接收到所有的信息
那么没有接收完的信息很可能和后面的信息黏在一起
3、黏包现象只发生在tcp协议
tcp协议的传输是:流式传输
每一条信息与信息之间是没有边界
4、udp协议中是不会发生黏包现象的
适合短数据的发送
不建议发送过长的数据
会增大数据丢失的几率
会发生黏包的两种情况:
1、发送方的缓存机制
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
2、接收方的缓存机制
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
总结:
黏包现象只发生在tcp协议中:
1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
三、黏包的解决方案
1、解决方案:
问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

server端:
import socket sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen() conn, addr = sk.accept()
inp = input('>>>')
s = str(len(inp)).zfill(4) + inp # 在输入的内容前面拼上它的长度(4位数,不足在前面用0补齐)
conn.send(s.encode('utf-8')) conn.close()
'''
两个弊端:
复杂
最多也只能一次性传递9999个字节
'''
client端:
import socket sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) ret1 = sk.recv(4) # 先接收前面的长度
num1 = int(ret1.decode('utf-8'))
ret = sk.recv(num1) # 再以长度接收
print(ret.decode('utf-8')) sk.close()
存在的问题:
程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗
2、解决方案进阶
我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。
struct模块:
该模块可以把一个类型,如数字,转成固定长度的bytes
print(struct.pack('i',1111111111111))
struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。
| 发送时 | 接收时 |
| 先发送struct转换好的数据长度4字节 | 先接受4个字节使用struct转换成数字来获取要接收的数据长度 |
| 再发送数据 | 再按照长度接收数据 |
server端:
import struct
import socket sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen() conn, addr = sk.accept()
while 1:
s = input('>>>').encode('utf-8')
pack_num = struct.pack('i', len(s))
conn.send(pack_num)
conn.send(s)
conn.close()
client端:
import socket
import struct sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) while 1:
pack_num = sk.recv(4)
num = struct.unpack('i', pack_num)[0]
ret = sk.recv(num)
print(ret.decode('utf-8'))
sk.close()
《Python》网络编程之黏包的更多相关文章
- Python学习笔记【第十四篇】:Python网络编程二黏包问题、socketserver、验证合法性
TCP/IP网络通讯粘包问题 案例:模拟执行shell命令,服务器返回相应的类容.发送指令的客户端容错率暂无考虑,按照正确的指令发送即可. 服务端代码 # -*- coding: utf- -*- # ...
- Python网络编程之黏包问题
二.解决黏包问题 2.1 解决黏包方法1 计算消息实体的大小 服务端接受两次,一次时消息大小,二次是消息实体,解决消息实体黏包 客户端发送两次,一次是消息大小,一次是消息实体 在两次收发之间加入一次多 ...
- python socket编程和黏包问题
一.基于TCP的socket tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端,有顺序,不重复,可靠.不会被加上数据边界. server端 import socket sk = so ...
- 网络编程- 解决黏包现象方案二之struct模块(七)
上面利用struct模块与方案一比较,减少一次发送和接收请求,因为方案一无法知道client端发送内容的长度到底有多长需要和接收OK.多一次请求防止黏包,减少网络延迟
- Python网络编程,粘包、分包问题的解决
tcp编程中的粘包.分包问题的解决: 参考:https://blog.csdn.net/yannanxiu/article/details/52096465 服务端: #!/bin/env pytho ...
- python网络编程-socket“粘包”(小数据发送问题)
一:什么是粘包 “粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了.或者数据等待超时了,数据才会 ...
- python网络编程之粘包
一.什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包 粘包不一定会发生 如果发生了:1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了 首先需要掌握一个socket收发消息的原 ...
- Python网络编程与并发编程
网络编程基础 黏包 , 并发 计算机网络的发展及基础网络概念 Python 中的进程与 锁 Python IO 多路复用 \协程
- Python之路 - 网络编程之粘包
Python之路 - 网络编程之粘包 粘包
随机推荐
- js判断手指的上滑,下滑,左滑,右滑,事件监听
原理:1:当开始一个touchstart事件的时候,获取此刻手指的横坐标startX和staerY: 2:当触发touchmove事件的时候,再获取此时手指的横坐标moveEndX和纵坐标moveEn ...
- traceback模块——获取详细的异常信息
try: 1/0 except Exception,e: print e 输出结果是integer division or modulo by zero,只知道是报了这个错,但是却不知道在哪个文件哪个 ...
- PHP中如何命令行
PHP中如何命令行 一.总结 一句话总结:配置php系统环境,然后命令行中运行 php -f 文件名即可 配置php系统环境 php_-f_文件名 例如: 1.三种运行php的方式? 运行文件_-f ...
- thinkphp5的Auth权限认证实战
thinkphp5的Auth权限认证实战 一.总结 一句话总结:基于角色的权限管理(真正做一遍,就会发现很简单,不然一直都是半懂不懂的) 角色 权限 真正做一遍,就会发现很简单,不然一直都是半懂不懂的 ...
- feign三:覆写feign的默认配置及feign的日志
feign三:覆写feign的默认配置及feign的日志 默认配置复写 本项目地址:http://192.168.1.103:7601 本例是通过feign调用 eureka项目中的/eureka/a ...
- python hashable
判断一个对象是否hashable: hash(obj) 或 obj.__hash__() ,返回 hash 值 hashable 的有: int / float / tuple / str/ obj ...
- vmware 10.0 安装centos6.5 客户系统 几个问题
1. vmware 10.0 安装centos6.5 客户系统 无法修改分辨率 要安装 desktop, KDE, legacy,x 组件 2. NAT 方式网卡无法自行启动 vim /etc/sy ...
- jquery中的全选、反选、全不选和单删、批删
HTML页面 <!doctype html><html lang="en"><head> <meta charset="UTF- ...
- HDU-2586-裸LCA入门-tarjan离线
http://acm.hdu.edu.cn/showproblem.php?pid=2586 给出一颗树和边权,询问两点距离. 考虑tarjan离线做法,做法很巧妙,当前进行到u,对他的儿子v,当v子 ...
- WDA基础十四:ALV字段属性配置表
ALV配置表管理 一.字段属性配置表 对于可编辑的ALV不用这个,尽可能多的设置一些控制: 单元格类型:默认A,特殊选择 ZLYE_TYPE E A 1 ...
