Python Socket聊天程序问题:输入/users返回IP端口而非用户名
问题分析与修复方案
核心问题
代码中线程参数传递错误是导致/users返回IP端口的直接原因:
- 服务端启动
user_conn线程时,传入的第二个参数是addr(客户端IP+端口的元组) - 但
user_conn函数将第二个参数定义为nicknames,导致函数内部使用的nicknames实际上是客户端的IP端口,而非全局存储用户名的列表。
具体修复步骤
1. 修正线程参数与函数定义
修改server函数中启动线程的代码,移除错误的addr参数传递:
# 原代码 threading.Thread(target=user_conn, args=[client, addr]).start() # 修正后 threading.Thread(target=user_conn, args=[client]).start()
同时修改user_conn函数的定义,去掉错误的nicknames参数,直接使用全局的nicknames列表:
# 原代码 def user_conn(conn: socket.socket, nicknames): # 修正后 def user_conn(conn: socket.socket):
2. 优化/users命令的返回格式
将直接发送列表字符串的逻辑改为更友好的格式化文本:
# 原代码 if "/users" in msg: conn.sendall(bytearray(str(nicknames).encode())) # 修正后 if "/users" in msg: # 生成友好的在线用户列表文本 user_list = "当前在线用户: " + ", ".join(nicknames) if nicknames else "暂无在线用户" conn.send(user_list.encode(FORMAT))
3. 完善用户断开后的清理逻辑
用户断开连接时,同步移除nicknames中对应的用户名,避免出现用户名残留:
# 原代码 def byeee(conn: socket.socket): if conn in clients: conn.close() clients.remove(conn) # 修正后 def byeee(conn: socket.socket): if conn in clients: # 找到客户端对应的索引,同步移除昵称 idx = clients.index(conn) clients.remove(conn) conn.close() if idx < len(nicknames): nicknames.pop(idx)
4. (可选)优化客户端命令发送逻辑
当前客户端发送/users时会带上昵称前缀(如张三: /users),可以修改write函数,让纯命令不带前缀,更简洁:
# 原代码 msg = f"{nickname}: {input('')}" # 修正后 content = input('') # 如果是纯命令,直接发送,否则带昵称前缀 msg = content if content.startswith('/') else f"{nickname}: {content}"
修复后的完整代码
服务端代码
import socket, threading clients = [] nicknames = [] BYTES = 1024 FORMAT = "utf-8" def server(): IP = "127.0.0.1" PORT = 9090 BIND = (IP, PORT) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(BIND) sock.listen() print("Welcome to Budget-Discord!") while True: client, addr = sock.accept() print("Connected with {}".format(str(addr))) client.send('NICKNAME'.encode(FORMAT)) nickname = client.recv(BYTES).decode() nicknames.append(nickname) clients.append(client) print("Nickname is {}".format(nickname)) print(nicknames) client.send("\t >>> Connected to server!".encode(FORMAT)) # 修正线程参数 threading.Thread(target=user_conn, args=[client]).start() except Exception as e: print(f"Error, socket: {e}") def sendall(msg: str, conn: socket.socket): # Broadcast for client_conn in clients: if client_conn != conn: try: client_conn.send(msg.encode()) except Exception as e: print(f"Error, sendall: {e}") byeee(client_conn) # 修正函数参数 def user_conn(conn: socket.socket): while True: try: msg = conn.recv(BYTES).decode() if "/users" in msg: # 优化返回格式 user_list = "当前在线用户: " + ", ".join(nicknames) if nicknames else "暂无在线用户" conn.send(user_list.encode(FORMAT)) if msg: print(msg) allchat = f"{msg}" sendall(allchat, conn) except Exception as e: print(f"Error, user connection: {e}") byeee(conn) break # 完善清理逻辑 def byeee(conn: socket.socket): if conn in clients: idx = clients.index(conn) clients.remove(conn) conn.close() if idx < len(nicknames): nicknames.pop(idx) if __name__ == "__main__": server()
客户端代码
import socket, threading IP = "127.0.0.1" PORT = 9090 BIND = (IP, PORT) BYTES = 1024 FORMAT = "utf-8" nickname = input("Choose your nickname: ") client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(BIND) def receive(): while True: try: msg = client.recv(BYTES).decode() if msg == 'NICKNAME': client.send(nickname.encode()) else: print(msg) except Exception as e: print(f"Error, client receive: {e}") client.close() break def write(): while True: content = input('') # 纯命令不带昵称前缀 msg = content if content.startswith('/') else f"{nickname}: {content}" client.send(msg.encode()) receive_t = threading.Thread(target=receive).start() write_t = threading.Thread(target=write).start()
内容的提问来源于stack exchange,提问作者PapaPeskwo
相关产品推荐
相关产品推荐

