Python Socket多线程聊天客户端:接收线程终止时如何停止发送线程
Python Socket多线程聊天客户端:接收线程终止时如何停止发送线程
嘿,我看了你的代码,问题主要出在几个细节上,咱们一步步来解决:
问题点梳理
- 全局变量使用错误:你定义了全局
conn,但在类的方法里修改时没有用global conn声明,而且你写的是conn == False(这是比较操作,不是赋值!),导致这个变量根本没被正确修改,发送线程完全不知道要停止。 - 发送线程的检查逻辑失效:你的
send函数只在最开始判断了一次conn,进入while循环后就再也不检查状态了,就算后面状态改变,循环也会一直跑下去。 - 全局变量不是最优选择:在类里用全局变量容易出现作用域问题,换成类的实例变量会更清晰、更安全。
解决方案
咱们把全局变量换成类的实例状态标记,同时修复循环检查和赋值错误:
- 在类的
__init__方法里添加一个实例变量self.running,用来标记客户端是否处于运行状态; - 在连接成功后把
self.running设为True; - 在接收线程的终止逻辑里,正确设置
self.running = False,并且关闭socket; - 修改发送线程的循环,每次迭代都检查
self.running,如果为False就退出循环; - 给发送操作添加异常处理,避免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
相关产品推荐
相关产品推荐

