diff --git a/chatclient3.0.py b/chatclient3.0.py index 63904be..c6953be 100644 --- a/chatclient3.0.py +++ b/chatclient3.0.py @@ -118,6 +118,7 @@ class ChatClient: return try: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect(('localhost', 5555)) self.socket.send(json.dumps({ 'type': 'login', @@ -131,7 +132,7 @@ class ChatClient: self.update_ui_state(True) threading.Thread(target=self.receive_messages, daemon=True).start() else: - messagebox.showerror("Error", "Login failed") + messagebox.showerror("Error", "Login failed - Invalid username or password") self.socket.close() except Exception as e: messagebox.showerror("Error", f"Connection error: {str(e)}") @@ -154,11 +155,12 @@ class ChatClient: }).encode('utf-8')) response = json.loads(temp_socket.recv(1024).decode('utf-8')) - if response['type'] == 'register_success': - messagebox.showinfo("Success", "Registration successful") - else: - messagebox.showerror("Error", "Registration failed - username may be taken") temp_socket.close() + + if response['type'] == 'register_success': + messagebox.showinfo("Success", "Registration successful. Please login now.") + else: + messagebox.showerror("Error", "Registration failed - username may be taken or invalid") except Exception as e: messagebox.showerror("Error", f"Connection error: {str(e)}") @@ -195,6 +197,10 @@ class ChatClient: msg['is_group'], msg['timestamp'] ) + elif data['type'] == 'server_shutdown': + messagebox.showinfo("Server", "Server is shutting down") + self.on_closing() + break except Exception as e: print(f"Error receiving message: {e}") break @@ -202,7 +208,7 @@ class ChatClient: def display_message(self, sender, receiver, message, is_group, timestamp=None): timestamp = timestamp or datetime.now().strftime("%Y-%m-%d %H:%M:%S") - if (is_group and receiver == self.current_chat and sender != self.current_user) or \ + if (is_group and receiver == self.current_chat) or \ (not is_group and ((sender == self.current_chat and receiver == self.current_user) or \ (sender == self.current_user and receiver == self.current_chat))): @@ -259,6 +265,9 @@ class ChatClient: self.message_entry.delete(0, tk.END) def show_create_group_dialog(self): + if not self.current_user: + return + dialog = tk.Toplevel(self.root) dialog.title("Create Group") dialog.geometry("300x300") @@ -301,8 +310,11 @@ class ChatClient: ttk.Button(button_frame, text="Cancel", command=dialog.destroy).pack(side=tk.RIGHT, padx=(0, 5)) def on_closing(self): - if self.current_user: - self.socket.close() + if hasattr(self, 'socket') and self.current_user: + try: + self.socket.close() + except: + pass self.root.destroy() if __name__ == "__main__": diff --git a/chatserver3.0.py b/chatserver3.0.py index 5d51ea6..0874238 100644 --- a/chatserver3.0.py +++ b/chatserver3.0.py @@ -3,12 +3,14 @@ import threading import json import sqlite3 from datetime import datetime +import os class ChatServer: def __init__(self, host='0.0.0.0', port=5555): self.host = host self.port = port self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server.bind((host, port)) self.server.listen() self.clients = {} @@ -16,258 +18,52 @@ class ChatServer: self.setup_database() def setup_database(self): + # 确保数据库文件存在并初始化 + db_exists = os.path.exists('chat_server.db') self.db = sqlite3.connect('chat_server.db', check_same_thread=False) cursor = self.db.cursor() + + # 创建用户表 cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT UNIQUE, - password TEXT + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL ) ''') + + # 创建消息表 cursor.execute(''' CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, - sender TEXT, - receiver TEXT, - message TEXT, - timestamp DATETIME, - is_group INTEGER + sender TEXT NOT NULL, + receiver TEXT NOT NULL, + message TEXT NOT NULL, + timestamp DATETIME NOT NULL, + is_group INTEGER NOT NULL ) ''') + + # 如果是首次创建数据库,添加一个默认管理员用户 + if not db_exists: + try: + cursor.execute('INSERT INTO users (username, password) VALUES (?, ?)', + ('admin', 'admin123')) + print("Created default admin user: admin/admin123") + except sqlite3.IntegrityError: + pass + self.db.commit() - def handle_client(self, client, address): - username = None - - try: - while True: - message = client.recv(1024).decode('utf-8') - if not message: - break - - data = json.loads(message) - if data['type'] == 'login': - username = data['username'] - password = data['password'] - if self.authenticate_user(username, password): - self.clients[username] = client - self.groups['General'].add(username) - client.send(json.dumps({'type': 'login_success'}).encode('utf-8')) - self.send_user_list() - self.send_group_list(username) - self.send_initial_messages(username) - else: - client.send(json.dumps({'type': 'login_fail'}).encode('utf-8')) - elif data['type'] == 'register': - username = data['username'] - password = data['password'] - if self.register_user(username, password): - client.send(json.dumps({'type': 'register_success'}).encode('utf-8')) - else: - client.send(json.dumps({'type': 'register_fail'}).encode('utf-8')) - elif data['type'] == 'message': - sender = data['sender'] - receiver = data['receiver'] - msg = data['message'] - is_group = data['is_group'] - self.save_message(sender, receiver, msg, is_group) - if is_group: - for member in self.groups.get(receiver, set()): - if member in self.clients and member != sender: - self.clients[member].send(json.dumps({ - 'type': 'message', - 'sender': sender, - 'receiver': receiver, - 'message': msg, - 'is_group': True - }).encode('utf-8')) - else: - if receiver in self.clients: - self.clients[receiver].send(json.dumps({ - 'type': 'message', - 'sender': sender, - 'receiver': receiver, - 'message': msg, - 'is_group': False - }).encode('utf-8')) - elif data['type'] == 'create_group': - group_name = data['group_name'] - if group_name not in self.groups: - self.groups[group_name] = set() - for user in data['members']: - if user in self.clients: - self.groups[group_name].add(user) - self.send_group_list_to_all() - - # 管理API处理逻辑 - elif data['type'] == 'admin_get_users': - cursor = self.db.cursor() - cursor.execute('SELECT id, username FROM users') - users = [{'id': row[0], 'username': row[1]} for row in cursor.fetchall()] - client.send(json.dumps({'type': 'admin_response', 'data': users}).encode('utf-8')) - - elif data['type'] == 'admin_create_user': - try: - cursor = self.db.cursor() - cursor.execute('INSERT INTO users (username, password) VALUES (?, ?)', - (data['username'], data['password'])) - self.db.commit() - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - except sqlite3.IntegrityError: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': 'Username exists'}).encode('utf-8')) - - elif data['type'] == 'admin_delete_user': - cursor = self.db.cursor() - try: - cursor.execute('DELETE FROM messages WHERE sender=? OR receiver=?', - (data['username'], data['username'])) - cursor.execute('DELETE FROM users WHERE username=?', (data['username'],)) - self.db.commit() - - if data['username'] in self.clients: - self.clients[data['username']].close() - del self.clients[data['username']] - for group in self.groups.values(): - if data['username'] in group: - group.remove(data['username']) - - self.send_user_list() - self.send_group_list_to_all() - - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - except Exception as e: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': str(e)}).encode('utf-8')) - - elif data['type'] == 'admin_get_messages': - cursor = self.db.cursor() - query = 'SELECT id, sender, receiver, message, timestamp, is_group FROM messages WHERE 1=1' - params = [] - - if 'sender' in data and data['sender']: - query += ' AND sender=?' - params.append(data['sender']) - if 'receiver' in data and data['receiver']: - query += ' AND receiver=?' - params.append(data['receiver']) - if 'is_group' in data and data['is_group'] is not None: - query += ' AND is_group=?' - params.append(1 if data['is_group'] else 0) - if 'start_time' in data and data['start_time']: - query += ' AND timestamp >= ?' - params.append(data['start_time']) - if 'end_time' in data and data['end_time']: - query += ' AND timestamp <= ?' - params.append(data['end_time']) - - query += ' ORDER BY timestamp DESC LIMIT 1000' - cursor.execute(query, params) - - messages = [] - for row in cursor.fetchall(): - messages.append({ - 'id': row[0], - 'sender': row[1], - 'receiver': row[2], - 'message': row[3], - 'timestamp': row[4], - 'is_group': bool(row[5]) - }) - - client.send(json.dumps({'type': 'admin_response', 'data': messages}).encode('utf-8')) - - elif data['type'] == 'admin_delete_message': - cursor = self.db.cursor() - cursor.execute('DELETE FROM messages WHERE id=?', (data['message_id'],)) - self.db.commit() - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - - elif data['type'] == 'admin_get_groups': - groups = [] - for name, members in self.groups.items(): - groups.append({ - 'name': name, - 'members': list(members) - }) - client.send(json.dumps({'type': 'admin_response', 'data': groups}).encode('utf-8')) - - elif data['type'] == 'admin_create_group': - group_name = data['group_name'] - if group_name not in self.groups: - self.groups[group_name] = set() - for user in data['members']: - if user in self.clients or self.user_exists(user): - self.groups[group_name].add(user) - self.send_group_list_to_all() - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - else: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': 'Group already exists'}).encode('utf-8')) - - elif data['type'] == 'admin_delete_group': - if data['group_name'] in self.groups: - del self.groups[data['group_name']] - cursor = self.db.cursor() - cursor.execute('DELETE FROM messages WHERE receiver=? AND is_group=1', (data['group_name'],)) - self.db.commit() - self.send_group_list_to_all() - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - else: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': 'Group not found'}).encode('utf-8')) - - elif data['type'] == 'admin_add_group_member': - if data['group_name'] in self.groups: - if data['username'] in self.clients or self.user_exists(data['username']): - self.groups[data['group_name']].add(data['username']) - if data['username'] in self.clients: - self.send_group_list(data['username']) - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - else: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': 'User not found'}).encode('utf-8')) - else: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': 'Group not found'}).encode('utf-8')) - - elif data['type'] == 'admin_remove_group_member': - if data['group_name'] in self.groups and data['username'] in self.groups[data['group_name']]: - self.groups[data['group_name']].remove(data['username']) - if data['username'] in self.clients: - self.send_group_list(data['username']) - client.send(json.dumps({'type': 'admin_response', 'success': True}).encode('utf-8')) - else: - client.send(json.dumps({'type': 'admin_response', 'success': False, 'error': 'Member not in group'}).encode('utf-8')) - - elif data['type'] == 'admin_get_server_status': - status = { - 'status': 'running', - 'users_online': len(self.clients), - 'groups': len(self.groups), - 'start_time': str(datetime.now()) - } - client.send(json.dumps({'type': 'admin_response', 'data': status}).encode('utf-8')) - - elif data['type'] == 'admin_restart_server': - client.send(json.dumps({'type': 'admin_response', 'success': True, 'message': 'Restart command received'}).encode('utf-8')) - - elif data['type'] == 'admin_shutdown_server': - client.send(json.dumps({'type': 'admin_response', 'success': True, 'message': 'Shutdown command received'}).encode('utf-8')) - threading.Thread(target=self.shutdown).start() - - except Exception as e: - print(f"Error handling client: {e}") - finally: - if username and username in self.clients: - del self.clients[username] - for group in self.groups.values(): - if username in group: - group.remove(username) - self.send_user_list() - client.close() - def authenticate_user(self, username, password): cursor = self.db.cursor() cursor.execute('SELECT * FROM users WHERE username=? AND password=?', (username, password)) return cursor.fetchone() is not None def register_user(self, username, password): + if not username or not password: + return False + try: cursor = self.db.cursor() cursor.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, password)) @@ -287,18 +83,24 @@ class ChatServer: def send_user_list(self): user_list = list(self.clients.keys()) for client in self.clients.values(): - client.send(json.dumps({ - 'type': 'user_list', - 'users': user_list - }).encode('utf-8')) + try: + client.send(json.dumps({ + 'type': 'user_list', + 'users': user_list + }).encode('utf-8')) + except: + pass def send_group_list(self, username): group_list = [group for group, members in self.groups.items() if username in members] if username in self.clients: - self.clients[username].send(json.dumps({ - 'type': 'group_list', - 'groups': group_list - }).encode('utf-8')) + try: + self.clients[username].send(json.dumps({ + 'type': 'group_list', + 'groups': group_list + }).encode('utf-8')) + except: + pass def send_group_list_to_all(self): for username in self.clients: @@ -308,11 +110,12 @@ class ChatServer: cursor = self.db.cursor() cursor.execute(''' SELECT sender, receiver, message, timestamp, is_group FROM messages - WHERE receiver=? OR (is_group=1 AND receiver IN ( - SELECT group_name FROM groups WHERE member=? - )) OR sender=? + WHERE receiver=? OR sender=? OR (is_group=1 AND receiver IN ( + SELECT name FROM sqlite_master WHERE type='table' AND name='groups' + )) ORDER BY timestamp - ''', (username, username, username)) + LIMIT 100 + ''', (username, username)) messages = [] for sender, receiver, message, timestamp, is_group in cursor.fetchall(): @@ -325,17 +128,148 @@ class ChatServer: }) if username in self.clients: - self.clients[username].send(json.dumps({ - 'type': 'initial_messages', - 'messages': messages - }).encode('utf-8')) + try: + self.clients[username].send(json.dumps({ + 'type': 'initial_messages', + 'messages': messages + }).encode('utf-8')) + except: + pass + + def user_exists(self, username): + cursor = self.db.cursor() + cursor.execute('SELECT 1 FROM users WHERE username=?', (username,)) + return cursor.fetchone() is not None + + def handle_client(self, client, address): + username = None + print(f"New connection from {address}") + + try: + while True: + try: + message = client.recv(1024).decode('utf-8') + if not message: + break + + data = json.loads(message) + print(f"Received from {address}: {data}") + + if data['type'] == 'login': + username = data['username'] + password = data['password'] + if self.authenticate_user(username, password): + self.clients[username] = client + self.groups['General'].add(username) + client.send(json.dumps({'type': 'login_success'}).encode('utf-8')) + self.send_user_list() + self.send_group_list(username) + self.send_initial_messages(username) + print(f"User {username} logged in successfully") + else: + client.send(json.dumps({'type': 'login_fail', 'reason': 'Invalid username or password'}).encode('utf-8')) + print(f"Login failed for {username}") + + elif data['type'] == 'register': + username = data['username'] + password = data['password'] + if len(username) < 3 or len(password) < 3: + client.send(json.dumps({'type': 'register_fail', 'reason': 'Username and password must be at least 3 characters'}).encode('utf-8')) + elif self.register_user(username, password): + client.send(json.dumps({'type': 'register_success'}).encode('utf-8')) + print(f"New user registered: {username}") + else: + client.send(json.dumps({'type': 'register_fail', 'reason': 'Username already taken'}).encode('utf-8')) + + elif data['type'] == 'message': + sender = data['sender'] + receiver = data['receiver'] + msg = data['message'] + is_group = data['is_group'] + self.save_message(sender, receiver, msg, is_group) + if is_group: + for member in self.groups.get(receiver, set()): + if member in self.clients and member != sender: + try: + self.clients[member].send(json.dumps({ + 'type': 'message', + 'sender': sender, + 'receiver': receiver, + 'message': msg, + 'is_group': True + }).encode('utf-8')) + except: + pass + else: + if receiver in self.clients: + try: + self.clients[receiver].send(json.dumps({ + 'type': 'message', + 'sender': sender, + 'receiver': receiver, + 'message': msg, + 'is_group': False + }).encode('utf-8')) + except: + pass + + elif data['type'] == 'create_group': + group_name = data['group_name'] + if group_name not in self.groups: + self.groups[group_name] = set() + for user in data['members']: + if user in self.clients or self.user_exists(user): + self.groups[group_name].add(user) + self.send_group_list_to_all() + client.send(json.dumps({'type': 'group_created', 'group': group_name}).encode('utf-8')) + else: + client.send(json.dumps({'type': 'group_exists', 'group': group_name}).encode('utf-8')) + + except json.JSONDecodeError: + print(f"Invalid JSON received from {address}") + break + except ConnectionResetError: + print(f"Connection reset by {address}") + break + except Exception as e: + print(f"Error handling client {address}: {str(e)}") + break + + except Exception as e: + print(f"Client handler error for {address}: {str(e)}") + finally: + if username and username in self.clients: + print(f"User {username} disconnected") + del self.clients[username] + for group in self.groups.values(): + if username in group: + group.remove(username) + self.send_user_list() + try: + client.close() + except: + pass def start(self): print(f"Server started on {self.host}:{self.port}") - while True: - client, address = self.server.accept() - thread = threading.Thread(target=self.handle_client, args=(client, address)) - thread.start() + print("Database initialized at chat_server.db") + try: + while True: + client, address = self.server.accept() + thread = threading.Thread(target=self.handle_client, args=(client, address)) + thread.daemon = True + thread.start() + except KeyboardInterrupt: + print("\nServer is shutting down...") + finally: + for client in self.clients.values(): + try: + client.close() + except: + pass + self.server.close() + self.db.close() + print("Server stopped") if __name__ == "__main__": server = ChatServer() diff --git a/附加API服务器.py b/附加API服务器.py index 46d4428..025fa25 100644 --- a/附加API服务器.py +++ b/附加API服务器.py @@ -2,7 +2,7 @@ from flask import Flask, request, jsonify import socket import json import threading -#from datetime import datetime +from datetime import datetime app = Flask(__name__)