• Python网络编程之黏包问题


    二、解决黏包问题

    2.1 解决黏包方法1

    • 计算消息实体的大小
    • 服务端接受两次,一次时消息大小,二次是消息实体,解决消息实体黏包
    • 客户端发送两次,一次是消息大小,一次是消息实体
    • 在两次收发之间加入一次多余通信,以防止消息大小和消息实体黏包

    server端

    import  socket
    
    sk = socket.socket()
    
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    
    conn,addr = sk.accept()
    print(conn,addr)
    while True:
        cmd = input('请输入你的命令: ')
        if cmd == 'q':
            break
    
        conn.send(cmd.encode('gbk'))
        data_length = conn.recv(1024).decode('gbk')
        conn.send(b'ok')  # 接受到消息大小后马上send,这样可以隔开两次recv,并且第二次接受指定长度的消息
        data = conn.recv(int(data_length)).decode('gbk')
        print(data)
    
    conn.close()
    sk.close()

    client

    import  socket
    import subprocess
    sk = socket.socket()
    
    sk.connect(('127.0.0.1',9000))
    
    while True:
        cmd = sk.recv(1024).decode('gbk')
        ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        std_out = ret.stdout.read()
        std_err = ret.stderr.read()
    
        data_length = str(len(std_out)+len(std_err)).encode('gbk')
        sk.send(data_length)
        sk.recv(1024)  # send消息大小后马上转入recv,隔开两次send。
        sk.send(std_out)
        sk.send(std_err)
    
    sk.close()

    2.2  借助于struct模块

    server端

    import socket
    import struct
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    
    
    conn,addr = sk.accept()
    
    
    while True:
        cmd = input('请输入命令: ')
        conn.send(cmd.encode('utf-8'))
        data_length_struct = conn.recv(4)
        data_length = struct.unpack('i',data_length_struct)[0]
        res = conn.recv(data_length).decode('gbk')
        print(res)
    
    conn.close()
    sk.close()

    client端

    import  socket
    import subprocess
    import  struct
    
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    while True:
        cmd = sk.recv(1024).decode('utf-8')
        res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        stdout = res.stdout.read()
        std_err = res.stderr.read()
        data_length = len(stdout) + len(std_err)
        data_length_struct = struct.pack('i',data_length)
        sk.send(data_length_struct)
        sk.send(stdout)
        sk.send(std_err)
    
    sk.close()

    2.3 通过struct定制报头传输大文件

    server端

    import socket
    import struct
    import json
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8090))
    sk.listen()
    buffer = 2048
    
    conn,addr = sk.accept()
    
    # 先接受报头
    # 根据报头接受消息实体
    
    struct_length = conn.recv(4) # 接受struct长度的包
    bytes_head_length = struct.unpack('i',struct_length)[0] # 计算报头的长度
    bytes_head = conn.recv(bytes_head_length) # 接受报头
    json_head = bytes_head.decode('utf-8') # 将报头转换为str
    head = json.loads(json_head) # 将报头转换为字典
    fileSize = head['fileSize']
    
    with open(head['fileName'],'wb') as f:
        while fileSize:
            if fileSize >= buffer:
                content = conn.recv(buffer)
                f.write(content)
                fileSize -= buffer
            else:
                content = conn.recv(fileSize)
                f.write(content)
                break
    
    
    conn.close()
    sk.close()

    client端

    import  socket
    import os
    import json
    import struct
    
    sk = socket.socket()
    sk.connect(('127.0.0.1',8090))
    buffer = 2048
    # 定制报头
    # 先发送报头大小
    # 发送报头
    # 发送消息实体
    head = {'fileSize':None,
            'fileName':r'a.tar.gz',
            'filePath':r'C:Users王诚Desktop'}
    file = os.path.join(head['filePath'],head['fileName'])
    fileSize = os.path.getsize(file)
    head['fileSize'] = fileSize
    json_head = json.dumps(head) # 字典转换成了字符串
    bytes_head = json_head.encode('utf-8') # 将字符串的head转换为bytes类型
    bytes_head_length = len(bytes_head) #  计算bytes类型的head的大小
    
    struct_length = struct.pack('i',bytes_head_length) # 通过struct转换为固定长度的bytes
    sk.send(struct_length) # 先发报头的长度
    sk.send(bytes_head) # 发报头
    with open(file,'rb') as f:
        while fileSize:
            if fileSize >= buffer:
                content= f.read(buffer)
                sk.send(content)
                fileSize -= buffer
            else:
                content = f.read(fileSize)
                sk.send(content)
                break
    
    
    sk.close()

    三、检查客户端的合法性

    使用hmac

    server端

    import  socket
    import os
    import hmac
    
    secret_key = b'wangys'
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    
    def check_conn(conn):
        msg = os.urandom(32)
        conn.send(msg)
        h = hmac.new(secret_key,msg)
        server_msg = h.digest()
        client_msg = conn.recv(1024)
        res = hmac.compare_digest(server_msg,client_msg)
        return res
    
    conn,addr = sk.accept()
    res = check_conn(conn)
    if res:
        print('合法的客户端')
        conn.close()
    else:
        print('不合法的客户端')
        conn.close()
    
    sk.close()

    client端

    import socket
    import hmac
    secret_key = b'wangys'
    
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    msg = sk.recv(1024)
    h = hmac.new(secret_key,msg)
    client_msg = h.digest()
    sk.send(client_msg)
    
    sk.close()

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    java基础之switch
    String的getBytes()方法
    Android adb命令
    shell中grep命令详解
    su root 和su
    adb shell 命令详解
    adb shell am 的用法
    adb logcat 基本用法
    Android、iOS和Windows Phone中的推送技术
    Android客户端消息推送原理简介
  • 原文地址:https://www.cnblogs.com/wc89/p/10425496.html
Copyright © 2020-2023  润新知