ChatRoom

这是一个基于Python的聊天室服务器程序,它使用socket和ssl库来创建安全的网络连接。服务器支持多个客户端同时连接,并使用线程处理每个客户端的请求。客户端之间可以通过加密的消息进行通信,消息内容在发送前会被加密,接收后再解密。服务器还提供了在线人数统计功能,可以实时更新在线人数信息。

Server
import socket
import ssl
import threading

from cryptography.fernet import Fernet

# 全局变量
clients = {}
addresses = {}
clients_lock = threading.Lock()

# 服务器配置
HOST = '127.0.0.1'
PORT = 5555


def generate_key():
    return Fernet.generate_key()


def handle_client(client_socket, addr):
    try:
        key = generate_key()
        print(f"密钥:{key}")
        cipher_suite = Fernet(key)
        client_socket.send(key)
        print(f"密钥已发送给 {addr}")

        encrypted_nickname = client_socket.recv(1024)
        nickname = cipher_suite.decrypt(encrypted_nickname).decode('utf-8')
        print(f"{addr} 的昵称是 {nickname}")
        welcome_message = f"欢迎 {nickname} 进入聊天室!输入 'quit' 退出。当前在线人数: {len(clients) + 1}/10"
        client_socket.send(cipher_suite.encrypt(welcome_message.encode('utf-8')))
        print(f"已发送欢迎消息给 {nickname}")

        # 使用线程锁 (clients_lock) 来确保多个线程在访问和修改这些共享资源时不会出现数据不一致的问题。
        with clients_lock:
            clients[client_socket] = (nickname, cipher_suite)
            addresses[client_socket] = addr

        broadcast(f"{nickname} 加入了聊天室!")
        update_online_count()

        while True:
            encrypted_message = client_socket.recv(1024)
            if not encrypted_message:
                break

            decrypted_message = cipher_suite.decrypt(encrypted_message).decode('utf-8')
            if decrypted_message.lower() == 'quit':
                client_socket.close()
                with clients_lock:
                    del clients[client_socket]
                broadcast(f"{nickname} 离开了聊天室。")
                update_online_count()
                break
            else:
                broadcast(f"{nickname}: {decrypted_message}", client_socket)

    except Exception as e:
        print(f"处理客户端 {addr} 出错: {e}")


def broadcast(message, sender_socket=None):
    with clients_lock:
        for client_socket, (nickname, cipher_suite) in clients.items():
            if client_socket != sender_socket:
                try:
                    encrypted_message = cipher_suite.encrypt(message.encode('utf-8'))
                    client_socket.send(encrypted_message)
                except Exception as e:
                    print(f"无法发送消息给 {nickname}: {e}")


def update_online_count():
    with clients_lock:
        count_message = f"当前在线人数: {len(clients)}"
        print(count_message)
        for client_socket, (nickname, cipher_suite) in clients.items():
            try:
                encrypted_message = cipher_suite.encrypt(count_message.encode('utf-8'))
                client_socket.send(encrypted_message)
            except Exception as e:
                print(f"无法发送在线人数消息给 {nickname}: {e}")


def main():
    try:
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind((HOST, PORT))
        server.listen(10)
        print(f"服务器启动在 {HOST}:{PORT}")

        context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        context.load_cert_chain(certfile="D:/path/to/server_cert.pem", keyfile="D:/path/to/server_key.pem")
        context.load_verify_locations("D:/path/to/ca_cert.pem")
        print("SSL 上下文已加载")

        while True:
            print("等待新连接...")
            client_socket, client_addr = server.accept()
            print(f"新连接来自 {client_addr}")
            try:
                secure_socket = context.wrap_socket(client_socket, server_side=True)
                print(f"已建立 SSL 连接 {client_addr}")
                client_thread = threading.Thread(target=handle_client, args=(secure_socket, client_addr))
                client_thread.start()
            except ssl.SSLError as e:
                print(f"SSL 错误: {e}")
                client_socket.close()

    except Exception as e:
        print(f"服务器启动出错: {e}")


if __name__ == "__main__":
    main()



Client
import socket
import ssl
import threading

from cryptography.fernet import Fernet

SERVER = '127.0.0.1'
PORT = 5555


def receive(client_socket, cipher_suite):
    try:
        while True:
            encrypted_message = client_socket.recv(1024)
            print(f"密文:{encrypted_message}")
            if not encrypted_message:
                break
            decrypted_message = cipher_suite.decrypt(encrypted_message).decode('utf-8')
            print(decrypted_message)

    except Exception as e:
        print(f"接收或解密消息出错: {e}")


def send(client_socket, cipher_suite):
    try:
        while True:
            message = input()
            encrypted_message = cipher_suite.encrypt(message.encode('utf-8'))
            client_socket.send(encrypted_message)
            if message.lower() == 'quit':
                break

    except Exception as e:
        print(f"发送或加密消息出错: {e}")


def main():
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
        context.load_verify_locations("D:/path/to/ca_cert.pem")
        context.check_hostname = False  # 关闭主机名验证
        context.verify_mode = ssl.CERT_NONE  # 禁用服务器证书验证

        secure_socket = context.wrap_socket(client, server_hostname=SERVER)
        print("Connecting to the server...")
        secure_socket.connect((SERVER, PORT))
        print("Connected to the server")

        key = secure_socket.recv(1024)
        cipher_suite = Fernet(key)
        print(f"收到密钥: {key}")

        nickname = input("输入您的昵称: ")
        encrypted_nickname = cipher_suite.encrypt(nickname.encode('utf-8'))
        secure_socket.send(encrypted_nickname)

        encrypted_welcome_message = secure_socket.recv(1024)
        welcome_message = cipher_suite.decrypt(encrypted_welcome_message).decode('utf-8')
        print(welcome_message)

        receive_thread = threading.Thread(target=receive, args=(secure_socket, cipher_suite))
        receive_thread.start()

        send_thread = threading.Thread(target=send, args=(secure_socket, cipher_suite))
        send_thread.start()

        send_thread.join()
        receive_thread.join()

    except Exception as e:
        print(f"连接服务器出错: {e}")


if __name__ == "__main__":
    main()

完整项目下载:
https://github.com/ChenKangluit/Chat-Room

posted @ 2024-06-29 23:50  敬畏虚空  阅读(7)  评论(0编辑  收藏  举报