当前位置:首页>python>一学一实践:经典网络协议之Python构建UDP包,通过Socket收发验证

一学一实践:经典网络协议之Python构建UDP包,通过Socket收发验证

  • 2026-09-23 00:14:50
一学一实践:经典网络协议之Python构建UDP包,通过Socket收发验证
从UDP的包结构来了解UDP协议的传输机制,这个逻辑同样适用于发指定端口号的UDP包,来验证业务的存活状态。

1.任务一: NetDevOps经典自动化协议[构建UDP协议]

设计一个自己的UDP协议!用于传输各种Python数据

协议字段设计

# ---header设计---          # 2 字节 版本 1          # 2 字节 类型 1 为请求 2 为响应(由于是UDP单向流量!所有此次试验只有请求)          # 4 字节 ID号          # 8 字节 长度          # ---变长数据部分---总长度控制为512          # 使用pickle转换数据          # ---HASH校验---          # 16 字节 MD5值

UDP客户端代码:

import socketimport pickleimport structimport hashlibimport random# 协议定义VERSION = 1TYPE_REQUEST = 1TYPE_RESPONSE = 2MAX_DATA_SIZE = 512  # 总长度控制为512字节# 生成随机IDdef generate_id():    return random.randint(0, 0xFFFFFFFF)  # 4字节无符号整数# 打包数据def pack_data(data, packet_id=None):    # 如果没有提供ID,则生成一个    if packet_id is None:        packet_id = generate_id()    # 序列化数据    serialized_data = pickle.dumps(data)    # 计算数据长度    data_length = len(serialized_data)    # 计算总长度 (header + data + hash)    total_length = 2 + 2 + 4 + 8 + data_length + 16  # header(16字节) + data + hash(16字节)    # 确保总长度不超过512字节    if total_length > MAX_DATA_SIZE:        raise ValueError(f"总长度({total_length}字节)超过最大限制(512字节)")    # 构建header    header = struct.pack(        '!H H I Q',  # !表示网络字节序(大端),H=2字节,I=4字节,Q=8字节        VERSION,     # 2字节版本号        TYPE_REQUEST,  # 2字节类型        packet_id,   # 4字节ID号        data_length  # 8字节长度    )    # 计算MD5哈希值    md5_hash = hashlib.md5()    md5_hash.update(header + serialized_data)    hash_value = md5_hash.digest()  # 16字节MD5值    # 组合完整数据包    packet = header + serialized_data + hash_value    return packet# 发送数据包def send_packet(host, port, data):    # 创建UDP套接字    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    try:        # 打包数据        packet = pack_data(data)        # 发送数据包        sock.sendto(packet, (host, port))        print(f"成功发送数据包到 {host}:{port}")        print(f"数据包长度: {len(packet)} 字节")        # 解析header信息(用于调试)        header = packet[:16]  # header长度为16字节        version, packet_type, packet_id, data_length = struct.unpack('!H H I Q', header)        print(f"版本: {version}, 类型: {packet_type}, ID: {packet_id}, 数据长度: {data_length}")    except Exception as e:        print(f"发送数据失败: {e}")    finally:        sock.close()if __name__ == "__main__":    # 目标主机和端口    target_host = "127.0.0.1"  # 本地测试    target_port = 8888    # 发送简单的文本数据    data_to_send = "测试测试"    # 发送数据    send_packet(target_host, target_port, data_to_send)

UDP服务器端代码:

import socketimport pickleimport structimport hashlib# 协议定义VERSION = 1TYPE_REQUEST = 1TYPE_RESPONSE = 2MAX_DATA_SIZE = 512  # 总长度控制为512字节# 解析数据包def unpack_data(packet):    # 检查数据包长度    if len(packet) < 32:  # header(16字节) + hash(16字节),至少32字节        raise ValueError("数据包长度不足")    # 解析header    header = packet[:16]    version, packet_type, packet_id, data_length = struct.unpack('!H H I Q', header)    # 检查版本    if version != VERSION:        raise ValueError(f"版本不匹配,期望: {VERSION}, 实际: {version}")    # 检查类型    if packet_type != TYPE_REQUEST:        print(f"警告: 收到非请求类型的数据包,类型: {packet_type}")    # 解析数据部分    data_start = 16    data_end = data_start + data_length    serialized_data = packet[data_start:data_end]    # 解析哈希值    received_hash = packet[data_end:data_end + 16]    # 验证哈希值    md5_hash = hashlib.md5()    md5_hash.update(header + serialized_data)    computed_hash = md5_hash.digest()    if received_hash != computed_hash:        raise ValueError("哈希值验证失败,数据包可能已损坏")    # 反序列化数据    data = pickle.loads(serialized_data)    return {        "version": version,        "type": packet_type,        "id": packet_id,        "data_length": data_length,        "data": data    }# 接收数据包def receive_packet(host, port):    # 创建UDP套接字    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    try:        # 绑定地址和端口        sock.bind((host, port))        # 显示服务器就绪消息        separator = "=" * 100        print(f"{separator}UDP 服务器就绪!等待客户数据!{separator}")        while True:            # 接收数据包            data, addr = sock.recvfrom(MAX_DATA_SIZE)            try:                # 解析数据包                packet_info = unpack_data(data)                # 显示数据信息                print(f"数据源自于 :{addr}")                print(f"数据序列号 :{packet_info['id']}")                print(f"数据长度为 :{packet_info['data_length']}")                print(f"数据内容为:{packet_info['data']}")                print(separator)            except Exception as e:                print(f"解析数据包失败: {e}")                print(f"原始数据包: {data}")                print(separator)    except KeyboardInterrupt:        print("\n接收端已停止")    except Exception as e:        print(f"接收数据失败: {e}")    finally:        sock.close()if __name__ == "__main__":    # 本地监听地址和端口    listen_host = "0.0.0.0"  # 监听所有网络接口    listen_port = 8888    # 启动接收端    receive_packet(listen_host, listen_port)

UDP服务器端显示结果:

#Python,#SRE,#CCIE,#NetDevops,#AIops,#Linux,#Trae,#cisco

最新文章

随机文章