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

    一、粘包问题

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

    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'))
    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: 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送。

    import socket
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 9000)
    )
    
    client.send(b'hello')
    client.send(b'hello')
    client.send(b'hello')
    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(真实数据长度)

    简单版:

    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'))
    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()

    序列化版:

    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)
    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()
  • 相关阅读:
    ASP.NET 静态化
    一周死磕fastreport ----ASP.NET (二)
    一周死磕fastreport ----ASP.NET (一)
    前台替换用户名部分转换为*显示
    截取部分字符信息
    win10家庭版设置移动热点出现“我们无法设置移动热点”
    云服务器配置出现的问题 2
    云服务器配置出现的问题 1
    关于使用jquery评论插件...
    $(selector).each() 和$each() 的区别
  • 原文地址:https://www.cnblogs.com/wddxx/p/13672397.html
Copyright © 2011-2022 走看看