首页 » 编程 » Python » Python学习 » 网络编程 » 正文

粘包

粘包现象

就是客户端发送到服务端的命令请求,服务端响应后发送结果回客户端,如果客户端接收的字节数小于结果的字节数,就会导致结果收不完,要在下一次的接收中才能再次把上一次没有接收完的数据收回来。

粘包底层原理分析

后面补充

解决粘包问题-简单版

服务端:

import socket
import subprocess
import struct

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(("127.0.0.1", 9090))
phone.listen(5)

while True:
    print("等待连接...")
    conn, client_addr = phone.accept()
    print("建立连接,客户端地址为:", client_addr)

    while True:
        try:
            # 1. 接收命令
            cmd = conn.recv(8196)
            if not cmd: break

            # 2. 执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode("GBK"),  # 使用GBK解码
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout = obj.stdout.read()  # 讲对象中的数据读出来
            stderr = obj.stderr.read()  # 讲对象中的数据读出来

            # 3. 把命令的结果返回给客户端
            # 3.1 制作固定长度的报头
            total_size = len(stdout) + len(stderr)
            header = struct.pack("i", total_size)   # 生成一个固定长度为4的报头

            # 3.2 把报头发送到客户端
            conn.send(header)  # header本身就是byte类型的,可以直接发送

            # 3.3 发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break

    conn.close()
phone.close()

 

客户端:

import socket
import struct

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 9090))

while True:
    cmd = input(">>>: ").strip()
    if not cmd: continue
    phone.send(cmd.encode("GBK"))  # 使用GBK编码,因为我在Windows上运行

    # 1. 接收报头信息
    header = phone.recv(4)

    # 2. 解析报头拿到真实数据的长度
    total_size = struct.unpack("i", header)[0]

    # 3. 接收真实数据
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        data = phone.recv(1024)
        recv_data += data
        recv_size += len(data)

    print(recv_data.decode("GBK"))  # 使用GBK解码

phone.close()

 

解决粘包问题-终极版

服务端:

import socket
import subprocess
import struct
import json

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(("127.0.0.1", 9090))
phone.listen(5)

while True:
    print("等待连接...")
    conn, client_addr = phone.accept()
    print("建立连接,客户端地址为:", client_addr)

    while True:
        try:
            # 1. 接收命令
            cmd = conn.recv(8196)
            if not cmd: break

            # 2. 执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode("GBK"),  # 使用GBK解码
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout = obj.stdout.read()  # 讲对象中的数据读出来
            stderr = obj.stderr.read()  # 讲对象中的数据读出来

            # 3. 把命令的结果返回给客户端
            # 3.1 制作报头
            header_dict = {
                "filename": "abc.txt",
                "md5": "xxxxxxx",
                "total_size": len(stdout) + len(stderr)
            }
            header_json = json.dumps(header_dict)  # 将报头字典序列化为字符串类型
            header_bytes = header_json.encode("GBK")  # 将序列化后字符串类型的报头编码为bytes类型

            # 3.2 发送报头长度
            conn.send(struct.pack('i', len(header_bytes)))

            # 3.3 发送报头
            conn.send(header_bytes)

            # 3.4 发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break

    conn.close()
phone.close()

 

客户端:

import socket
import struct
import json

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 9090))

while True:
    cmd = input(">>>: ").strip()
    if not cmd: continue
    phone.send(cmd.encode("GBK"))  # 使用GBK编码,因为我在Windows上运行

    # 1. 接收报头长度
    obj = phone.recv(4)
    head_size = struct.unpack("i", obj)[0]

    # 2. 接收报头
    header_bytes = phone.recv(head_size)

    # 3. 将报头解码
    header_json = header_bytes.decode("GBK")

    # 4. 将报头反序列化为json
    header_dict = json.loads(header_json)

    # 5. 获取到json里的真实数据的大小
    total_size = header_dict["total_size"]

    # 4. 接收真实数据
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        data = phone.recv(1024)
        recv_data += data
        recv_size += len(data)
    print(recv_data.decode("GBK"))  # 使用GBK解码

phone.close()

 

 

赞 (0)

发表评论

*