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

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()

功能验证说明

  1. 多客户端登录:启动多个客户端,分别输入不同用户名
  2. 私聊测试:输入@目标用户名 你好,如果目标在线,双方都会收到带发送者的私聊消息;如果目标不在线,发送者会收到系统错误提示
  3. 公聊测试:直接输入消息,所有在线用户都会收到带发送者名称的消息

内容的提问来源于stack exchange,提问作者Žan Konečnik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:45:35