提问人:Halil Deniz 提问时间:11/5/2023 最后编辑:Halil Deniz 更新时间:11/5/2023 访问量:26
在聊天应用程序中创建私人群组聊天室
Creating a private group room in the chat application
问:
我正在编写一个聊天应用程序。连接到客户端的每个人都通过编写他们想要的消息来相互聊天。但是我想添加创建私人组的功能。
客户端聊天创建私人房间后,我所在的房间应该写在我的名字旁边。 加入聊天室后,未加入聊天室的人员不应交换消息。 但是加入房间后,程序崩溃了。 即使我离开房间,我也无法发送消息等。
import os
import socket
import logging
import hashlib
import argparse
import threading
from datetime import datetime
from colorama import init, Fore, Style
clients = {}
clients_lock = threading.Lock()
# Groups dictionary to keep track of groups
groups = {}
groups_lock = threading.Lock()
class Group:
def __init__(self, name, password=None):
self.name = name
self.password = password # Store the hashed password
self.members = set()
def add_member(self, client_handler):
with groups_lock:
self.members.add(client_handler)
def remove_member(self, client_handler):
with groups_lock:
self.members.remove(client_handler)
def send_to_all(self, message, from_member):
with groups_lock:
for member in self.members:
if member != from_member:
member.client_socket.send(message.encode('utf-8'))
def log_setup(loglevel, logfile):
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError(f"Invalid log level: {loglevel}")
logging.basicConfig(level=numeric_level,
format="%(asctime)s [%(levelname)s] - %(message)s",
handlers=[logging.FileHandler(logfile),
logging.StreamHandler()])
class ClientHandler(threading.Thread):
def __init__(self, client_socket):
threading.Thread.__init__(self)
self.client_socket = client_socket
self.username = None
self.group = None
def run(self):
global clients
logging.info(f"New client connected: {self.client_socket.getpeername()}") # Eklenen log
# Ask for and validate the username
while True:
try:
self.client_socket.send("Enter your username: ".encode('utf-8'))
username = self.client_socket.recv(1024).decode('utf-8').strip()
with clients_lock:
if username in clients or not username:
self.client_socket.send(
"This username is already taken or invalid. Please enter a different name.".encode('utf-8'))
continue # After sending the error message, return to the beginning of the loop
else:
self.username = username
clients[self.username] = self.client_socket
self.client_socket.send("Username set successfully.".encode('utf-8'))
break
except BrokenPipeError as e:
if e.errno == 32:
pass
else:
print(f"An unknown error occurred: {e}")
return
# Process messages
try:
while True:
message = self.client_socket.recv(1024).decode('utf-8')
if message == "/userlist":
with clients_lock:
userlist = "\n".join([f"\t{i + 1}) {user}" for i, user in enumerate(clients.keys())])
response = f"Connected Users:\n{userlist}"
self.client_socket.send(response.encode('utf-8'))
continue
if message == "/help":
response = Fore.BLUE + "Help Menu:\n" \
"\t/help -> Help Menu\n" \
"\t/exit -> Exit the program.\n" \
"\t/userlist -> View the list of connected users.\n" \
"\t/dm [user] [message] -> Send a direct message to a user.\n" \
"\t/changeuser [new_username] -> Change your username.\n" \
"\t/creategroup [group_name] [password] -> Create a new group with an optional password.\n" \
"\t/joingroup [group_name] [password] -> Join an existing group with the correct password.\n" \
"\t/leavegroup -> Leave the current group.\n" \
"\t/listgroups -> List all available groups.\n" + Style.RESET_ALL
self.client_socket.send(response.encode('utf-8'))
continue
if message.startswith("/changeuser "):
_, new_username = message.split()
with clients_lock:
if new_username in clients:
self.client_socket.send(
"This username is already taken. Please choose another one.".encode('utf-8'))
else:
# Remove the old username and add the new one
del clients[self.username]
self.username = new_username
clients[self.username] = self.client_socket
self.client_socket.send(f"Username changed to {new_username}.".encode('utf-8'))
continue
if message.startswith("/dm "):
_, recipient, *dm_msg_parts = message.split()
dm_message = " ".join(dm_msg_parts)
with clients_lock:
if recipient in clients:
clients[recipient].send(f"[DM from {self.username}] {dm_message}".encode('utf-8'))
self.client_socket.send(f"[DM to {recipient}] {dm_message}".encode('utf-8'))
else:
self.client_socket.send("Specified user not found.".encode('utf-8'))
continue
""" this part is the group start start"""
if message.startswith("/creategroup "):
_, group_name, *password_parts = message.split()
password = " ".join(password_parts)
hashed_password = hashlib.sha256(password.encode('utf-8')).hexdigest() if password else None
group = Group(group_name, hashed_password)
with groups_lock:
groups[group_name] = group
group.add_member(self)
self.group = group
self.client_socket.send(f"Group '{group_name}' created.".encode('utf-8'))
continue
if message.startswith("/joingroup "):
_, group_name, *password_parts = message.split()
password = " ".join(password_parts)
hashed_password = hashlib.sha256(password.encode('utf-8')).hexdigest() if password else None
with groups_lock:
group = groups.get(group_name)
if group:
print(f"Group found: {group_name}") # Debug print
if group.password is None or group.password == hashed_password:
group.add_member(self)
self.group = group
print(f"Sending join confirmation to {self.client_socket}") # Debug print
self.client_socket.send(f"Joined group '{group_name}'.".encode('utf-8'))
else:
print(f"Password mismatch for group: {group_name}") # Debug print
self.client_socket.send("Failed to join group: Wrong password.".encode('utf-8'))
else:
print(f"Group not found: {group_name}") # Debug print
self.client_socket.send("Failed to join group: Group does not exist.".encode('utf-8'))
continue
if message == "/leavegroup":
if self.group:
self.group.remove_member(self)
self.client_socket.send(f"Left group '{self.group.name}'.".encode('utf-8'))
self.group = None
else:
self.client_socket.send("You are not in a group.".encode('utf-8'))
continue
if message == "/listgroups":
with groups_lock:
group_list = ["name".ljust(20) + "password"]
for i, (group_name, group) in enumerate(groups.items(), 1):
indicator = '*' if group.password else ''
group_list.append(f"{i}) {group_name.ljust(20)}{indicator}")
response = "Available Groups:\n\t" + "\n\t".join(group_list)
self.client_socket.send(response.encode('utf-8'))
continue
""" and this part is the group stop"""
if not message or message == "/exit":
break
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
broadcast_message = f"[{current_time}] {self.username}: {message}"
with clients_lock:
for usr, client in clients.items():
if usr != self.username:
client.send(broadcast_message.encode('utf-8'))
except:
pass
# Cleanup when the client exits
with clients_lock:
del clients[self.username]
self.client_socket.close()
def start_server(host, port):
try:
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
host_ip, host_port = server_socket.getsockname()
server_socket.listen(5)
print("Server started. Waiting for clients...")
print(f"{Fore.YELLOW}Host information: {Style.RESET_ALL}{host_ip}:{host_port}")
logging.info(f"Server started on {host_ip}:{host_port}") # Eklenen log
while True:
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
client_socket, client_address = server_socket.accept()
print(f"[{current_time}] {client_address} Connected.")
logging.info(f"Accepted connection from {client_address}") # Eklenen log
handler = ClientHandler(client_socket)
handler.start()
except OSError as e:
if e.errno == 98:
print("Address already in use, you wild thing :D")
logging.error("Address already in use") # Eklenen log
else:
print(f"An error occurred while starting the server: {e}")
logging.error(f"An error occurred: {e}") # Eklenen log
except KeyboardInterrupt:
print("Program terminated.....")
logging.info("Server was terminated by keyboard interrupt") # Eklenen log
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Start the chat server.")
parser.add_argument("--host", default="0.0.0.0", help="The IP address to bind the server to. (Default: 0.0.0.0)")
parser.add_argument("--port", type=int, default=12345, help="The port number to bind the server to. (Default: 12345)")
parser.add_argument("--loglevel", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],help="Set the logging level (Default: INFO)")
parser.add_argument("--logfile", default="server.log", help="Set the log file name. (Default: server.log")
args = parser.parse_args()
log_setup(args.loglevel, args.logfile) # Log ayarlarını başlatma
start_server(args.host, args.port)
和 client.py
import socket
import argparse
import threading
from colorama import init, Fore, Style
init(autoreset=True)
def start_client(host, port):
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host, port))
message_lock = threading.Lock()
except ConnectionRefusedError as e:
if e.errno == 111:
print("Connection refused")
else:
print(f"An unknown error occurred {e}")
return
# Firstly, get the username
while True:
username_prompt = client_socket.recv(1024).decode('utf-8')
print(Fore.CYAN + username_prompt, end="")
username = input()
client_socket.send(username.encode('utf-8'))
response = client_socket.recv(1024).decode('utf-8')
if "Please enter a different name." not in response:
break
print(Fore.RED + response)
print(Fore.BLUE + "Help Menu:")
print("\t/help -> Help menu")
# Listen for messages from the server
def listen_to_server():
nonlocal username # Make sure we can modify the outer scope's username variable
while True:
data = client_socket.recv(1024).decode('utf-8')
if not data:
break
with message_lock:
# Check for username change confirmation and update if found
if "Username changed to " in data:
username = data.split("Username changed to ")[1].rstrip(".") # Extract the new username
print(f"{Fore.GREEN}\n{data}\n{Style.RESET_ALL}{username}:{Fore.YELLOW} Enter your message: {Style.RESET_ALL}",end='')
else:
print(f"{Fore.GREEN}\n{data}\n{Style.RESET_ALL}{username}:{Fore.YELLOW} Enter your message: {Style.RESET_ALL}",end='')
threading.Thread(target=listen_to_server, daemon=True).start()
while True:
try:
print(f"{username}: {Fore.YELLOW}Enter your message: {Style.RESET_ALL}", end='')
message = input()
if message == "/exit":
client_socket.send(message.encode('utf-8'))
break
client_socket.send(message.encode('utf-8'))
except ConnectionRefusedError as e:
if e.errno == 111:
print("Connection refused")
else:
print(f"An unknown error occurred {e}")
except KeyboardInterrupt:
print(Fore.RED + "\nClosing connection...")
client_socket.send("/exit".encode('utf-8'))
break
client_socket.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Connect to the chat server.")
parser.add_argument("--host", default="127.0.0.1", help="The server's IP address.")
parser.add_argument("--port", type=int, default=12345, help="The port number of the server.")
args = parser.parse_args()
start_client(args.host, args.port)
答: 暂无答案
下一个:未正确填充数据库
评论