zoukankan      html  css  js  c++  java
  • 峰哥解决粘包的方式

    峰哥解决粘包的方法
    为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

    struct模块

    该模块可以把一个类型,如数字,转成固定长度的bytes

    >> > struct.pack('i', 1111111111111)

    。。。。。。。。。

    struct.error: 'i'
    format
    requires - 2147483648 <= number <= 2147483647 # 这个是范围

    import json, struct
    
    # 假设通过客户端上传1T:1073741824000的文件a.txt
    
    # 为避免粘包,必须自定制报头
    header = {'file_size': 1073741824000, 'file_name': '/a/b/c/d/e/a.txt',
              'md5': '8f6fbf8347faa4924a76856701edb0f3'}  # 1T数据,文件路径和md5值
    
    # 为了该报头能传送,需要序列化并且转为bytes
    head_bytes = bytes(json.dumps(header), encoding='utf-8')  # 序列化并转成bytes,用于传输
    
    # 为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
    head_len_bytes = struct.pack('i', len(head_bytes))  # 这4个字节里只包含了一个数字,该数字是报头的长度
    
    # 客户端开始发送
    conn.send(head_len_bytes)  # 先发报头的长度,4个bytes
    conn.send(head_bytes)  # 再发报头的字节格式
    conn.sendall(文件内容)  # 然后发真实内容的字节格式
    
    # 服务端开始接收
    head_len_bytes = s.recv(4)  # 先收报头4个bytes,得到报头长度的字节格式
    x = struct.unpack('i', head_len_bytes)[0]  # 提取报头的长度
    
    head_bytes = s.recv(x)  # 按照报头长度x,收取报头的bytes格式
    header = json.loads(json.dumps(header))  # 提取报头
    
    # 最后根据报头的内容提取真实的数据,比如
    real_data_len = s.recv(header['file_size'])
    s.recv(real_data_len)
    # struct的用法
    #
    _*_coding:utf-8_*_ # http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html __author__ = 'Linhaifeng' import struct import binascii import ctypes values1 = (1, 'abc'.encode('utf-8'), 2.7) values2 = ('defg'.encode('utf-8'), 101) s1 = struct.Struct('I3sf') s2 = struct.Struct('4sI') print(s1.size, s2.size) prebuffer = ctypes.create_string_buffer(s1.size + s2.size) print('Before : ', binascii.hexlify(prebuffer)) # t=binascii.hexlify('asdfaf'.encode('utf-8')) # print(t) s1.pack_into(prebuffer, 0, *values1) s2.pack_into(prebuffer, s1.size, *values2) print('After pack', binascii.hexlify(prebuffer)) print(s1.unpack_from(prebuffer, 0)) print(s2.unpack_from(prebuffer, s1.size)) s3 = struct.Struct('ii') s3.pack_into(prebuffer, 0, 123, 123) print('After pack', binascii.hexlify(prebuffer)) print(s3.unpack_from(prebuffer, 0))

    自定义报头

    # 服务端
    import
    socket, struct, json import subprocess phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加 phone.bind(('127.0.0.1', 8080)) phone.listen(5) while True: conn, addr = phone.accept() while True: cmd = conn.recv(1024) if not cmd: break print('cmd: %s' % cmd) res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() print(err) if err: back_msg = err else: back_msg = res.stdout.read() conn.send(struct.pack('i', len(back_msg))) # 先发back_msg的长度 conn.sendall(back_msg) # 在发真实的内容 conn.close()
    
    
    # 客户端
    # _*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    import socket, time, struct
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    res = s.connect_ex(('127.0.0.1', 8080))
    
    while True:
        msg = input('>>: ').strip()
        if len(msg) == 0: continue
        if msg == 'quit': break
    
        s.send(msg.encode('utf-8'))
    
        l = s.recv(4)
        x = struct.unpack('i', l)[0]
        print(type(x), x)
        # print(struct.unpack('I',l))
        r_s = 0
        data = b''
        while r_s < x:
            r_d = s.recv(1024)
            data += r_d
            r_s += len(r_d)
    
        # print(data.decode('utf-8'))
        print(data.decode('gbk'))  # windows默认gbk编码

    我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)

    发送时:

    先发报头长度

    再编码报头内容然后发送

    最后发真实内容

    接收时:

    先手报头长度,用struct取出来

    根据取出的长度收取报头内容,然后解码,反序列化

    从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

    #服务端定制复杂的报头
    import socket, struct, json
    import subprocess
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 就是它,在bind前加
    
    phone.bind(('127.0.0.1', 8080))
    
    phone.listen(5)
    
    while True:
        conn, addr = phone.accept()
        while True:
            cmd = conn.recv(1024)
            if not cmd: break
            print('cmd: %s' % cmd)
    
            res = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            err = res.stderr.read()
            print(err)
            if err:
                back_msg = err
            else:
                back_msg = res.stdout.read()
    
            headers = {'data_size': len(back_msg)}
            head_json = json.dumps(headers)
            head_json_bytes = bytes(head_json, encoding='utf-8')
    
            conn.send(struct.pack('i', len(head_json_bytes)))  # 先发报头的长度
            conn.send(head_json_bytes)  # 再发报头
            conn.sendall(back_msg)  # 在发真实的内容
    
        conn.close()
    # 客户端
    from socket import *
    import struct, json
    
    ip_port = ('127.0.0.1', 8080)
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(ip_port)
    
    while True:
        cmd = input('>>: ')
        if not cmd: continue
        client.send(bytes(cmd, encoding='utf-8'))
    
        head = client.recv(4)
        head_json_len = struct.unpack('i', head)[0]
        head_json = json.loads(client.recv(head_json_len).decode('utf-8'))
        data_len = head_json['data_size']
    
        recv_size = 0
        recv_data = b''
        while recv_size < data_len:
            recv_data += client.recv(1024)
            recv_size += len(recv_data)
    
        print(recv_data.decode('utf-8'))
        # print(recv_data.decode('gbk')) #windows默认gbk编码
  • 相关阅读:
    TransactSQL语言 学习sql server2005 step by step(四)
    一步一步学习C#(一)
    SQL实例进阶学习sql server2005 step by step(八)
    SQL Server中常用全局变量和函数 学习sql server2005 step by step(五)
    SQL实例进阶学习sql server2005 step by step(七)
    C#操作excel(开篇)
    SQL进阶提升(平时小积累)学习sql server2005 step by step(十)
    mysql 备份各种方法
    ubuntu 这可怕的弹出窗口啊“Enter password to unlock your login keyring”
    很简单的内核模块A+B
  • 原文地址:https://www.cnblogs.com/dangrui0725/p/9484623.html
Copyright © 2011-2022 走看看