python聊天服务器用户发送的消息不转发给其他用户
问题是服务器启动后第一个客户端连接服务器,给服务器发送消息都没问题,但是第二个客户端连接时如果任何一个客户端发送消息,服务端不会转发消息给其他客户端
服务器代码
import os
import socket
import multiprocessing
import datetime
class ChatServer:
def __init__(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.host = "127.0.0.1"
self.port = 12345
self.clients = {}
self.running = False
self.log_filename = None
def start(self):
try:
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(5)
self.running = True
self.create_log_file()
print(f"服务器已启动,监听 {self.host}:{self.port}")
multiprocessing.Process(target=self.accept_clients).start()
except Exception as e:
print(f"启动服务器失败: {e}")
def remove_client(self, client_socket):
if client_socket in self.clients:
del self.clients[client_socket]
client_socket.close()
def accept_clients(self):
while self.running:
try:
client_socket, client_address = self.server_socket.accept()
print(f"连接来自 {client_address}")
multiprocessing.Process(target=self.handle_client, args=(client_socket, client_address)).start()
self.broadcast_message(f"用户 {client_address[0]} 已加入服务器")
except OSError as e:
if self.running:
print(f"服务器已停止接受新的客户端连接:{e}")
break
def handle_client(self, client_socket, client_address):
username = client_address[0]
self.clients[client_socket] = username
try:
while True:
message = client_socket.recv(1024).decode()
if not message:
break
print(f"收到来自 {username} 的消息:{message}")
self.log_message(f"收到来自 {username} 的消息:{message}")
if message.startswith("/ip"):
ip_port_msg = f"您的IP地址和端口为:{client_address[0]}:{client_address[1]}"
client_socket.send(ip_port_msg.encode())
elif message.startswith("/stop"):
parts = message.split(" ")
if len(parts) >= 2:
password = parts[1]
self.prompt_password_and_stop(client_socket, password)
break
else:
client_socket.send("密码错误,请重新输入。".encode())
elif message.startswith("/list"):
self.send_user_list(client_socket)
self.output_user_list()
elif message.startswith("/kick"):
self.prompt_password_and_kick(client_socket, message)
elif message.startswith("/history"):
self.send_chat_history(client_socket)
else:
self.broadcast_message(f"{username}: {message}", sender=client_socket)
except ConnectionResetError:
print(f"客户端 {username} 异常断开连接")
finally:
if client_socket in self.clients:
del self.clients[client_socket]
client_socket.close()
def prompt_password_and_stop(self, client_socket, password):
if password == "1234":
self.stop()
client_socket.send("服务器已停止。".encode())
self.log_message("管理员停止了服务器")
else:
client_socket.send("密码错误,请重新输入。".encode())
def output_user_list(self):
print("在线用户列表:")
for user in self.clients.values():
print(user)
def broadcast_message(self, message, sender=None):
self.log_message(message)
for client_socket, username in list(self.clients.items()):
if client_socket != sender:
try:
client_socket.send(message.encode())
except (ConnectionResetError, socket.error) as e:
print(f"与客户端 {username} 的连接意外断开:{e}")
self.remove_client(client_socket)
def log_message(self, message):
with open(self.log_filename, "a") as f:
f.write(f"{datetime.datetime.now()} - {message}\n")
def stop(self):
self.running = False
self.server_socket.close()
def send_user_list(self, client_socket):
user_list = "在线用户:\n"
for user in self.clients.values():
user_list += f"- {user}\n"
client_socket.send(user_list.encode())
def prompt_password_and_kick(self, client_socket, message):
parts = message.split(" ")
if len(parts) >= 4 and parts[-1] == "1234":
target_username = parts[1]
kick_reason = " ".join(parts[2:-1])
for sock, username in list(self.clients.items()):
if username == target_username:
sock.send(f"您已被管理员踢出服务器,原因:{kick_reason}".encode())
del self.clients[sock]
sock.close()
print(f"用户 {target_username} 已被踢出服务器。")
self.log_message(f"管理员踢出用户 {target_username},原因:{kick_reason}")
return
break
client_socket.send(f"未找到用户 {target_username}。".encode())
else:
client_socket.send("命令格式错误或密码错误。".encode())
def send_chat_history(self, client_socket):
try:
with open(self.log_filename, "r") as f:
chat_history = f.read()
client_socket.send(chat_history.encode())
except FileNotFoundError:
client_socket.send("聊天记录不存在。".encode())
def create_log_file(self):
log_dir = "logs"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
self.log_filename = os.path.join(log_dir, f"server_log_{timestamp}.txt")
def main():
server = ChatServer()
server.start()
if __name__ == "__main__":
main()
客户端代码:
import socket
import threading
import sys
class ChatClient:
def __init__(self):
self.server_ip = "127.0.0.1"
self.server_port = 12345
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.running = False
def connect_to_server(self):
try:
self.client_socket.connect((self.server_ip, self.server_port))
self.running = True
threading.Thread(target=self.receive_messages).start()
print("连接到服务器成功!")
except ConnectionRefusedError:
print("连接被拒绝。服务器可能未启动或者无法连接。")
def receive_messages(self):
while self.running:
try:
message = self.client_socket.recv(1024).decode()
if not message:
print("与服务器的连接已断开。")
self.running = False
break # 服务器关闭连接时退出循环
print(message)
except ConnectionResetError:
print("与服务器的连接意外断开。")
self.running = False
break
def send_message(self, message):
try:
self.client_socket.send(message.encode())
except ConnectionAbortedError:
print("与服务器的连接意外断开。")
self.running = False
def reconnect(self):
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect_to_server()
def leave(self):
self.running = False
self.client_socket.close()
print("已离开聊天室。")
def main():
client = ChatClient()
client.connect_to_server()
while client.running:
message = input()
if message == "/leave":
client.leave()
sys.exit()
elif message == "/reconnect":
client.reconnect()
else:
client.send_message(message)
if __name__ == "__main__":
main()
关于 LearnKu
The attribute
self.clientsin targetself.handle_clientfor eachmultiprocessing.Processis not the same variable and not sharing, so you will always get empty value, no matter how many clients connected, then no client will receive the broadcast message.Similar case
Example Code (Maybe not a good method for your case)