zoukankan      html  css  js  c++  java
  • tcp协议粘包问题

     粘包问题是tcp协议流式传输数据的方式导致的
    举例:
    from socket import *
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))
    
    # 通信循环
    while True:
        cmd=input('>>: ').strip()
        if len(cmd) == 0:continue
        client.send(cmd.encode('utf-8'))
        cmd_res=client.recv(1024)
        print(cmd_res.decode('gbk'))
    
    client.close()
    客户端
    # 服务端必须满足至少三点:
    # 1. 绑定一个固定的ip和port
    # 2. 一直对外提供服务,稳定运行
    # 3. 能够支持并发
    from socket import *
    import subprocess
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)
    
    # 链接循环
    while True:
        conn, client_addr = server.accept()
        print(client_addr)
    
        # 通信循环
        while True:
            try:
                cmd = conn.recv(1024) #cmd=b'dir'
                if len(cmd) == 0: break  # 针对linux系统
                obj=subprocess.Popen(cmd.decode('utf-8'),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
                print(len(stdout) + len(stderr))
                conn.send(stdout+stderr)
            except ConnectionResetError:
                break
    
        conn.close()
    
    server.close()
    View Code

    在每次运行时,数据量太大时,下一次命令的结果就是上次没发完的结果

    如何解决粘包问题:接收端能够精确地收干净每个数据包没有任何残留
    from socket import *
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))
    
    # tcp协议会将数据量较小且发送时间间隔较短的数据合并成一个数据报发送
    client.send(b'hello')
    client.send(b'world')
    client.send(b'egon')
    客户端
    from socket import *
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)
    
    conn,_=server.accept()
    data1=conn.recv(5)
    print('第一次收: ',data1)
    
    data2=conn.recv(5)
    print('第二次收: ',data2)
    
    data3=conn.recv(4)
    print('第三次收: ',data3)
    服务端

    解决粘包问题简单版

    from socket import *
    import struct
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))
    
    # 通信循环
    while True:
        cmd=input('>>: ').strip()
        if len(cmd) == 0:continue
        client.send(cmd.encode('utf-8'))
        #1. 先收报头,从报头里解出数据的长度
        header=client.recv(4)
        total_size=struct.unpack('i',header)[0]
        #2. 接收真正的数据
        cmd_res=b''
        recv_size=0
        while recv_size < total_size:
            data=client.recv(1024)
            recv_size+=len(data)
            cmd_res+=data
    
        print(cmd_res.decode('gbk'))
    
    client.close()
    客户端
    # 服务端必须满足至少三点:
    # 1. 绑定一个固定的ip和port
    # 2. 一直对外提供服务,稳定运行
    # 3. 能够支持并发
    from socket import *
    import subprocess
    import struct
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)
    
    # 链接循环
    while True:
        conn, client_addr = server.accept()
        print(client_addr)
    
        # 通信循环
        while True:
            try:
                cmd = conn.recv(1024) #cmd=b'dir'
                if len(cmd) == 0: break  # 针对linux系统
                obj=subprocess.Popen(cmd.decode('utf-8'),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
                # 1. 先制作固定长度的报头
                header=struct.pack('i',len(stdout) + len(stderr))
                # 2. 再发送报头
                conn.send(header)
                # 3. 最后发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
    
        conn.close()
    
    server.close()
    服务端

    模拟ssh解决粘包问题终极版

    from socket import *
    import struct
    import json
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))
    
    # 通信循环
    while True:
        cmd=input('>>: ').strip()
        if len(cmd) == 0:continue
        client.send(cmd.encode('utf-8'))
        #1. 先收4bytes,解出报头的长度
        header_size=struct.unpack('i',client.recv(4))[0]
    
        #2. 再接收报头,拿到header_dic
        header_bytes=client.recv(header_size)
        header_json=header_bytes.decode('utf-8')
        header_dic=json.loads(header_json)
        print(header_dic)
        total_size=header_dic['total_size']
    
        #3. 接收真正的数据
        cmd_res=b''
        recv_size=0
        while recv_size < total_size:
            data=client.recv(1024)
            recv_size+=len(data)
            cmd_res+=data
    
        print(cmd_res.decode('gbk'))
    
    client.close()
    客户端
    # 服务端必须满足至少三点:
    # 1. 绑定一个固定的ip和port
    # 2. 一直对外提供服务,稳定运行
    # 3. 能够支持并发
    from socket import *
    import subprocess
    import struct
    import json
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)
    
    # 链接循环
    while True:
        conn, client_addr = server.accept()
        print(client_addr)
    
        # 通信循环
        while True:
            try:
                cmd = conn.recv(1024)  # cmd=b'dir'
                if len(cmd) == 0: break  # 针对linux系统
                obj = subprocess.Popen(cmd.decode('utf-8'),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE
                                       )
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
                # 1. 先制作报头
                header_dic = {
                    'filename': 'a.txt',
                    'md5': 'asdfasdf123123x1',
                    'total_size': len(stdout) + len(stderr)
                }
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode('utf-8')
    
                # 2. 先发送4个bytes(包含报头的长度)
                conn.send(struct.pack('i', len(header_bytes)))
                # 3  再发送报头
                conn.send(header_bytes)
    
                # 4. 最后发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
    
        conn.close()
    
    server.close()
    服务端

    struct模块

    import struct
    
    obj1=struct.pack('i',1332)
    print(obj1,len(obj1))
    # 结果:b'4x05x00x00' 4
    res1=struct.unpack('i',obj1)
    print(res1[0])
    #结果:1332
    obj1=struct.pack('q',1332)
    print(obj1,len(obj1))
    # 结果:b'4x05x00x00x00x00x00x00' 8
    
    
    
    import json
    
    header_dic={
                    'filename':'a.txt',
                    'md5':'asdfasdf123123x1',
                    'total_size':12312311111111111111111111111111111111111111123
                }
    
    header_json=json.dumps(header_dic)
    print(header_json)
    #结果:
    # {"filename": "a.txt", "md5": "asdfasdf123123x1", "total_size": 123123111111111111111111。。。}
    header_bytes=header_json.encode('utf-8')
    print(len(header_bytes))
    # 结果:502
    举例:
  • 相关阅读:
    几种常见的Map的区别
    BlockingQueue详解
    Android开发过程中内存泄露检测
    Android studio 技巧设置(持续更新中)
    Android Support兼容包详解
    单例模式的饿汉式为什么需要双重锁定
    View分析
    Activity的启动流程分析
    LeetCode第十四题-字符串数组中最长的共同前缀
    LeetCode第十三题-将罗马数字转化为数字
  • 原文地址:https://www.cnblogs.com/zhouhao123/p/11261813.html
Copyright © 2011-2022 走看看