• 网络编程~~~~粘包


    一 两种发生粘包的情况:

    1. 接收方没有及时接受缓存区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
    2. 发送端需要等缓冲区满才发送出去,造成粘包 (发送数据时间间隔很短,数据也很小,会合到一起,造成粘包)

    二 粘包的解决方案:

    客户端循环接收,拿到接收数据的字节数的长度,用作判断结束循环的条件

    调用struct模块,把一个类型变成固定长度的bytes类型

    import struct
    # 将一个数字转化成等长度的bytes类型。
    ret = struct.pack('i', 183346)
    print(ret, type(ret), len(ret))
    
    # 通过unpack反解回来
    ret1 = struct.unpack('i',ret)[0]
    print(ret1, type(ret1))
    # 但是通过struct 处理不能处理太大,数字太大会报错
    

    2.1 low版解决粘包方案(远程执行命令)

    服务器端:

    import subprocess
    import socket
    import struct
    phone = socket.socket()
    phone.bind(('127.0.0.1',8848))
    phone.listen()
    print('已开机')
    while 1:
        conn, addr = phone.accept()
        while 1:
            try:
                from_client_data = conn.recv(1024).decode("utf-8")
                if from_client_data.upper() == 'Q':
                    print('客户端已经正常退出!')
                    break
                else:
                    print(f'来自客户端的信息:{from_client_data}')
                    obj = subprocess.Popen(from_client_data,
                                           shell=True,                                     stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE,
                                           )
                    to_client_data = obj.stdout.read() + obj.stderr.read()
                    data_size = len(to_client_data)
                    print(data_size)
                    ret = struct.pack('i',data_size)
                    conn.send(ret)
                    conn.send(to_client_data)
            except ConnectionResetError:
                print('客户端异常断开连接!')
                break
        conn.close()
    phone.close()
    

    客户端:

    import socket
    import struct
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8848))
    while 1:
        try:
            data = input('>>>')
            if not data:
                print('内容不能为空!')
                continue
            phone.send(data.encode('utf-8'))
            if data.upper() == "Q":
                break
            else:
                data_size = phone.recv(4)
                ret = struct.unpack('i',data_size)[0]
                data = b''
                while len(data) < ret:
                    from_server_data = phone.recv(1024)
                    data += from_server_data
                print(len(data))
                print(f'来自服务器的消息:{data.decode("gbk")}')
        except ConnectionResetError:
            print('服务器异常断开连接')
            break
    phone.close()
    

    2.2 自定制报头:

    服务器端:

    # 1. 自定义报头
    head_dic = {
        'file_name': 'test1',
        'md5': 6567657678678,
        'total_size': total_size,
    }
    # 2. json形式的报头
    head_dic_json = json.dumps(head_dic)
    
    # 3. bytes形式报头
    head_dic_json_bytes = head_dic_json.encode('utf-8')
    
    # 4. 获取bytes形式的报头的总字节数
    len_head_dic_json_bytes = len(head_dic_json_bytes)
    
    # 5. 将不固定的int总字节数变成固定长度的4个字节
    four_head_bytes = struct.pack('i', len_head_dic_json_bytes)
    
    # 6. 发送固定的4个字节
    conn.send(four_head_bytes)
    
    # 7. 发送报头数据
    conn.send(head_dic_json_bytes)
    
    # 8. 发送总数据
    conn.send(result)
    

    客户端:

    # 1. 接收固定长度的4个字节
    head_bytes = phone.recv(4)
    
    # 2. 获得bytes类型字典的总字节数
    len_head_dic_json_bytes = struct.unpack('i',head_bytes)[0]
    
    # 3. 接收bytes形式的dic数据
    head_dic_json_bytes = phone.recv(len_head_dic_json_bytes)
    
    # 4. 转化成json类型dic
    head_dic_json = head_dic_json_bytes.decode('utf-8')
    
    # 5. 转化成字典形式的报头
    head_dic = json.loads(head_dic_json)
    

    三 基于网络的UDP协议的socket

    客户端:

    import socket
    server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    server.bind(('192.168.14.198',9000))
    
    while 1:
        from_client_data = server.recvfrom(1024)  # 阻塞,等待客户来消息
        print(f'33[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} 33[0m')
        to_client_data = input('>>>').strip()
        server.sendto(to_client_data.encode('utf-8'),from_client_data[1])
    server.close()
    # 1. 基于udp协议的socket无须建立管道,先开启服务端或者客户端都行.
    # 2. 基于udp协议的socket接收一个消息,与发送一个消息都是无连接的.
    # 3. 只要拿到我的ip地址和端口就都可以给我发消息,我按照顺序接收消息.
    

    客户端:

    import socket
    client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    while 1:
        to_server_data = input('>>>:').strip()
        client.sendto(to_server_data.encode('utf-8'),('127.0.0.1',9000))
        data,addr = client.recvfrom(1024)
        print(f'来自服务端{addr}消息:{data.decode("utf-8")}')
    
  • 相关阅读:
    layui 参照赋值的两种方式
    c笔记
    Linux操作系统笔记
    make笔记
    Gcc如何知道文件类型。
    #include <xxx.h>和#include "xxx.h"的区别
    GCC编译流程
    c++ Socket客户端和服务端示例版本三(多线程版本)
    c++ Socket客户端和服务端示例版本二
    c++ Socket客户端和服务端示例版本一
  • 原文地址:https://www.cnblogs.com/lav3nder/p/11802148.html
Copyright © 2020-2023  润新知