修复数据库

This commit is contained in:
DZY 2025-05-31 19:28:43 +08:00
parent 0870a2f923
commit b9b2af664a
3 changed files with 207 additions and 261 deletions

View File

@ -118,6 +118,7 @@ class ChatClient:
return return
try: try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect(('localhost', 5555)) self.socket.connect(('localhost', 5555))
self.socket.send(json.dumps({ self.socket.send(json.dumps({
'type': 'login', 'type': 'login',
@ -131,7 +132,7 @@ class ChatClient:
self.update_ui_state(True) self.update_ui_state(True)
threading.Thread(target=self.receive_messages, daemon=True).start() threading.Thread(target=self.receive_messages, daemon=True).start()
else: else:
messagebox.showerror("Error", "Login failed") messagebox.showerror("Error", "Login failed - Invalid username or password")
self.socket.close() self.socket.close()
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"Connection error: {str(e)}") messagebox.showerror("Error", f"Connection error: {str(e)}")
@ -154,11 +155,12 @@ class ChatClient:
}).encode('utf-8')) }).encode('utf-8'))
response = json.loads(temp_socket.recv(1024).decode('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() 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: except Exception as e:
messagebox.showerror("Error", f"Connection error: {str(e)}") messagebox.showerror("Error", f"Connection error: {str(e)}")
@ -195,6 +197,10 @@ class ChatClient:
msg['is_group'], msg['is_group'],
msg['timestamp'] msg['timestamp']
) )
elif data['type'] == 'server_shutdown':
messagebox.showinfo("Server", "Server is shutting down")
self.on_closing()
break
except Exception as e: except Exception as e:
print(f"Error receiving message: {e}") print(f"Error receiving message: {e}")
break break
@ -202,7 +208,7 @@ class ChatClient:
def display_message(self, sender, receiver, message, is_group, timestamp=None): def display_message(self, sender, receiver, message, is_group, timestamp=None):
timestamp = timestamp or datetime.now().strftime("%Y-%m-%d %H:%M:%S") 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 \ (not is_group and ((sender == self.current_chat and receiver == self.current_user) or \
(sender == self.current_user and receiver == self.current_chat))): (sender == self.current_user and receiver == self.current_chat))):
@ -259,6 +265,9 @@ class ChatClient:
self.message_entry.delete(0, tk.END) self.message_entry.delete(0, tk.END)
def show_create_group_dialog(self): def show_create_group_dialog(self):
if not self.current_user:
return
dialog = tk.Toplevel(self.root) dialog = tk.Toplevel(self.root)
dialog.title("Create Group") dialog.title("Create Group")
dialog.geometry("300x300") 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)) ttk.Button(button_frame, text="Cancel", command=dialog.destroy).pack(side=tk.RIGHT, padx=(0, 5))
def on_closing(self): def on_closing(self):
if self.current_user: if hasattr(self, 'socket') and self.current_user:
self.socket.close() try:
self.socket.close()
except:
pass
self.root.destroy() self.root.destroy()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -3,12 +3,14 @@ import threading
import json import json
import sqlite3 import sqlite3
from datetime import datetime from datetime import datetime
import os
class ChatServer: class ChatServer:
def __init__(self, host='0.0.0.0', port=5555): def __init__(self, host='0.0.0.0', port=5555):
self.host = host self.host = host
self.port = port self.port = port
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 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.bind((host, port))
self.server.listen() self.server.listen()
self.clients = {} self.clients = {}
@ -16,258 +18,52 @@ class ChatServer:
self.setup_database() self.setup_database()
def setup_database(self): def setup_database(self):
# 确保数据库文件存在并初始化
db_exists = os.path.exists('chat_server.db')
self.db = sqlite3.connect('chat_server.db', check_same_thread=False) self.db = sqlite3.connect('chat_server.db', check_same_thread=False)
cursor = self.db.cursor() cursor = self.db.cursor()
# 创建用户表
cursor.execute(''' cursor.execute('''
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE, username TEXT UNIQUE NOT NULL,
password TEXT password TEXT NOT NULL
) )
''') ''')
# 创建消息表
cursor.execute(''' cursor.execute('''
CREATE TABLE IF NOT EXISTS messages ( CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
sender TEXT, sender TEXT NOT NULL,
receiver TEXT, receiver TEXT NOT NULL,
message TEXT, message TEXT NOT NULL,
timestamp DATETIME, timestamp DATETIME NOT NULL,
is_group INTEGER 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() 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): def authenticate_user(self, username, password):
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute('SELECT * FROM users WHERE username=? AND password=?', (username, password)) cursor.execute('SELECT * FROM users WHERE username=? AND password=?', (username, password))
return cursor.fetchone() is not None return cursor.fetchone() is not None
def register_user(self, username, password): def register_user(self, username, password):
if not username or not password:
return False
try: try:
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, password)) cursor.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, password))
@ -287,18 +83,24 @@ class ChatServer:
def send_user_list(self): def send_user_list(self):
user_list = list(self.clients.keys()) user_list = list(self.clients.keys())
for client in self.clients.values(): for client in self.clients.values():
client.send(json.dumps({ try:
'type': 'user_list', client.send(json.dumps({
'users': user_list 'type': 'user_list',
}).encode('utf-8')) 'users': user_list
}).encode('utf-8'))
except:
pass
def send_group_list(self, username): def send_group_list(self, username):
group_list = [group for group, members in self.groups.items() if username in members] group_list = [group for group, members in self.groups.items() if username in members]
if username in self.clients: if username in self.clients:
self.clients[username].send(json.dumps({ try:
'type': 'group_list', self.clients[username].send(json.dumps({
'groups': group_list 'type': 'group_list',
}).encode('utf-8')) 'groups': group_list
}).encode('utf-8'))
except:
pass
def send_group_list_to_all(self): def send_group_list_to_all(self):
for username in self.clients: for username in self.clients:
@ -308,11 +110,12 @@ class ChatServer:
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute(''' cursor.execute('''
SELECT sender, receiver, message, timestamp, is_group FROM messages SELECT sender, receiver, message, timestamp, is_group FROM messages
WHERE receiver=? OR (is_group=1 AND receiver IN ( WHERE receiver=? OR sender=? OR (is_group=1 AND receiver IN (
SELECT group_name FROM groups WHERE member=? SELECT name FROM sqlite_master WHERE type='table' AND name='groups'
)) OR sender=? ))
ORDER BY timestamp ORDER BY timestamp
''', (username, username, username)) LIMIT 100
''', (username, username))
messages = [] messages = []
for sender, receiver, message, timestamp, is_group in cursor.fetchall(): for sender, receiver, message, timestamp, is_group in cursor.fetchall():
@ -325,17 +128,148 @@ class ChatServer:
}) })
if username in self.clients: if username in self.clients:
self.clients[username].send(json.dumps({ try:
'type': 'initial_messages', self.clients[username].send(json.dumps({
'messages': messages 'type': 'initial_messages',
}).encode('utf-8')) '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): def start(self):
print(f"Server started on {self.host}:{self.port}") print(f"Server started on {self.host}:{self.port}")
while True: print("Database initialized at chat_server.db")
client, address = self.server.accept() try:
thread = threading.Thread(target=self.handle_client, args=(client, address)) while True:
thread.start() 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__": if __name__ == "__main__":
server = ChatServer() server = ChatServer()

View File

@ -2,7 +2,7 @@ from flask import Flask, request, jsonify
import socket import socket
import json import json
import threading import threading
#from datetime import datetime from datetime import datetime
app = Flask(__name__) app = Flask(__name__)