Python聊天服务器改造需求:实现私聊与接收者登录校验
Python聊天服务器改造:实现私聊、在线校验与消息溯源
核心需求梳理
- 支持指定用户私聊:客户端发送消息时通过
@目标用户名前缀指定接收者 - 在线状态校验:如果接收者未登录,立即给发送者返回错误提示
- 消息溯源:所有转发的消息(无论公聊/私聊)都附带原始发送者的用户名
服务器改造关键步骤与代码实现
首先,服务器需要维护一个在线用户的映射表,同时解析消息的私聊标识,处理转发逻辑:
import socket import struct import threading PORT = 8888 HEADER_LENGTH = 2 online_users = {} # 存储{用户名: socket连接} def receive_fixed_length_msg(sock, msglen): message = b'' while len(message) < msglen: chunk = sock.recv(msglen - len(message)) if not chunk: return None message += chunk return message def handle_client(client_socket): try: # 第一步:接收客户端发送的用户名 username_len = struct.unpack('!H', receive_fixed_length_msg(client_socket, HEADER_LENGTH))[0] username = receive_fixed_length_msg(client_socket, username_len).decode('utf-8') online_users[username] = client_socket print(f"用户 {username} 已登录,当前在线用户: {list(online_users.keys())}") while True: # 接收消息头部和内容 msg_len = struct.unpack('!H', receive_fixed_length_msg(client_socket, HEADER_LENGTH))[0] msg_content = receive_fixed_length_msg(client_socket, msg_len).decode('utf-8') if not msg_content: break # 解析消息:判断是否为私聊(以@开头) if msg_content.startswith('@'): # 拆分目标用户名和消息内容 parts = msg_content.split(' ', 1) if len(parts) < 2: # 格式错误,返回提示 error_msg = "[系统提示] 私聊格式错误,请使用:@用户名 消息内容" send_msg(client_socket, error_msg) continue target_user = parts[0][1:] # 去掉@符号 actual_msg = parts[1] # 检查目标用户是否在线 if target_user not in online_users: error_msg = f"[系统提示] 用户 {target_user} 未在线或不存在" send_msg(client_socket, error_msg) continue # 构造带发送者的私聊消息,转发给目标用户 forwarded_msg = f"[私聊][{username}]: {actual_msg}" send_msg(online_users[target_user], forwarded_msg) # 给发送者回显自己的私聊消息 send_msg(client_socket, f"[你私聊{target_user}]: {actual_msg}") else: # 公聊消息,转发给所有在线用户 forwarded_msg = f"[{username}]: {msg_content}" for user_sock in online_users.values(): if user_sock != client_socket: send_msg(user_sock, forwarded_msg) except Exception as e: print(f"客户端连接异常: {e}") finally: # 用户退出,从在线列表移除 if username in online_users: del online_users[username] print(f"用户 {username} 已退出,当前在线用户: {list(online_users.keys())}") client_socket.close() def send_msg(sock, message): # 打包消息并发送 msg_bytes = message.encode('utf-8') msg_len = struct.pack('!H', len(msg_bytes)) sock.sendall(msg_len + msg_bytes) def start_server(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', PORT)) server_socket.listen(5) print(f"服务器已启动,监听端口 {PORT}...") while True: client_sock, client_addr = server_socket.accept() print(f"新客户端连接: {client_addr}") threading.Thread(target=handle_client, args=(client_sock,)).start() if __name__ == "__main__": start_server()
客户端适配改造(补全你的代码片段)
客户端需要先发送用户名,然后支持输入私聊格式的消息:
import socket import struct import sys import threading from datetime import datetime PORT = 8888 HEADER_LENGTH = 2 def receive_fixed_length_msg(sock, msglen): message = b'' while len(message) < msglen: chunk = sock.recv(msglen - len(message)) if not chunk: return None message += chunk return message def receive_messages(sock): while True: try: msg_len = struct.unpack('!H', receive_fixed_length_msg(sock, HEADER_LENGTH))[0] msg = receive_fixed_length_msg(sock, msg_len).decode('utf-8') if msg: print(f"\n[{datetime.now().strftime('%H:%M:%S')}] {msg}") print("输入消息(私聊格式:@用户名 消息):", end='', flush=True) except: print("\n与服务器断开连接") sock.close() sys.exit() def main(): if len(sys.argv) != 3: print("用法: python chatClient.py <服务器IP> <用户名>") sys.exit(1) server_ip = sys.argv[1] username = sys.argv[2] try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((server_ip, PORT)) # 发送用户名到服务器 username_bytes = username.encode('utf-8') sock.sendall(struct.pack('!H', len(username_bytes)) + username_bytes) print(f"已登录,用户名:{username}") # 启动接收消息的线程 threading.Thread(target=receive_messages, args=(sock,), daemon=True).start() # 发送消息循环 while True: msg = input("输入消息(私聊格式:@用户名 消息):") if msg.lower() == 'exit': sock.close() sys.exit() # 发送消息 msg_bytes = msg.encode('utf-8') sock.sendall(struct.pack('!H', len(msg_bytes)) + msg_bytes) except ConnectionRefusedError: print("无法连接到服务器,请检查服务器是否启动") sys.exit(1) if __name__ == "__main__": main()
功能验证说明
- 多客户端登录:启动多个客户端,分别输入不同用户名
- 私聊测试:输入
@目标用户名 你好,如果目标在线,双方都会收到带发送者的私聊消息;如果目标不在线,发送者会收到系统错误提示 - 公聊测试:直接输入消息,所有在线用户都会收到带发送者名称的消息
内容的提问来源于stack exchange,提问作者Žan Konečnik
相关产品推荐
相关产品推荐

