From e1862126e8bab0bf2561b47c039787e55e84cc4a Mon Sep 17 00:00:00 2001 From: DZY Date: Fri, 13 Jun 2025 20:48:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=88=91=E4=B8=8D=E5=88=B0=E5=95=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CS3.1.py | 155 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 114 insertions(+), 41 deletions(-) diff --git a/CS3.1.py b/CS3.1.py index 24b78b2..36ad0e7 100644 --- a/CS3.1.py +++ b/CS3.1.py @@ -6,13 +6,21 @@ import socket import secrets import time import os +import logging + +# 配置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) app = Flask(__name__) socket_server = socket.socket() socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 修改数据结构:使用用户名作为主键 -active_connections = {} # {username: {'conn': conn, 'ip': ip}} +active_connections = {} # {username: {'conn': conn, 'ip': ip, 'last_active': timestamp}} chat_connections = [] # 所有活跃连接列表 tokens = {} # 令牌管理 @@ -49,7 +57,6 @@ def register_user(usr, pwd, avatar="default_avatar.png"): conn = get_db_connection() try: cursor = conn.cursor() - # 添加avatar字段 cursor.execute("INSERT INTO users (name, passwd, avatar) VALUES (?, ?, ?)", (usr, pwd, avatar)) conn.commit() @@ -78,9 +85,8 @@ def register1(): vl = request.get_json() usr = vl.get('username') pwd = vl.get('password') - avatar = vl.get('avatar', 'default_avatar.png') # 获取头像 + avatar = vl.get('avatar', 'default_avatar.png') - # 头像验证 if avatar and not (avatar.endswith('.png') or avatar.endswith('.jpg')): return jsonify({ "type": "register_0", @@ -101,11 +107,24 @@ def login(): # 检查账号是否已登录 if username in active_connections: - return jsonify({ - "type": "login_0", - "status": "error", - "message": "Account already logged in" - }), 409 # 冲突状态码 + # 检查连接是否仍然活跃 + conn_info = active_connections[username] + try: + # 发送测试消息检查连接是否有效 + conn_info['conn'].sendall(json.dumps({"type": "ping"}).encode('utf-8')) + logger.info(f"用户 {username} 的连接仍然活跃") + return jsonify({ + "type": "login_0", + "status": "error", + "message": "Account already logged in" + }), 409 + except: + # 连接已失效,清理旧连接 + logger.warning(f"清理无效连接: {username}") + if username in active_connections: + del active_connections[username] + if conn_info['conn'] in chat_connections: + chat_connections.remove(conn_info['conn']) if isuserxist(username) and ispsswdright(username, data['password']): token = generate_token(username) @@ -114,7 +133,7 @@ def login(): "type": "login_1", "status": "success", "token": token, - "avatar": avatar # 返回头像 + "avatar": avatar }) return jsonify({ "type": "login_0", @@ -139,19 +158,20 @@ def chat(): "type": "chat", "user": username, "message": data['message'], - "avatar": get_avatar(username) # 添加头像信息 + "avatar": get_avatar(username) } broadcast_message(message) return jsonify({"type": "chat", "status": "success"}) def broadcast_message(message, sender=None): - for conn in chat_connections: + for conn in chat_connections[:]: # 使用副本迭代 try: conn.sendall(json.dumps(message).encode('utf-8')) except: # 连接异常时移除 for uname, info in list(active_connections.items()): if info['conn'] == conn: + logger.warning(f"广播时移除无效连接: {uname}") del active_connections[uname] break if conn in chat_connections: @@ -170,26 +190,31 @@ def handle_socket_message(data, addr, conn): username = data['username'] password = data['password'] - # 检查账号是否已登录 + # 检查账号是否已登录且连接有效 if username in active_connections: - response = { - "type": "login_0", - "status": "error", - "message": "Account already logged in" - } - conn.sendall(json.dumps(response).encode('utf-8')) - return response + conn_info = active_connections[username] + try: + # 测试连接是否仍然有效 + conn_info['conn'].sendall(json.dumps({"type": "ping"}).encode('utf-8')) + logger.info(f"用户 {username} 尝试登录但已有活跃连接") + response = { + "type": "login_0", + "status": "error", + "message": "Account already logged in" + } + conn.sendall(json.dumps(response).encode('utf-8')) + return response + except: + # 连接已失效,清理旧连接 + logger.warning(f"清理无效连接后允许登录: {username}") + if username in active_connections: + del active_connections[username] + if conn_info['conn'] in chat_connections: + chat_connections.remove(conn_info['conn']) if isuserxist(username) and ispsswdright(username, password): - # 移除旧连接(如果存在) - if username in active_connections: - old_conn = active_connections[username]['conn'] - if old_conn in chat_connections: - chat_connections.remove(old_conn) - del active_connections[username] - # 添加新连接 - active_connections[username] = {'conn': conn, 'ip': addr[0]} + active_connections[username] = {'conn': conn, 'ip': addr[0], 'last_active': time.time()} if conn not in chat_connections: chat_connections.append(conn) @@ -201,9 +226,10 @@ def handle_socket_message(data, addr, conn): "message": "Login successful", "token": token, "username": username, - "avatar": avatar # 返回头像 + "avatar": avatar } conn.sendall(json.dumps(response).encode('utf-8')) + logger.info(f"用户 {username} 登录成功") return response else: response = { @@ -244,33 +270,81 @@ def handle_socket_message(data, addr, conn): conn.sendall(json.dumps(response).encode('utf-8')) return response + # 更新最后活跃时间 + active_connections[username]['last_active'] = time.time() + message = { "type": "chat", "user": username, "message": data['message'], - "avatar": get_avatar(username) # 添加头像 + "avatar": get_avatar(username) } broadcast_message(message) - # 返回成功响应 response = {"type": "chat", "status": "success"} conn.sendall(json.dumps(response).encode('utf-8')) return response + elif action == 'heartbeat': + # 心跳检测 + token = data.get('token') + if token: + username = validate_token(token) + if username and username in active_connections: + # 更新最后活跃时间 + active_connections[username]['last_active'] = time.time() + response = {"type": "heartbeat", "status": "success"} + conn.sendall(json.dumps(response).encode('utf-8')) + return response + return {"type": "heartbeat", "status": "error"} + except Exception as e: + logger.error(f"处理消息时出错: {str(e)}") response = { "status": "error", "message": str(e) } - conn.sendall(json.dumps(response).encode('utf-8')) + try: + conn.sendall(json.dumps(response).encode('utf-8')) + except: + pass return response +def check_inactive_connections(): + """定期检查不活跃的连接并清理""" + while True: + time.sleep(60) # 每分钟检查一次 + current_time = time.time() + inactive_users = [] + + for username, info in list(active_connections.items()): + # 5分钟无活动视为不活跃 + if current_time - info['last_active'] > 300: + logger.warning(f"检测到不活跃用户: {username}, 最后活跃: {current_time - info['last_active']}秒前") + inactive_users.append(username) + + for username in inactive_users: + info = active_connections[username] + try: + info['conn'].close() + except: + pass + if username in active_connections: + del active_connections[username] + if info['conn'] in chat_connections: + chat_connections.remove(info['conn']) + logger.info(f"已清理不活跃用户: {username}") + def run_socket_server(): - socket_server.bind(("localhost", 8889)) + socket_server.bind(("0.0.0.0", 8889)) socket_server.listen() - print("Socket server running on port 8889") + logger.info("Socket server running on port 8889") + + # 启动连接检查线程 + threading.Thread(target=check_inactive_connections, daemon=True).start() + while True: conn, addr = socket_server.accept() - print(f"Socket client connected: {addr}") + logger.info(f"Socket client connected: {addr}") try: while True: data = conn.recv(1024) @@ -278,7 +352,6 @@ def run_socket_server(): break try: - # 尝试解码为UTF-8,忽略错误字符 decoded_data = data.decode('utf-8', errors='ignore') json_data = json.loads(decoded_data) response = handle_socket_message(json_data, addr, conn) @@ -290,6 +363,7 @@ def run_socket_server(): } conn.sendall(json.dumps(response).encode('utf-8')) except Exception as e: + logger.error(f"处理数据时出错: {str(e)}") response = { "type": "error", "status": "error", @@ -297,13 +371,13 @@ def run_socket_server(): } conn.sendall(json.dumps(response).encode('utf-8')) except (ConnectionResetError, BrokenPipeError): - print(f"Client {addr} disconnected abruptly") + logger.warning(f"Client {addr} disconnected abruptly") finally: # 清理断开的连接 for username, info in list(active_connections.items()): if info['conn'] == conn: del active_connections[username] - print(f"User {username} disconnected") + logger.info(f"用户 {username} 断开连接") break if conn in chat_connections: @@ -313,14 +387,13 @@ def run_socket_server(): conn.close() except: pass - print(f"Connection closed for {addr}") + logger.info(f"Connection closed for {addr}") if __name__ == '__main__': with get_db_connection() as conn: - # 添加avatar字段 conn.execute('''CREATE TABLE IF NOT EXISTS users (name TEXT PRIMARY KEY, passwd TEXT, avatar TEXT DEFAULT 'default_avatar.png')''') threading.Thread(target=run_socket_server, daemon=True).start() - app.run(port=5001) \ No newline at end of file + app.run(port=5001, host='0.0.0.0') \ No newline at end of file