You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

双向收发的改进

单向通信修复后,要实现双向收发,需要两端同时具备发送和接收能力:

  1. 用多线程拆分逻辑:一个线程处理用户输入并发送消息,另一个线程持续监听接收消息。
  2. 两端都需要绑定端口,且线程需设置为守护线程,避免阻塞主程序退出。

示例(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:35:35