From ad9142dced062a77de9cf778a7004f4db9c203aa Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 18 Sep 2025 16:55:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client1.py | 126 ++++++++++++++++++++++++++++--------------------- client2.py | 80 +++++++++++++++++++++++++++++++- server.py | 134 ++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 248 insertions(+), 92 deletions(-) diff --git a/client1.py b/client1.py index 5ba04e0..d5f72fa 100644 --- a/client1.py +++ b/client1.py @@ -1,59 +1,79 @@ -import json import socket +import threading +import json +def listen_for_messages(sock): + """后台线程:监听并显示消息""" + while True: + try: + data = sock.recv(1024).decode("utf-8") + if not data: + break + resp = json.loads(data) + if resp["status"] == "msg": + print(f"\n📩 {resp['from']} 发来消息: {resp['msg']}") + print("请输入消息(q退出): ", end="", flush=True) + elif resp["status"] == "system": + print(f"\n👥 在线用户更新: {resp['users']}") + print("请输入消息(q退出): ", end="", flush=True) + except: + break -def chat(username, chat_obj, content): - ''' - 发送聊天信息 - ''' - pass - - -# 创建socket -ip_port = ("127.0.0.1", 9000) -sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - -# 选择菜单 -while 1: - print("menu".center(30, "=")) - print("1.login".center(30, " ")) - print("2.register".center(30, " ")) - print("=".center(30, "=")) - choice = input(">>>") - - if choice == "1": - username = input("用户名:") - password = input("密码:") - data = { - "option": "login", - "username": username, - "password": password - } - sk.sendto(json.dumps(data).encode("utf-8"), ip_port) - msg, addr = sk.recvfrom(1024) - # {"status": 1} - if json.loads(msg.decode("utf-8"))["status"]: - chat_obj = input("输入收件人:") - content = input("输入内容:") - chat(username, chat_obj, content) - continue +def login_or_register(sock): + while True: + print("1. 登录") + print("2. 注册") + choice = input("请选择: ") + username = input("用户名: ") + password = input("密码: ") + if choice == "1": + sock.send(json.dumps({"action": "login", "username": username, "password": password}).encode("utf-8")) else: - print("登录失败") + sock.send(json.dumps({"action": "register", "username": username, "password": password}).encode("utf-8")) + resp = json.loads(sock.recv(1024).decode("utf-8")) + print(resp["msg"]) + if resp["status"] == "ok" and choice == "1": + return username + +def chat(sock, username): + while True: + # 请求在线用户 + sock.send(json.dumps({"action": "list_users"}).encode("utf-8")) + resp = json.loads(sock.recv(1024).decode("utf-8")) + users = resp.get("users", []) + if not users: + print("⚠️ 暂无其他在线用户") continue - elif choice == "2": - username = input("用户名:") - password = input("密码:") - data = { - "option": "register", - "username": username, - "password": password - } - sk.sendto(json.dumps(data).encode("utf-8"), ip_port) - msg, addr = sk.recvfrom(1024) - # {"status": 1} - if json.loads(msg.decode("utf-8"))["status"]: - print("注册成功") - continue - else: - print(json.loads(msg.decode("utf-8"))["msg"]) + print("\n=== 在线用户 ===") + for i, u in enumerate(users): + print(f"{i+1}. {u}") + choice = input("选择聊天对象编号(q退出): ") + if choice.lower() == "q": + break + try: + target = users[int(choice)-1] + except: + print("无效选择") continue + + # 聊天循环 + while True: + msg = input("请输入消息(q退出): ") + if msg.lower() == "q": + break + sock.send(json.dumps({"action": "send", "to": target, "msg": msg}).encode("utf-8")) + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("127.0.0.1", 8888)) + + username = login_or_register(sock) + + # 开启监听线程 + threading.Thread(target=listen_for_messages, args=(sock,), daemon=True).start() + + # 主线程执行聊天逻辑 + chat(sock, username) + +if __name__ == "__main__": + main() diff --git a/client2.py b/client2.py index 44c2d4d..d5f72fa 100644 --- a/client2.py +++ b/client2.py @@ -1 +1,79 @@ -import socket \ No newline at end of file +import socket +import threading +import json + +def listen_for_messages(sock): + """后台线程:监听并显示消息""" + while True: + try: + data = sock.recv(1024).decode("utf-8") + if not data: + break + resp = json.loads(data) + if resp["status"] == "msg": + print(f"\n📩 {resp['from']} 发来消息: {resp['msg']}") + print("请输入消息(q退出): ", end="", flush=True) + elif resp["status"] == "system": + print(f"\n👥 在线用户更新: {resp['users']}") + print("请输入消息(q退出): ", end="", flush=True) + except: + break + +def login_or_register(sock): + while True: + print("1. 登录") + print("2. 注册") + choice = input("请选择: ") + username = input("用户名: ") + password = input("密码: ") + if choice == "1": + sock.send(json.dumps({"action": "login", "username": username, "password": password}).encode("utf-8")) + else: + sock.send(json.dumps({"action": "register", "username": username, "password": password}).encode("utf-8")) + resp = json.loads(sock.recv(1024).decode("utf-8")) + print(resp["msg"]) + if resp["status"] == "ok" and choice == "1": + return username + +def chat(sock, username): + while True: + # 请求在线用户 + sock.send(json.dumps({"action": "list_users"}).encode("utf-8")) + resp = json.loads(sock.recv(1024).decode("utf-8")) + users = resp.get("users", []) + if not users: + print("⚠️ 暂无其他在线用户") + continue + print("\n=== 在线用户 ===") + for i, u in enumerate(users): + print(f"{i+1}. {u}") + choice = input("选择聊天对象编号(q退出): ") + if choice.lower() == "q": + break + try: + target = users[int(choice)-1] + except: + print("无效选择") + continue + + # 聊天循环 + while True: + msg = input("请输入消息(q退出): ") + if msg.lower() == "q": + break + sock.send(json.dumps({"action": "send", "to": target, "msg": msg}).encode("utf-8")) + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("127.0.0.1", 8888)) + + username = login_or_register(sock) + + # 开启监听线程 + threading.Thread(target=listen_for_messages, args=(sock,), daemon=True).start() + + # 主线程执行聊天逻辑 + chat(sock, username) + +if __name__ == "__main__": + main() diff --git a/server.py b/server.py index 7808ff4..13621e3 100644 --- a/server.py +++ b/server.py @@ -1,41 +1,99 @@ -import json import socket +import threading +import json +import os -# 创建socket -sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -sk.bind(("0.0.0.0", 9000)) -active = [] -db = {} -chat_msg = {} +DB_FILE = "chat_db.json" +ONLINE_USERS = {} # {username: conn} -while 1: - print(f"当前活跃用户:{active} 当前注册用户:{db}") - data, addr = sk.recvfrom(2048) - data = json.loads(data) - res = {"status": 0} - if data["option"] == "login": - username = data["username"] - password = data["password"] - if username in db: - if db[username] == password: - res = {"status": 1} - active.append(data["username"]) - else: - res = {"status": 0} - else: - res = {"status": 0} - elif data["option"] == "register": - username = data["username"] - password = data["password"] - if username in db: - res = {"status": 0, "msg": "用户名已存在"} - else: - db[username] = password - res = {"status": 1, "msg": "注册成功"} - elif data["option"] == "chat": - # 接受聊天信息,并且存入聊天临时字典 - pass - elif data["option"] == "hello": - # 接受客户端的存活状态,如果发现临时聊天字典有该用户的未收消息,发送过去 - pass - sk.sendto(json.dumps(res).encode("utf-8"), addr) \ No newline at end of file +# 初始化数据库 +if not os.path.exists(DB_FILE): + with open(DB_FILE, "w") as f: + json.dump({"users": {}, "messages": []}, f) + +def load_db(): + with open(DB_FILE, "r") as f: + return json.load(f) + +def save_db(data): + with open(DB_FILE, "w") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + +def handle_client(conn, addr): + username = None + try: + while True: + data = conn.recv(1024).decode("utf-8") + if not data: + break + req = json.loads(data) + action = req.get("action") + + if action == "register": + db = load_db() + users = db["users"] + if req["username"] in users: + conn.send(json.dumps({"status": "fail", "msg": "用户已存在"}).encode("utf-8")) + else: + users[req["username"]] = req["password"] + save_db(db) + conn.send(json.dumps({"status": "ok", "msg": "注册成功"}).encode("utf-8")) + + elif action == "login": + db = load_db() + users = db["users"] + if users.get(req["username"]) == req["password"]: + username = req["username"] + ONLINE_USERS[username] = conn + conn.send(json.dumps({"status": "ok", "msg": "登录成功"}).encode("utf-8")) + broadcast_users() + else: + conn.send(json.dumps({"status": "fail", "msg": "用户名或密码错误"}).encode("utf-8")) + + elif action == "list_users": + users = list(ONLINE_USERS.keys()) + if username in users: + users.remove(username) # 移除自己 + conn.send(json.dumps({"status": "ok", "users": users}).encode("utf-8")) + + elif action == "send": + to_user = req["to"] + msg = req["msg"] + db = load_db() + db["messages"].append({"from": username, "to": to_user, "msg": msg}) + save_db(db) + if to_user in ONLINE_USERS: + ONLINE_USERS[to_user].send( + json.dumps({"status": "msg", "from": username, "msg": msg}).encode("utf-8") + ) + else: + conn.send(json.dumps({"status": "fail", "msg": "对方不在线"}).encode("utf-8")) + + except Exception as e: + print(f"错误: {e}") + finally: + if username and username in ONLINE_USERS: + del ONLINE_USERS[username] + broadcast_users() + conn.close() + +def broadcast_users(): + users = list(ONLINE_USERS.keys()) + for user, conn in ONLINE_USERS.items(): + ulist = [u for u in users if u != user] + try: + conn.send(json.dumps({"status": "system", "users": ulist}).encode("utf-8")) + except: + pass + +def main(): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.bind(("0.0.0.0", 8888)) + server.listen(5) + print("服务器已启动,监听 8888 端口...") + while True: + conn, addr = server.accept() + threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start() + +if __name__ == "__main__": + main()