python之粘包/粘包的解决方案

python之粘包/粘包的解决方案

什么是粘包

粘包就是在数据传输过程中有多个数据包被粘连在一起被发送或接受

服务端:

import socket
import struct# 创建Socket
Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定服务器和端口号
servers_addr = ('127.0.0.1', 8081)
Socket.bind(servers_addr)# 监听客户端请求 最大连接数为5
Socket.listen(5)
print('服务器启动成功,等待客户端连接...')# 接受数据
client_socket, client_addr = Socket.accept()
print('与客户端建立连接', client_addr)
client_socket.setblocking(False)
# 数据交换
while True:data = client_socket.recv(10880)  # 最大1024字节if len(data) < 1:print('关闭服务')break# 接受客户器端传来的数据print(data.decode())# 向客户端返回数据client_socket.sendall(data)break
Socket.close()

客户端:

import socket
import subprocess# 获取cmd指令
cmd_from_client = 'ipconfig'
cmd_msg = subprocess.Popen(cmd_from_client,shell=True,  # 使用shell命令stdout=subprocess.PIPE,  # 管道一:输出结果stderr=subprocess.PIPE  # 管道二:输出错误信息)
msg_one = cmd_msg.stdout.read().decode('gbk')
msg_two = cmd_msg.stderr.read().decode('gbk')
msg = msg_one + msg_two# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 服务器地址和端口
server_address = ('localhost', 8081)# 连接服务器
client_socket.connect(server_address)
print('已连接到服务器:', server_address)while True:# 发送数据# message = input('>>>>')client_socket.sendall(msg.encode())# 接收响应response = client_socket.recv(1024)print('服务器响应:', response.decode())break
client_socket.close()

案例中使用了subprocess模块输出了ip信息,在服务端打印的数据中可以看到内容是能够正常输出的

image-20240120212503635但是根据客户端的控制台显示数据在返回时被截断了

其实原因很简单:

response = client_socket.recv(1024)

数据在服务端中能一次性的接收,但由于客户端只能接受1024,所以就不会从缓存中一下取完大于1024的那部分数据,其实不管是客户端还是服务端,recv()的缓存区大小都是可控的,但是发送方发送了一个 10KB 的数据包,而接收方使用 recv(1024) 只能一次接收最多 1KB 的数据,这样就需要多次调用 recv() 来接收完整的数据,可能会引发粘包问题

客户端
import socket# 创建 Socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 服务器地址和端口
server_address = ('localhost', 8081)# 连接服务器
client_socket.connect(server_address)
print('已连接到服务器:', server_address)# 发送数据包
message1 = 'Hello'
message2 = 'World'# 连续发送两个数据包
client_socket.sendall(message1.encode())
client_socket.sendall(message2.encode())# 关闭连接
client_socket.close()
服务端
import socket# 创建 Socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定服务器地址和端口
server_address = ('localhost', 8081)
server_socket.bind(server_address)# 监听客户端请求
server_socket.listen(1)
print('等待客户端连接...')while True:# 接受连接client_socket, client_addr = server_socket.accept()print('与客户端建立连接:', client_addr)# 接收数据data = client_socket.recv(1024)  # 接收数据包received_data = data.decode()# 处理接收到的数据print('接收到数据:', received_data)

理想情况:

等待客户端连接...
与客户端建立连接: ('127.0.0.1', 61127)
接收到数据: Hello
接收到数据: World

实际情况:

等待客户端连接...
与客户端建立连接: ('127.0.0.1', 61127)
接收到数据: HelloWorld

导致粘包的原因

1.缓冲区大小限制:在TCP传输中,由于数据过大,超出缓存区大小限制,导致接收方不能接收到所有的数据包,造成了数据包的截断或丢失

2.底层协议特性:底层传输协议如 TCP 是面向流的,不保留消息边界。TCP 协议会将数据流切分为适当大小的数据块进行传输,因此无法保证每个数据包的边界

3.数据发送速度过快:发送方连续发送数据包,而接收方无法及时处理,导致多个数据包在接收缓冲区中堆积

解决方案:struct模块

利用pack()方法将任意长度的 数字 打包成新的数据

再用unpack()方法将固定长度的 数字 解包成打包前数据真实的长度

  • pack()方法 第一个参数是格式,第二个参数是整数(数据的长度),返回值是一个新的数据
  • unpack()方法 第一个参数是格式,第二个参数是 pack()方法打包后生成的新数据,返回值是一个元组,元组中放着打包前数据真实的长度
import structmsg_one = '你好'
msg_two = ('struct 是 Python 标准库中的一个模块,用于进行字节与数据类型之间的相互转换。它提供了''一组函数来打包(pack)和解包(unpack)数据,使得数据在网络传输或文件存储时能够以二进制形式进行处理。')
total = len(msg_one) + len(msg_two)  # 106# 将数据打包
res = struct.pack('i', total)# 解包数据
un_res = struct.unpack('i', res)print(len(res))  # 4
print(res)  # bytes类型:  b'j\x00\x00\x00'
print(un_res)  # 元组类型: (106,)

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

客户端
import socket
import struct# 创建 Socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 服务器地址和端口
server_address = ('localhost', 8081)# 连接服务器
client_socket.connect(server_address)
print('已连接到服务器:', server_address)# 发送数据包
msg = b'helloworld'
data = struct.pack('i', len(msg))# 先发送报头
client_socket.send(data)# 发送真实数据
client_socket.send(msg)
服务端
import socket
import struct# 创建 Socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定服务器地址和端口
server_address = ('localhost', 8081)
server_socket.bind(server_address)# 监听客户端请求
server_socket.listen(1)
print('等待客户端连接...')while True:# 接受连接client_socket, client_addr = server_socket.accept()print('与客户端建立连接:', client_addr)# 接收数据data = client_socket.recv(1024)  # 接收数据包received_data = struct.unpack('i', data)data_len = received_data[0]real_data = client_socket.recv(data_len)# 处理接收到的数据print('接收到数据:', real_data.decode('utf8'))

根据该原理改进案例代码

客户端
import socket
import struct# 创建Socket
Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定服务器和端口号
servers_addr = ('127.0.0.1', 8082)
Socket.bind(servers_addr)# 监听客户端请求 最大连接数为5
Socket.listen(5)
print('服务器启动成功,等待客户端连接...')# 接受数据
client_socket, client_addr = Socket.accept()
print('与客户端建立连接', client_addr)
# client_socket.setblocking(False)
# 数据交换
while True:# 接受报头header = client_socket.recv(4)  # 最大1024字节if len(header) < 1:print('关闭服务')breakdata_len = struct.unpack('i', header)[0]print(data_len)# 接受真实数据real_data = client_socket.recv(data_len)print(real_data.decode('gbk'))# 向客户端返回数据client_socket.send(real_data)
服务端
import socket
import struct
import subprocess# 获取cmd指令
cmd_from_client = 'ipconfig'
cmd_msg = subprocess.Popen(cmd_from_client,shell=True,  # 使用shell命令stdout=subprocess.PIPE,  # 管道一:输出结果stderr=subprocess.PIPE  # 管道二:输出错误信息)
msg_one = cmd_msg.stdout.read().decode('gbk')
msg_two = cmd_msg.stderr.read().decode('gbk')
msg = msg_one + msg_two# 创建Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 服务器地址和端口
server_address = ('localhost', 8082)# 连接服务器
client_socket.connect(server_address)
print('已连接到服务器:', server_address)while True:# 先发报头data_len = struct.pack('i', len(msg))client_socket.send(data_len)# 发送数据client_socket.send(msg.encode('gbk'))# 接收响应response = client_socket.recv(data_len[0])print('服务器响应:', response.decode('gbk'))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/418430.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu系统pycharm以及annaconda的安装配置笔记以及问题集锦(更新中)

Ubuntu 22.04系统pycharm以及annaconda的安装配置笔记以及问题集锦 pycharm安装 安装完之后桌面上并没有生成图标 后面每次启动pycharm都要到它的安装路径下的bin文件夹下&#xff0c; cd Downloads/pycharm-2018.1.4/bin然后使用sh命令启动脚本程序来打开pycharm sh pycha…

【Linux的权限命令详解】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 shell命令以及运行原理 Linux权限的概念 Linux权限管理 一、什么是权限&#xff1f; 二、权限的本质 三、Linux中的用户 四、linux中文件的权限 4.1、文件访问…

Mybatis Plus baomidou EasyCode插件自动生成驼峰字段实体类,而不是全小写字段实体类

开发环境&#xff1a; springboot 2.4.3baomidou 3.4.0mybatis plus 3.4.0jdk8 问题描述&#xff1a; 1、mybatis 使用baomidou 插件&#xff0c;EasyCode自动生成实体类&#xff0c;但字段都是全部小写的&#xff0c;不太符合编码规范。 2、mysql表字段全是驼峰&#xff0c…

正则表达式第三四个作用:替换、切割

目录 方法二 replaceAll&#xff1a; 方法三&#xff1a;spilt&#xff1a; 方法一之前已经见过了&#xff1a; 方法二 replaceAll&#xff1a; 形参中&#xff1a; 参数regex表示一个正则表达式。可以将当前字符串中匹配regex正则表达式的字符串替换为newStr。 代码演示 S…

Windows给docker设置阿里源

windows环境搭建专栏&#x1f517;点击跳转 Windows系统的docker设置阿里源 文章目录 Windows系统的docker设置阿里源1.获得镜像加速器2.配置docker 由于我们生活在中国大陆&#xff0c;所以外网的访问总是那么慢又困难&#xff0c;用docker拉取几兆的小镜象还能忍受&#xff…

一步一步写线程之五线程池的模型之一领导者追随者模型

一、线程池的模型 在学习过相关的多线程知识后&#xff0c;从更抽象的角度来看待多线程解决问题的方向&#xff0c;其实仍然是典型的生产和消费者的模型。无论是数据计算、存储、分发和任务处理等都是通过多线程这种手段来解决生产者和消费者不匹配的情况。所以&#xff0c;得…

github经常登不上去怎么办?

问题 想少些代码&#xff0c;多学习&#xff0c;少不了使用github&#xff0c;但是在国内经常上不去&#xff0c;很耽误事&#xff0c;这里提供一个简单方法&#xff0c;供参考。 github GitHub是一个面向开源及私有软件项目的托管平台&#xff0c;可以让开发者共同协作开发软…

softmax回实战

1.数据集 MNIST数据集 (LeCun et al., 1998) 是图像分类中广泛使用的数据集之一&#xff0c;但作为基准数据集过于简单。 我们将使用类似但更复杂的Fashion-MNIST数据集 (Xiao et al., 2017)。 import torch import torchvision from torch.utils import data from torchvisi…

Web自动化测试 —— cookie复用

一、cookie简介 cookie是一些数据&#xff0c;存储于用户电脑的文本文件中 当web服务器想浏览器发送web页面时&#xff0c;在链接关闭后&#xff0c;服务端不会记录用户信息 二、为什么要使用Cookie自动化登录 复用浏览器仍然在每次用例开始都需要人为介入若用例需要经常执行&…

浅析性能测试策略及适用场景

前言 面对日益复杂的业务场景和不同的系统架构&#xff0c;前期的需求分析和准备工作&#xff0c;需要耗费很多的时间。而不同的测试策略&#xff0c;也对我们的测试结果是否符合预期目标至关重要。 这篇博客&#xff0c;聊聊我个人对常见的性能测试策略的理解&#xff0c;以…

Android Matrix绘制PaintDrawable设置BitmapShader,手指触点为圆心scale放大原图,Kotlin(二)

Android Matrix绘制PaintDrawable设置BitmapShader&#xff0c;手指触点为圆心scale放大原图&#xff0c;Kotlin&#xff08;二&#xff09; 在 Android Matrix绘制PaintDrawable设置BitmapShader&#xff0c;手指触点为圆心scale放大原图&#xff0c;Kotlin-CSDN博客 基础上&…

信息登记小程序怎么做_重塑用户互动,开启全新营销篇章

信息登记小程序&#xff1a;重塑用户互动&#xff0c;开启全新营销篇章 在数字化浪潮中&#xff0c;小程序以其便捷、高效的特点&#xff0c;逐渐成为企业与用户之间沟通的桥梁。其中&#xff0c;信息登记小程序更是凭借其独特的定位&#xff0c;在众多小程序中脱颖而出。本文…