• 解决粘包问题


    解决粘包问题

    一、解决粘包问题方式一

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

    1.1 服务器

    import socket, subprocess
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    server.bind(('127.0.0.1', 8000))
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
    
        print('start...')
        while True:
            cmd = conn.recv(1024)
            print('cmd:', cmd)
    
            obj = subprocess.Popen(cmd.decode('utf8'),
                                   shell=True,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE)
    
            stdout = obj.stdout.read()
    
            if stdout:
                ret = stdout
            else:
                stderr = obj.stderr.read()
                ret = stderr
    
            ret_len = len(ret)
    
            conn.send(str(ret_len).encode('utf8'))
    
            data = conn.recv(1024).decode('utf8')
    
            if data == 'recv_ready':
                conn.sendall(ret)
    
        conn.close()
    
    server.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 简单实用

    [124-解决粘包问题-struct模块参数.png?x-oss-process=style/watermark

    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'xc6x04x00x00' 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'xdfx00x00x00' 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(3)
    
    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()
                """
                发送的时候需要先把长度计算出来
                头必须是固定长度
                先发4位,头的长度
                """
                import json
    
                dic = {'size': len(msg)}
                # 文件的长度,将字典序列化成字符串,进行二进制编码
                dict_bytes = json.dumps(dic).encode('utf-8')
                print("dict_bytes", dict_bytes)
    
                # head count是四个字节长度,统计字典的长度,转换成二进制
                head_count = struct.pack('i', len(dict_bytes))
                print(dic)
                print('head_count', head_count)
    
                # 发送头的长度 4个字节
                conn.send(head_count)
    
                # 发送头部内部
                conn.send(dict_bytes)
    
                # 发送内容
                conn.send(msg)
                print(msg)
    
    
    
            except Exception as e:
                print(e)
                conn.close()
                break
    soc.close()
    
    
    3.3 客户端
    import socket
    import struct
    import json
    
    soc = socket.socket()
    soc.connect(('127.0.0.1',8001))
    while True:
        in_s = input("请输入要执行的命令")
        soc.send(in_s.encode('utf-8'))
    
        # 头部字典的长度, 读取字典的长度
        head_dic_len = soc.recv(4)
        print(head_dic_len)
        # 解除真正的长度,对获取的字典长度进行解码,得到实际的长度
        head_l = struct.unpack('i', head_dic_len)[0]
    
        # byte 字典长度,读取字典的内容
        dic_byte = soc.recv(head_l)
    
        #  真正受到的头部字典,将读取的字典反序列化, 得到真正的字典
        head = json.loads(dic_byte)
        print(head)
    
        lengs = head['size']
        count = 0
        data_total = b""
        print(lengs)
        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'))
    

    四、总结

    解决粘包问题:就是转换成发送数据和接收数据的格式,

    发送数据:发送数据,先发送数据的长度,然后在发送真实数据的字节数;

    接收数据:接收真实数据的长度,然后在安置字节长度接收数据;

    在当下的阶段,必将由程序员来主导,甚至比以往更甚。
  • 相关阅读:
    iOS 方便的宏定义
    IOS 推送消息 php做推送服务端
    iOS 7 动画UIDynamicAnimator
    iOS 适配
    ios 实现简单的解析xml网页
    用 MPMoviePlayerController 实现简单的视频下载播放功能
    ios 自定义弹出对话框效果
    ios国外大神
    git学习
    ios 7UI适配方法
  • 原文地址:https://www.cnblogs.com/randysun/p/11516999.html
Copyright © 2020-2023  润新知