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

基于Socket的Python Fallout Terminal聊天室通信异常求助

Fallout Terminal聊天室发送消息后冻结问题排查与修复

问题描述

我正在开发一个功能完整的Fallout Terminal,集成了运行脚本用户间聊天的聊天室功能。临时搭建的小型聊天室起初运行正常,但来回发送约10条消息后,消息无法正常收发,最终程序陷入无响应冻结状态,寻求原因分析与修复方法。

注:return_menu()函数仅用于退出循环返回主页,不会对消息系统代码产生任何影响。

现有代码

定义代码

import socket
import time

def charp(userInput, speed):
    for char in userInput:
        print(char, end='')
        sys.stdout.flush()
        time.sleep(speed)
    e()

def e():
    print("")

服务器代码

def server_host():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("localhost", 9999))

    server.listen()

    client, addr = server.accept()

    charp("| Save user's name as: ", 0.05)
    msgServer = input("")
    e()

    while True:
        msg = client.recv(1024).decode('utf-8')
        if msg == 'EXIT':

            client.send("EXIT".encode('utf-8'))

            break

        else:
            charp(msgServer + ": " + msg, 0.05)

        client.send(input("You: ").encode('utf-8'))

    client.close()

    server.close()

    return_menu()

客户端代码

def client_host():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    client.connect(("localhost", 9999))

    charp("| Save user's name as: ", 0.05)
    msgClient = input("")
    e()

    while True:
        client.send(input("You: ").encode('utf-8'))
        msg = client.recv(1024).decode('utf-8')
        if msg == "EXIT":

            client.send("EXIT".encode('utf-8'))

            break

        else:
            charp(msgClient + ": " + msg, 0.05)

    client.close()

    return_menu()

问题原因分析

  1. TCP粘包/拆包问题:TCP是字节流协议,recv(1024)无法保证每次都能拿到完整的一条消息。当消息长度超过1024字节,或多条消息被合并发送时,会导致某次recv只获取到部分消息,后续逻辑混乱,引发双方互相等待(一方等recv完整消息,另一方等input输入),最终死锁冻结。
  2. 缺少sys模块导入:charp函数中调用了sys.stdout.flush(),但代码未导入sys模块,可能在运行时抛出异常,干扰消息流转。
  3. 同步收发逻辑缺陷:当前代码采用严格的"发-收-发"同步逻辑,一旦某次消息接收不完整,整个流程就会阻塞,无法继续处理后续消息。

修复方案与修改后代码

核心修复点

  • 给每条消息添加边界标记(如换行符\n),接收时循环读取直到获取完整的一条消息,解决粘包/拆包问题。
  • 补全sys模块导入,避免函数执行异常。
  • 使用try-finally确保socket资源正常关闭,避免资源泄漏。
  • 可选:使用多线程分离收发逻辑,避免输入时阻塞消息接收。

修改后代码

定义代码(补全导入)

import socket
import time
import sys  # 新增sys模块导入

def charp(userInput, speed):
    for char in userInput:
        print(char, end='')
        sys.stdout.flush()
        time.sleep(speed)
    e()

def e():
    print("")

服务器代码(修复收发逻辑)

def server_host():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("localhost", 9999))
    server.listen()
    client, addr = server.accept()

    charp("| Save user's name as: ", 0.05)
    msgServer = input("")
    e()

    try:
        while True:
            # 循环接收直到获取完整的一行消息
            msg_buffer = b""
            while b"\n" not in msg_buffer:
                chunk = client.recv(1024)
                if not chunk:  # 客户端断开连接
                    break
                msg_buffer += chunk
            msg = msg_buffer.decode('utf-8').strip()
            
            if msg == 'EXIT':
                client.send("EXIT\n".encode('utf-8'))
                break

            charp(f"{msgServer}: {msg}", 0.05)
            
            # 发送消息时添加换行标记
            send_msg = input("You: ")
            client.send(f"{send_msg}\n".encode('utf-8'))
    finally:
        client.close()
        server.close()
        return_menu()

客户端代码(修复收发逻辑)

def client_host():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("localhost", 9999))

    charp("| Save user's name as: ", 0.05)
    msgClient = input("")
    e()

    try:
        while True:
            send_msg = input("You: ")
            client.send(f"{send_msg}\n".encode('utf-8'))
            
            # 循环接收直到获取完整的一行消息
            msg_buffer = b""
            while b"\n" not in msg_buffer:
                chunk = client.recv(1024)
                if not chunk:  # 服务器断开连接
                    break
                msg_buffer += chunk
            msg = msg_buffer.decode('utf-8').strip()
            
            if msg == "EXIT":
                client.send("EXIT\n".encode('utf-8'))
                break

            charp(f"{msgClient}: {msg}", 0.05)
    finally:
        client.close()
        return_menu()

可选优化:多线程分离收发

如果希望在输入消息时也能实时接收对方消息,避免阻塞,可以用多线程实现:

客户端多线程版本

import threading

def receive_messages(client, msgClient):
    try:
        while True:
            msg_buffer = b""
            while b"\n" not in msg_buffer:
                chunk = client.recv(1024)
                if not chunk:
                    charp("对方已断开连接", 0.05)
                    break
                msg_buffer += chunk
            msg = msg_buffer.decode('utf-8').strip()
            if msg == "EXIT":
                charp("对方已退出聊天", 0.05)
                break
            charp(f"{msgClient}: {msg}", 0.05)
    finally:
        client.close()
        return_menu()

def client_host():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("localhost", 9999))

    charp("| Save user's name as: ", 0.05)
    msgClient = input("")
    e()

    # 启动接收线程
    recv_thread = threading.Thread(target=receive_messages, args=(client, msgClient))
    recv_thread.daemon = True
    recv_thread.start()

    try:
        while True:
            send_msg = input("You: ")
            client.send(f"{send_msg}\n".encode('utf-8'))
            if send_msg == "EXIT":
                break
    finally:
        client.close()

服务器的多线程版本可以参照客户端逻辑实现,这里不再赘述。

内容的提问来源于stack exchange,提问作者TrickyTaco11

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:04:54