zoukankan      html  css  js  c++  java
  • socket粘包问题及解决方案

    一、粘包问题

    问题1: 无法确认对方发送过来数据的大小。

    ‘client.py'

    import socket
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 9000)
    )
    
    while True:
    
        cmd = input('客户端输入的内容: ')
    
        client.send(cmd.encode('utf-8'))
    
        data = client.recv(19190)
        print(len(data))
        print(data.decode('gbk'))
    

    ‘server.py'

    import socket
    import subprocess
    
    server = socket.socket()
    server.bind(('127.0.0.1',9000))
    server.listen(5)
    
    while True:
        conn,addr = server.accept()
        print(addr)
        while True:
            try:
                cmd = conn.recv(10)
                if len(cmd) == 0:
                    continue
                cmd = cmd.decode('utf-8')  #utf8
                if cmd == 'q':
                    break
                #调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
                obj = subprocess.Popen(
                    #cmd接受的是解码后的字符串
                    cmd,shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                #结果交给result变量名
                result = obj.stdout.read()+obj.stderr.read()
                print(len(result))
                print(result.decode('gbk'))  #windows系统下默认编码gbk
                #将结果返回给客户端
                conn.send(result)
    
            except Exception as e:
                print(e)
                break
        conn.close()
    
    

    问题2: 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送。

    ‘client.py'

    import socket
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 9000)
    )
    
    client.send(b'hello')
    client.send(b'hello')
    client.send(b'hello')
    

    ‘server.py'

    import socket
    
    server = socket.socket()
    
    server.bind(
        ('127.0.0.1', 9000)
    )
    
    server.listen(5)
    
    conn, addr = server.accept()
    
    data = conn.recv(5)
    print(data)  # b'hello'
    
    data = conn.recv(1024)
    print(data)  # b'hello'
    
    data = conn.recv(1024)
    print(data)  # b'hello'
    

    二、粘包问题的解决方案:

    粘包问题的解决方案: 确认对方数据的大小。

    这里需要用 struct模块

    struct是什么?
    是一个python内置的模块,它可以将固定长度的数据,打包成固定格式的长度。
    固定格式:如 “ i ” 模式
    i : 4

    struct作用:
    可以将真实数据,做成一个固定长度的报头,客户端发送给服务器,服务器可以接受报头,然后对报头进行解包,获取真实数据的长度,进行接收即可

    import struct
    
    data = b'1111111111111111'
    print(len(data))  #16
    
    #打包制作报头
    header = struct.pack('i',len(data))
    print(header)   #b'x10x00x00x00'
    print(len(header))  #4
    
    #解包获取真实数据长度 --->得到一个元组,元组中第一个值是真实数据的长度
    res = struct.unpack('i',header)[0]
    print(res)  #16
    

    无论哪一端先发送数据

    • 客户端
      - 1) 先制作报头,并发送 (struct)
      - 2) 发送真实数据

    • 服务端:
      - 1) 接收报头,并解包获取 真实数据长度
      - 2) 根据真实数据长度 接收真实数据
      recv(真实数据长度)

    简单版:
    ‘client.py'

    import socket
    import struct
    
    client = socket.socket()
    client.connect(('127.0.0.1', 9000))
    
    while True:
        cmd = input('客户端输入的内容: ')
        cmd_bytes = cmd.encode('utf-8')
    
        header = struct.pack('i',len(cmd_bytes))    #做一个报头
        print(len(header))  #打印报头的长度
        client.send(header)   #发送报头
        client.send(cmd_bytes)      #待服务端确认长度后,发送真实数据长度
        
        headers = client.recv(4)    #接受服务端的报头
        data_len = struct.unpack('i',headers)[0]     #解包
        result = client.recv(data_len)  #接受服务器返回的真实数据的长度
    
        print('接受服务器返回的真实数据的长度',len(result))
        print(result.decode('gbk'))
    

    ‘server.py'

    import socket
    import subprocess
    import struct
    
    server = socket.socket()
    server.bind(('127.0.0.1',9000))
    server.listen(5)
    
    while True:
        conn,addr = server.accept()
        print(addr)
        while True:
            try:
                header = conn.recv(10)    #获取客户端传过来的报头
                data_len = struct.unpack('i',header)[0]  #解包获取真实数据的长度
      		    cmd = conn.recv(data_len)       #准备接受真实数据
    
                if len(cmd) == 0:
                    continue
                cmd = cmd.decode('utf-8')  
                if cmd == 'q':
                    break
                #调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
                obj = subprocess.Popen(
                    #cmd接受的是解码后的字符串
                    cmd,shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                result = obj.stdout.read()+obj.stderr.read() #获取结果
                print('发送给服务端返回的真实数据的长度', len(result))
                header = struct.pack('i', len(result)) #做报头
                print(len(header))
                conn.send(header)   #发送报头给客户端
                conn.send(result)     #将结果返回给客户端
             
            except Exception as e:
                print(e)
                break
        conn.close()
    

    序列化版:
    ‘client.py'

    import socket,json
    import struct
    
    client = socket.socket()
    client.connect(('127.0.0.1',9000))
    while True:
        movie_name = input('请输入上传的电影名字:')
    
        #伪装电影的真实数据
        movie = 1000000
        send_dic ={'movie_name':movie_name,
                   'movie':movie}
        #序列化
        json = json.dumps(send_dic)
        print(json)
        print(json.encode('utf-8'))
        print(len(json.encode('utf-8')))
        json_bytes = json.encode('utf-8')
    
        #做一个报头
        header = struct.pack('i',len(json_bytes))
        #先发送报头
        client.send(header)
        #再发送真实数据
        client.send(json_bytes)
    

    ‘server.py'

    import socket,json
    import struct
    
    server = socket.socket()
    server.bind(('127.0.0.1',9000))
    server.listen(5)
    
    while True:
        conn,addr = server.accept()
        while True:
            try:
                #获取客户端传过来的报头
                header = conn.recv(4)
                #解包获取真实数据的长度
                json_len = struct.unpack('i',header)[0]
                #接受json(dic)的真实数据
                json_bytes_data = conn.recv(json_len)
                #将bytes类型数据转为json数据类型
                json_data = json_bytes_data.decode('utf-8')
                #反序列化   json--->dict
                back_dic = json.loads(json_data)
                print(back_dic)
                print(back_dic.get('movie'))
    
            except Exception as e:
                print(e)
                break
        conn.close()
    
  • 相关阅读:
    cnpm镜像安装
    wxParse解析html
    C++回调函数
    QT源码分析:QTcpServer
    QT实现支持加密的Sqlite数据库引擎
    VS2013+OpenCV3.4.2编译
    Android Tcp操作
    使用Delphi开发linux应用
    QT5.10+MinGW+OpenCV3.4.2编译
    C++ ActiveX开发的问题讨论
  • 原文地址:https://www.cnblogs.com/baohanblog/p/12143846.html
Copyright © 2011-2022 走看看