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

news2024/11/28 10:32:55

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 struct

msg_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('关闭服务')
        break
    data_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.coloradmin.cn/o/1399254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java打包及上传到私服务

一、准备Maven私服Nexus 添加saas.maven 仓库地址&#xff1a;http://192.168.31.109:8081/repository/saas.maven 二、新建SpringBoot项目com.saas.pdf 添加类&#xff1a;PdfUtil.java package com.saas.pdf;public class PdfUtil {public static void Save(String fileP…

ubuntu20遇到缺少qt4相关库的问题

最近需要做套接字通讯的工作&#xff0c;最好是有一个网络调试软件能够接受或者发送套接字&#xff0c;测试代码能够正常通讯。windows下有很多&#xff0c;但是linux下比较少&#xff0c;使用广泛的是下面这一款。 1、安装 首先从网盘&#xff08;链接: https://pan.baidu.c…

安装conda搭建python环境(保姆级教程)

目录 一、Anaconda简介二、Anaconda安装 2.1 Anaconda下载2.2 Anaconda安装2.3 配置环境变量 三、通过conda配置python环境 3.1 创建并激活虚拟环境3.2 管理虚拟环境 一、Anaconda简介 Anaconda 是专门为了方便使用 Python 进行数据科学研究而建立的一组软件包&#xff0c;…

【设计模式-08】Flyweight享元模式

简要说明 简要的理解&#xff1a;享元模式就是新建一个池(Pool)&#xff0c;该池子(Pool)中有新建好的一堆对象&#xff0c;当需要使用时&#xff0c;从池子(Pool)中直接获取&#xff0c;不用重新新建一个对象。通俗的讲就是&#xff1a;共享元数据。 比如Java中的String就是使…

Python圣诞主题绘图:用turtle库打造冬日奇妙画面【第31篇—python:圣诞节】

文章目录 Python圣诞主题绘图导言代码结构概览详细解析drawlight函数tree函数xzs函数drawsnow函数五角星的绘制 完整代码代码解析总结 Python圣诞主题绘图 导言 圣诞季节是个充满欢乐和创意的时刻。在这个技术博客中&#xff0c;我们将深入探讨如何使用Python的turtle库创建一…

【华为 ICT HCIA eNSP 习题汇总】——题目集4

1、&#xff08;多选&#xff09;网络中出现故障后&#xff0c;管理员通过排查发现某台路由器的配置被修改了&#xff0c;那么管理员应该采取哪些措施来避免这种状况再次发生&#xff1f; A、管理员应该通过配置 ACL 来扩展只有管理员能够登录设备 B、管理员应该在路由的管理端…

Redis原理篇(QuickList)

一.前言&#xff1a;ZipList出现的问题 QuickList的出现是为了解决ZipList所存在的一些问题 1.寻找大内存块&#xff0c;申请内存效率低 ZipList所申请的是连续的内存空间&#xff0c;如果ZipList里面存放的数据过多&#xff0c;就需要一块很大的连续内存&#xff0c;系统需…

【c++笔记】用c++解决一系列质数问题!

质数是c语言和c中比较常见的数学问题&#xff0c;本篇文章将带你走进有关质数的一系列基础问题&#xff0c;其中包含常见的思路总结&#xff0c;本篇文章过后&#xff0c;将会持续更新c算法系列&#xff0c;感兴趣的话麻烦点个关注吧&#xff01; 希望能给您带来帮助&#xff…

新能源智慧充电桩方案:AI视频分析技术如何助力充电桩智能监管?

随着AI人工智能、大数据、云计算等技术快速发展与落地&#xff0c;视频智能分析技术在智慧充电桩场景中的应用也越来越广泛。这种技术能够为充电桩站点提供全方位的监控和管理&#xff0c;提高运营效率&#xff0c;保障充电桩设备的安全和稳定运行。 通过TSINGSEE青犀&触角…

实验五 PLSQL编程

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

Arduino开发实例-MTH02温湿度传感器驱动

MTH02温湿度传感器驱动 文章目录 MTH02温湿度传感器驱动1、MTH02温湿度传感器介绍2、硬件准备及接线3、代码实现1、MTH02温湿度传感器介绍 市场上的温湿度传感器在价格、精度和测量范围等方面种类繁多。 MTH02O 是这些传感器中最小的一种。 该传感器使用数字引脚传输温度和湿度…

领略指针之妙

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

mac-hadoop3.3.6 源码构建以及踩坑记录

1. 为什么需要构建源码 因为hadoop的可执行文件 是在专门的机器上编译的 其中native库 不一定能适用于每个机器 导致在启动hadoop过程中 出现烦人的警告 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes w…

探索图像检索:从理论到实战的应用

目录 一、引言二、图像检索技术概述图像检索的基本概念图像检索与文本检索的区别特征提取技术相似度计算索引技术 三、图像检索技术代码示例图像特征提取示例相似度计算索引技术 四、图像搜索流程架构数据采集与预处理特征提取相似度计算与排名结果呈现与优化 五、实际应用图像…

探索世界,从一款好用的浏览器开始!

好用的浏览器分享 在这个数字化的时代&#xff0c;浏览器已经成为了我们生活中不可或缺的工具。从浏览新闻、社交媒体到工作学习&#xff0c;我们几乎无时无刻不在与浏览器打交道。那么&#xff0c;如何选择一款好用的浏览器呢&#xff1f;今天&#xff0c;我就来为大家分享几…

git中合并分支时出现了代码冲突怎么办

目录 第一章、Git代码冲突介绍1.1&#xff09;什么是Git代码冲突①git merge命令介绍②代码冲突原因 1.2&#xff09;提示代码冲突的两种情况①本地不同分支的文件有差异时&#xff1a;②本地仓库和git远程仓库的文件有差异时&#xff1a; 1.3&#xff09;解决合并时的代码冲突…

linux sudo指令提权

sudo指令 sudo 是在linux中用于以超级用户&#xff08;root&#xff09;权限执行命令的命令。它允许普通用户在执行特定命令时提升其权限&#xff0c;以完成需要超级用户权限的任务。sudo 的名称是 "superuser do" 的缩写。 格式 接受权限的用户登陆的主机 &#xff…

【LLM问答】两阶段的对话式问答模型ChatQA思路和兼看两阶段的RAG知识问答引擎QAnything

一、ChatQA 1.1 微调 如下图&#xff0c;ChatQA微调包含两个阶段&#xff0c;Supervised Fine-tuning和Context-Enhanced Instruction Tuning 1.1.1 阶段一&#xff1a;SFT&#xff08;Supervised Fine-tuning&#xff09; 这个阶段的目标是基于LLM&#xff0c;通过SFT&…

虚拟歌姬学习:DiffSinger,让GitHub下载快的方法!

《三分钟上手DiffSinger》系列 ——基础篇https://www.bilibili.com/video/BV1ug4y1S7Dk/?spm_id_from333.337.search-card.all.click&vd_source124076d7d88eee393a1d8bf6fc787efa 下载DiffSinger 建议用edge浏览器还有steam&#xff0c;有时只是慢&#xff0c;但是还是…

Harmony Ble蓝牙App(四)描述符

Harmony Ble蓝牙App&#xff08;四&#xff09;描述符 前言正文一、优化二、描述① 概念② 描述提供者③ 显示描述符 三、源码 前言 上一篇中了解了特性和属性&#xff0c;同时显示设备蓝牙服务下的特性和属性&#xff0c;本文中就需要来使用这些特性和属性来完成一些功能。 正…