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

Python Socket多线程聊天客户端:接收线程终止时如何停止发送线程

Python Socket多线程聊天客户端:接收线程终止时如何停止发送线程

嘿,我看了你的代码,问题主要出在几个细节上,咱们一步步来解决:

问题点梳理

  1. 全局变量使用错误:你定义了全局conn,但在类的方法里修改时没有用global conn声明,而且你写的是conn == False(这是比较操作,不是赋值!),导致这个变量根本没被正确修改,发送线程完全不知道要停止。
  2. 发送线程的检查逻辑失效:你的send函数只在最开始判断了一次conn,进入while循环后就再也不检查状态了,就算后面状态改变,循环也会一直跑下去。
  3. 全局变量不是最优选择:在类里用全局变量容易出现作用域问题,换成类的实例变量会更清晰、更安全。

解决方案

咱们把全局变量换成类的实例状态标记,同时修复循环检查和赋值错误:

  1. 在类的__init__方法里添加一个实例变量self.running,用来标记客户端是否处于运行状态;
  2. 在连接成功后把self.running设为True;
  3. 在接收线程的终止逻辑里,正确设置self.running = False,并且关闭socket;
  4. 修改发送线程的循环,每次迭代都检查self.running,如果为False就退出循环;
  5. 给发送操作添加异常处理,避免socket关闭后发送数据报错。

修改后的完整代码

import socket
import threading
import pickle
import sys

class ChatClient: #creates the clients connection to the server
    # admin passes a command to the server which allows commands
    def __init__(self, nickname, host, port, roomcode):
        self.nickname = str(nickname)
        self.host = host
        self.port = port
        self.roomcode = str(roomcode)
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.recieve = threading.Thread(target=self.receive)
        self.sending = threading.Thread(target=self.send)
        # 修改:添加实例变量标记运行状态
        self.running = False

    def connect(self): #connects to the server and starts a trhead to recieve data
        self.client_socket.connect((self.host, self.port))
        print("Connected to the server")
        # 修改:用实例变量代替全局变量
        self.running = True
        self.client_socket.send(self.nickname.encode())
        self.client_socket.recv(1024)
        self.client_socket.send(self.roomcode.encode())
        response = self.client_socket.recv(1024).decode()

        if response == "/adminReady":
            print(f"You're the Admin of Room: {self.roomcode}")
        elif response == "/ready":
            print(f"You've joined the Room: {self.roomcode}")
        elif response == "/wait":
            print(f"You're request to join Room: {self.roomcode} is pending approval")
            print("COMMANDS YOU CAN USE:\n/waitDisconnect - to finish waiting for approval\n")

        if self.running == True:
            self.recieve.start()
            self.sending.start()
        else:
            exit()

    def receive(self): #recieves data & breaks connection
        while self.running:
            try:
                message = self.client_socket.recv(1024).decode()
                if message == "/viewRequests":
                    self.client_socket.send(bytes(".","utf-8"))
                    response = self.client_socket.recv(1024).decode()
                    if response == "/sendingData":
                        self.client_socket.send(b"/readyForData")
                        data = pickle.loads(self.client_socket.recv(1024))
                        if data == set():
                            print("No pending requests")
                        else:
                            print("Pending Requests")
                            for request in data:
                                print(request)
                elif message == "/approveRequest":
                    self.client_socket.send(bytes(".", "utf-8"))
                    response = self.client_socket.recv(1024).decode()
                    if response == "/proceed":
                        usernameToAllow = input("Please enter username to allow in the room: ")
                        self.client_socket.send(bytes(usernameToAllow, "utf-8"))
                        print(self.client_socket.recv(1024).decode())
                    else:
                        print(response)
                elif message == "/disconnect":
                    self.client_socket.send(bytes(".", "utf-8"))
                    self.client_socket.close()
                    # 修改:正确赋值停止状态
                    self.running = False
                    break
                elif message == "/waitdisconnect":
                    self.client_socket.close()
                    self.running = False
                    break
                elif message == "/findAdmin":
                    self.client_socket.send(bytes(".", "utf-8"))
                    print(pickle.loads(self.client_socket.recv(1024)))
                elif message == "/onlineUsers":
                    self.client_socket.send(bytes(".", "utf-8"))
                    data = pickle.loads(self.client_socket.recv(1024))
                    print("Online Room Users:")
                    for user in data:
                        print(user)
                elif message == "/kickUser":
                    self.client_socket.send(bytes(".", "utf-8"))
                    response = self.client_socket.recv(1024).decode()
                    if response == "/proceed":
                        usernameToKick = input("Username to kick: ")
                        self.client_socket.send(bytes(usernameToKick, "utf-8"))
                    else:
                        print(response)
                elif message == "/kicked":
                    print("You've been kicked from the room")
                    self.client_socket.close()
                    self.running = False
                    break
                else:
                    print(message)
            except:
                print("An error occurred")
                self.client_socket.close()
                self.running = False
                raise SystemExit(0)

    def send(self): #sends data to other users
        # 修改:循环里每次检查运行状态
        while self.running:
            try:
                message = input("")
                # 发送前再检查一次,避免刚触发停止就发送
                if self.running:
                    self.client_socket.send(message.encode())
            except:
                # socket已关闭时的异常处理
                print("Connection closed, can't send messages")
                break

if __name__ == '__main__':
    nickname = input("Enter username: ")
    roomcode = int(input("Enter roomcode: "))
    client = ChatClient(nickname, 'localhost', 55555, roomcode)
    try:
        client.connect()
        # 等待两个线程结束后再退出
        client.recieve.join()
        client.sending.join()
    except KeyboardInterrupt:
        print("\nExiting...")
        client.running = False
        client.client_socket.close()

效果说明

现在当接收线程收到/disconnect、/kicked等终止命令时,会把self.running设为False,发送线程每次循环都会检查这个状态,发现为False就会退出循环,同时如果socket已经关闭,发送操作的异常处理也会让线程退出。这样整个程序就能完全终止,不会再让用户输入内容了。

备注:内容来源于stack exchange,提问作者Elsa May

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 10:29:07