粘包问题以及解决方法

一、粘包问题

1、问题一:无法确认对方发送过来数据的大小,对数据接收有影响

 

  server.py文件内容:

"""
先启动套接字服务端
    注意:
        客户端一次发送,服务端先一次接收,再发送
"""
import socket
import subprocess


server = socket.socket()

server.bind(("127.0.0.1", 9527))

server.listen(5)

while True:
    conn, addr = server.accept()
    print(addr)
    while True:
        try:
            # 从内存中获取数据
            data = conn.recv(1024)

            if len(data) == 0:
                continue

            data = data.decode("utf-8")
            if data == "q":
                break

            # 调用subprocess,对终端进行操作,并获取操作后正确或错误的结果
            # 接收转码后的字符串
            obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

            # 将结果交给result变量
            result = obj.stdout.read() + obj.stderr.read()
            print(result.decode("gbk"))

            # 将结果返回给客户端
            conn.send(result)

        except Exception as e:
            print(e)
            break



    conn.close()

  client.py文件内容:

"""
启动服务端后再启动客户端
"""
import socket


client = socket.socket()

client.connect(("127.0.0.1", 9527))

while True:

    cmd = input("请输入服务端命令:")

    client.send(cmd.encode("utf-8"))

    data = client.recv(1024)    # 这里只接受到1024字节数据,可以调整,但是对内存有影响

    if len(data) == 0:
        continue

    if data == "q":
        break

    print(data.decode('gbk'))

  server.py执行结果:

  client.py执行结果:

 

 

2、问题二:在发送数据间隔短并且数据量小的情况下,会将所有数据一次性传入

 

  server.py文件内容:

"""
先启动套接字服务端
    注意:
        客户端一次发送,服务端先一次接收,再发送
"""
import socket


server = socket.socket()

server.bind(("127.0.0.1", 9527))

server.listen(5)

conn, addr = server.accept()

data = conn.recv(1024)

print(data)

  client.py文件内容:

"""
启动服务端后再启动客户端
"""
import socket


client = socket.socket()

client.connect(("127.0.0.1", 9527))

'''
发送三次请求,理论上结果应该是
b"hello"
b"hello"
b"hello"
''' client.send(b
"hello") client.send(b"hello") client.send(b"hello")

  server.py执行结果:

 

 

3、解决粘包问题(struct模块)

1、struct模块是什么

  struct模块是一个python内置模块,他可以将固定长度的数据,打包成固定格式的长度

  " i "模式:可以将数据打包成 4 个bytes

 

2、struct模块的作用

  可以将真实数据,做成一个固定长度的报头,客户端发送给服务端,服务端可以接受报头,然后对报头进行解包,获取真实数据的长度,进行接收即可

 

3、struct模块的使用

import struct

data = b'11111111111111'
# 打包制作报头
header = struct.pack("i", len(data))
print(header)

# 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度
res = struct.unpack("i", header)[0]
print(res)

  执行结果:

b'\x0e\x00\x00\x00'
14

 

4、粘包问题解决方法

  只要确认对方数据的大小(长度),根据大小进行接收即可解决

 

  - 无论哪一端先发送数据(例  客户端先行发送数据)

    - 客户端

      - 先制作报头并发送  struct.pack()

      - 发送真实数据  

    - 服务端:

      - 接收报头,并解包获取真实数据长度  struct.unpack()

      - 根据真实数据长度 接收真实数据

 

  server.py文件内容:

import socket
import subprocess
import struct


server = socket.socket()

server.bind(("127.0.0.1", 8001))

server.listen(5)

while True:
    conn, addr = server.accept()
    # print(addr)
    while True:
        try:
            # 获取客户端传过来的报头
            client_headers = conn.recv(4)
            # 解包获取真实数据长度
            data_len = struct.unpack("i", client_headers)[0]
            # 准备接收真实数据
            data = conn.recv(data_len)

            if len(data) == 0:
                continue

            data = data.decode("utf-8")
            if data == "q":
                break

            # 调用subprocess,对终端进行操作,并获取操作后正确或错误的结果
            # 接收转码后的字符串
            obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            # 将结果交给result变量
            result = obj.stdout.read() + obj.stderr.read()

            # 服务端做一个报头发送给客户端
            server_headers = struct.pack('i', len(result))
            # 传送报头数据
            conn.send(server_headers)
            # 待客户端确认长度后,发送真实数据返回给客户端
            conn.send(result)

        except Exception as e:
            print(e)
            break


    conn.close()

  client.py文件内容:

import socket
import struct


client = socket.socket()

client.connect(("127.0.0.1", 8001))

while True:

    cmd = input("请输入服务端命令:")

    cmd_bytes = cmd.encode("utf-8")

    # 做一个报头
    client_headers = struct.pack("i", len(cmd_bytes))
    # 想服务端传送报头
    client.send(client_headers)
    # 待服务端确认数据长度后,发送真实数据
    client.send(cmd_bytes)

    # 获取服务端的传过来的报头
    server_headers = client.recv(4)
    # 解包获取数据的真实长度
    data_len = struct.unpack('i', server_headers)[0]
    # 准备接受真实数据
    data = client.recv(data_len)

    if len(data) == 0:
        continue

    if data == "q":
        break

    print(data.decode('gbk'))

 

  可自行测试。

posted @ 2020-11-02 23:02  chchcharlie、  阅读(424)  评论(0编辑  收藏  举报