python粘包的解决方案

 更新时间:2024年01月21日 10:34:33   作者:Az_plus  
粘包就是在数据传输过程中有多个数据包被粘连在一起被发送或接受,本文主要介绍了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'))

到此这篇关于python粘包的解决方案的文章就介绍到这了,更多相关python 粘包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python中关于集合的介绍与常规操作解析

    Python中关于集合的介绍与常规操作解析

    Python除了List、Tuple、Dict等常用数据类型外,还有一种数据类型叫做集合(set),集合的最大特点是:集合里边的元素是不可重复的并且集合内的元素还是无序的
    2021-09-09
  • Python编程pytorch深度卷积神经网络AlexNet详解

    Python编程pytorch深度卷积神经网络AlexNet详解

    AlexNet和LeNet的架构非常相似。这里我们提供了一个稍微精简版本的AlexNet,去除了当年需要两个小型GPU同时运算的设计特点
    2021-10-10
  • Python制作微信好友背景墙教程(附完整代码)

    Python制作微信好友背景墙教程(附完整代码)

    这篇文章主要介绍了Python制作微信好友背景墙教程(附完整代码),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • 详解Python用三种方式统计词频的方法

    详解Python用三种方式统计词频的方法

    这篇文章主要介绍了Python用三种方式统计词频,每种方法给大家介绍的非常详细,需要的朋友可以参考下
    2019-07-07
  • pytorch中节约显卡内存的方法和技巧

    pytorch中节约显卡内存的方法和技巧

    显存不足是很多人感到头疼的问题,毕竟能拥有大量显存的实验室还是少数,而现在的模型已经越跑越大,模型参数量和数据集也越来越大,所以这篇文章给大家总结了一些pytorch中节约显卡内存的方法和技巧,需要的朋友可以参考下
    2023-11-11
  • python3第三方爬虫库BeautifulSoup4安装教程

    python3第三方爬虫库BeautifulSoup4安装教程

    这篇文章主要为大家详细介绍了python3第三方爬虫库BeautifulSoup4的安装教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • Python Pandas 箱线图的实现

    Python Pandas 箱线图的实现

    这篇文章主要介绍了Python Pandas 箱线图的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Python中跳出循环的两种方法

    Python中跳出循环的两种方法

    在 Python 中,跳出循环通常指的是使用 break 语句来结束一个循环结构,比如 for 循环或 while 循环,当程序执行到 break 语句时,循环会立即终止,程序会继续执行循环后面的代码,本文给大家介绍了Python中跳出循环的两种方法,需要的朋友可以参考下
    2024-09-09
  • python爬取数据中的headers和代理IP问题分析

    python爬取数据中的headers和代理IP问题分析

    这篇文章主要为大家介绍了python爬取数据中的headers和代理IP问题分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • python virtualenv虚拟环境配置与使用教程详解

    python virtualenv虚拟环境配置与使用教程详解

    这篇文章主要介绍了python virtualenv虚拟环境配置与使用教程详解,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07

最新评论