Python UDP双向消息脚本故障求助:本地测试消息无法接收
问题分析与修复方案
核心问题1:Socket被提前关闭
send.py的send_message函数中,处理完单个分段后就执行了sock.close(),直接关闭了整个socket,导致后续消息无法发送,甚至首次消息的ACK接收也会失败。
修复:移除send_message函数内的sock.close(),仅在整个聊天结束时关闭socket(send_chat函数末尾已有该逻辑)。
核心问题2:ACK校验逻辑不匹配
send.py中判断ACK的条件是if ack == segment_with_checksum,但receive.py的send_ack函数仅发送分段的校验和(segment[-2:]),两者内容完全不匹配,导致发送端永远无法确认ACK,一直卡在循环等待。
修复:修改发送端的ACK判断条件,对比收到的ACK与当前分段的校验和:
if ack == segment_with_checksum[-2:]:
核心问题3:缺少消息结束标记
send.py未给最后一个分段添加接收端期待的$结束标记,导致receive.py无法判断消息是否接收完成,也就不会打印收到的内容。
修复:在发送端生成分段后,给最后一个分段追加$:
segments = [message[i:i+1400] for i in range(0, len(message), 1400)] if segments: segments[-1] += "$"
核心问题4:接收端未处理分段拼接
receive.py的receive_message函数未被调用,当前逻辑仅处理单个分段的校验和ACK,未将多个分段拼接成完整消息。
修复:调整receive_chat函数,添加消息缓存逻辑处理分段拼接:
def receive_chat(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("localhost", 54321)) sock.settimeout(10.0) message_buffer = {} # 用发送方地址做key,缓存未完成的消息 while True: try: segment, address = sock.recvfrom(1400) received_checksum = int.from_bytes(segment[-2:], "big") segment_data = segment[:-2] calculated_checksum = sum(segment_data) & 0xFFFF if received_checksum == calculated_checksum: send_ack(segment, address, sock) # 初始化地址对应的消息缓存 if address not in message_buffer: message_buffer[address] = "" # 判断是否为最后一段 if segment_data[-1:] == b"$": message_buffer[address] += segment_data[:-1].decode() print("Mensagem recebida:", message_buffer[address]) del message_buffer[address] # 清空已完成的缓存 else: message_buffer[address] += segment_data.decode() else: print("Erro! Checksum não corresponde.") continue except socket.timeout: print("Timeout! Nenhuma mensagem recebida.") continue except KeyboardInterrupt: print("Encerrando chat...") break sock.close()
双向收发的改进
单向通信修复后,要实现双向收发,需要两端同时具备发送和接收能力:
- 用多线程拆分逻辑:一个线程处理用户输入并发送消息,另一个线程持续监听接收消息。
- 两端都需要绑定端口,且线程需设置为守护线程,避免阻塞主程序退出。
示例(send.py修改为双向版):
import socket import random import time import threading def calculate_checksum(segment): checksum = sum(segment) & 0xFFFF return checksum.to_bytes(2, "big") def send_segment(segment, address, sock): if random.random() < 0.2: print("模拟丢包,未发送该分段") return time.sleep(0.1) sock.sendto(segment, address) def send_message(message, sock, address): segments = [message[i:i+1400] for i in range(0, len(message), 1400)] estimated_rtt = 0.1 dev_rtt = 0.0 if segments: segments[-1] += "$" for segment in segments: checksum = calculate_checksum(segment.encode()) segment_with_checksum = segment.encode() + checksum send_segment(segment_with_checksum, address, sock) start_time = time.time() while True: try: ack, _ = sock.recvfrom(1400) if ack == segment_with_checksum[-2:]: rtt = time.time() - start_time dev_rtt = 0.75 * dev_rtt + 0.25 * abs(rtt - estimated_rtt) estimated_rtt = 0.875 * estimated_rtt + 0.125 * rtt print(f"收到ACK,RTT: {rtt:.2f}s") break except socket.timeout: timeout = estimated_rtt + 4 * dev_rtt if time.time() - start_time > timeout: print(f"超时,重发分段") send_segment(segment_with_checksum, address, sock) def receive_loop(sock): message_buffer = {} while True: try: segment, address = sock.recvfrom(1400) received_checksum = int.from_bytes(segment[-2:], "big") segment_data = segment[:-2] calculated_checksum = sum(segment_data) & 0xFFFF if received_checksum == calculated_checksum: sock.sendto(segment[-2:], address) if address not in message_buffer: message_buffer[address] = "" if segment_data[-1:] == b"$": message_buffer[address] += segment_data[:-1].decode() print(f"\n来自{address}的消息: {message_buffer[address]}") print("Message ('quit' to quit): ", end="", flush=True) del message_buffer[address] else: message_buffer[address] += segment_data.decode() else: print("Checksum错误,丢弃分段") except Exception: continue def send_chat(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("localhost", 12345)) sock.settimeout(0.5) recipient_address = input("IP: ") recipient_port = int(input("Port: ")) recipient = (recipient_address, recipient_port) # 启动接收线程 receive_thread = threading.Thread(target=receive_loop, args=(sock,), daemon=True) receive_thread.start() while True: message = input("Message ('quit' to quit): ") if message.lower() == "quit": break send_message(message, sock, recipient) sock.close() send_chat()
对receive.py做类似修改后,两端即可实现双向消息收发。
内容的提问来源于stack exchange,提问作者Diego Parente
相关产品推荐
相关产品推荐

