解决粘包问题

Posted randysun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决粘包问题相关的知识,希望对你有一定的参考价值。

解决粘包问题

一、解决粘包问题方式一

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

1.1 服务器

import socket
import subprocess
import struct

soc = socket.socket()
soc.bind(('127.0.0.1', 8001))
soc.listen(4)
while True:
    print("等待客户端连接")
    conn, addr = soc.accept()
    print("有个客户端连接上:", addr)

    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:
                break
            print(data)
            obj = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            # 执行结果为b格式, gbk编码(windows平台)
            msg = obj.stdout.read()

            """
            发送的时候需要把长度计算出来
            头必须是固定的长度
            选取出要发送数据的长度
            """
            leng = len(msg)
            # head是固定的四个字节
            head = struct.pack('i', leng)

            # 发送头
            conn.send(head)
            # 发送内容
            conn.send(msg)


        except Exception as e:
            print(e)
            break
    conn.close()

soc.close()

1.2 客户端

import socket
import struct

soc = socket.socket()

soc.connect(('127.0.0.1', 8001))

while True:
    in_s = input("请输入要执行命令:")
    soc.send(in_s.encode('utf-8'))
    head = soc.recv(4)
    lengs = struct.unpack('i', head)[0]

    count = 0
    data_total = b""
    while count < lengs:
        if lengs < 1024:
            # 如果接收的数据小于1024,直接接收数据的大小
            data = soc.recv(lengs)
        else:
            # 如果接收的数据大于1024
            if lengs - count > 1024:
                # 总数据长度减去count(目前收到多少, count就是多少), 如果还大于1024,在收1024

                data = soc.recv(1024)
            else:
                # 总数据长度减去count(目前收到多少,count就是多少), 如果小于1024, 只收剩下的部分即可
                data = soc.recv(lengs- count)

        data_total += data
        count += len(data)

    print(data_total.decode('gbk'))

缺点:

程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

二、补充struct模块

2.1 简单实用

[技术图片

import struct
import json

# 'i'是格式
try:
    obj = struct.pack('i', 1222222222223)
except Exception as e:
    print(e)
    obj = struct.pack('i', 1222)
print(obj, len(obj))

‘i‘ format requires -2147483648 <= number <= 2147483647
b‘\xc6\x04\x00\x00‘ 4

res = struct.unpack('i', obj)
print(res[0])

1222

三、解决粘包问题终结版

解决粘包问题的核心就是:为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。

3.1 使用struct模块创建报头

import json
import struct

header_dic = 
    'filename': 'a.txt',
    'total_size':
    111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223131232,
    'hash': 'asdf123123x123213x'


header_json = json.dumps(header_dic)

header_bytes = header_json.encode('utf-8')
print(len(header_bytes))

# 'i'是格式
obj = struct.pack('i', len(header_bytes))
print(obj, len(obj))

223
b‘\xdf\x00\x00\x00‘ 4

res = struct.unpack('i', obj)
print(res[0])

223

3.2服务端

import socket
import subprocess
import struct

soc = socket.socket()
soc.bind(('127.0.0.1', 8001))
soc.listen(4)
while True:
    print("等待客户端连接")
    conn, addr = soc.accept()
    print("有个客户端连接上:", addr)

    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:
                break
            print(data)
            obj = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            # 执行结果为b格式, gbk编码(windows平台)
            msg = obj.stdout.read()

            """
            发送的时候需要把长度计算出来
            头必须是固定的长度
            选取出要发送数据的长度
            """
            leng = len(msg)
            # head是固定的四个字节
            head = struct.pack('i', leng)

            # 发送头
            conn.send(head)
            # 发送内容
            conn.send(msg)


        except Exception as e:
            print(e)
            break
    conn.close()

soc.close()
3.3 客户端
import socket
import struct

soc = socket.socket()

soc.connect(('127.0.0.1', 8001))

while True:
    in_s = input("请输入要执行命令:")
    soc.send(in_s.encode('utf-8'))
    head = soc.recv(4)
    lengs = struct.unpack('i', head)[0]

    count = 0
    data_total = b""
    while count < lengs:
        if lengs < 1024:
            # 如果接收的数据小于1024,直接接收数据的大小
            data = soc.recv(lengs)
        else:
            # 如果接收的数据大于1024
            if lengs - count > 1024:
                # 总数据长度减去count(目前收到多少, count就是多少), 如果还大于1024,在收1024

                data = soc.recv(1024)
            else:
                # 总数据长度减去count(目前收到多少,count就是多少), 如果小于1024, 只收剩下的部分即可
                data = soc.recv(lengs- count)

        data_total += data
        count += len(data)

    print(data_total.decode('gbk'))

以上是关于解决粘包问题的主要内容,如果未能解决你的问题,请参考以下文章

Netty进阶——粘包与半包(固定长度方式解决粘包问题)

Netty进阶——粘包与半包(固定长度方式解决粘包问题)

Netty进阶——粘包与半包(预设长度方式解决粘包问题)

Netty进阶——粘包与半包(预设长度方式解决粘包问题)

Netty进阶——粘包与半包(固定分隔符方式解决粘包问题)

Netty进阶——粘包与半包(固定分隔符方式解决粘包问题)