解决数据传输粘包的问题

解决数据传输粘包的问题

服务端

将数据分为6个阶段发送给客户端

  1. 将报头内容,制作成字典形式
  2. 将字典dumps为json字符串
  3. 把得到的json字符串,转换为二进制bytes
  4. 通过struct.pack把二进制bytes的json字符串计算出4个字节长度
  5. 发送报头 4个字节长度
  6. 发送数据
import socket
import subprocess
import struct
import json

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)  # 在bing 之前添加; 用于端口重用,释放

phone.bind(("127.0.0.1",8080))

phone.listen(5)

print("server start....")

# 接收消息,和发送消息
while True:
    conn,client_addr=phone.accept()     # 重新建立连接

    while True:
        try:
            cmd = conn.recv(1024)
            print("客户端接收:",cmd.decode("utf-8"))
            if not cmd:break                            # 针对linux 客户端如果断开后,服务端持续打印空
            res = subprocess.Popen(cmd.decode("utf-8"),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE
                                   )
            stdout = res.stdout.read()
            stderr = res.stderr.read()

            # 制作报头
            # header = struct.pack("i",len(stdout)+len(stderr))       # i 表示把长度打包成 4个 字节长度
            header_dic = {"total_size":len(stdout)+len(stderr),"md5":None}
            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode("utf-8")

            # 先把报头的长度 打包成 4个 字节长度 发送给客户端
            conn.send(struct.pack("i",len(header_bytes)))


            # 发送报头
            conn.send(header_bytes)

            conn.send(stderr)
            conn.send(stdout)

        except Exception:                               # 针对windows 如果客户端断开,服务端也断开
            break
    conn.close()
phone.close()

客户端

客户端接收数据

  1. 接收报头长度为struct.pack“i”大小为4个字节,固定长度
  2. 解析报头,取出真正数据报头的长度
  3. 根据数据报头的长度收取bytes字符串长度
  4. 提取报头字典
  5. 从字典中取出,数据的大小
  6. 循环接收数据

import socket
import struct
import json

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(("127.0.0.1",8080))

# phone.send("hello".encode("utf-8"))
while True:
    cmd = input(">>:").strip()
    if not cmd:continue
    phone.send(cmd.encode("utf-8"))

    # 接收报头长度,大小为 “i” 定义的4个字节
    struct_res = phone.recv(4)
    # 解析报头,取出报头长度
    header_size = struct.unpack("i",struct_res)[0]

    # 在收报头
    header_bytes = phone.recv(header_size)
    # 提取报头字典
    head_json = header_bytes.decode("utf-8")
    head_dic = json.loads(head_json)
    # 取出loads后字典中的total_size的值
    total_size = head_dic["total_size"]

    # 根据 取出的 字符串长度,循环接收内容
    recv_size = 0                               # 初始接收字节数为空
    data = b""                                  # 初始数据值为空
    while recv_size < total_size:
        recv_date = phone.recv(1024)
        recv_size += len(recv_date)
        data += recv_date

    print("命令结果:", data.decode("gbk"))

phone.close()


posted @ 2017-08-28 14:58  叨客厨子  阅读(565)  评论(0编辑  收藏  举报