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字节)")# 构建headerheader = 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_valuereturn 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("数据包长度不足")# 解析headerheader = 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 = 16data_end = data_start + data_lengthserialized_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 = "=" * 100print(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服务器端显示结果:
