杰瑞科技汇

python tornado 聊天

  1. Tornado 服务器:负责处理 WebSocket 连接、维护在线用户列表、以及广播消息。
  2. 前端 HTML/JavaScript:提供一个简单的聊天界面,用于连接服务器、发送和接收消息。

我们将使用 WebSocket 协议,因为它能实现服务器与客户端之间的全双工、低延迟通信,非常适合聊天应用。

python tornado 聊天-图1
(图片来源网络,侵删)

第 1 步:安装 Tornado

你需要安装 Tornado 库,如果尚未安装,请打开终端或命令行运行:

pip install tornado

第 2 步:创建 Tornado 服务器代码

我们将创建一个名为 chat_server.py 的文件,这个服务器将管理所有客户端连接,并将消息广播给所有在线用户。

chat_server.py

import tornado.ioloop
import tornado.web
import tornado.websocket
# 用于存储所有已连接的客户端
# 这是一个列表,每个元素是一个 WebSocketHandler 实例
clients = []
class ChatHandler(tornado.websocket.WebSocketHandler):
    """
    处理 WebSocket 连接的核心类。
    """
    def open(self):
        """
        当新的 WebSocket 连接建立时被调用。
        """
        print(f"新用户连接: {self.request.remote_ip}")
        clients.append(self)
        # 广播新用户加入的消息
        self.broadcast_message(f"系统消息: 用户 {self.request.remote_ip} 加入了聊天室。")
    def on_message(self, message):
        """
        当从客户端收到消息时被调用。
        """
        print(f"收到消息: {message}")
        # 广播收到的消息给所有客户端
        self.broadcast_message(message)
    def on_close(self):
        """
        当 WebSocket 连接关闭时被调用。
        """
        print(f"用户断开连接: {self.request.remote_ip}")
        clients.remove(self)
        # 广播用户离开的消息
        self.broadcast_message(f"系统消息: 用户 {self.request.remote_ip} 离开了聊天室。")
    def broadcast_message(self, message):
        """
        向所有已连接的客户端广播消息的辅助方法。
        """
        for client in clients:
            try:
                client.write_message(message)
            except:
                # 如果向某个客户端发送消息失败(它已经断开连接),
                # 我们可以将其从列表中移除。
                # 这里简单打印错误,实际应用中可能需要更健壮的错误处理。
                print(f"向客户端 {client.request.remote_ip} 发送消息失败。")
# Tornado 应用配置
app = tornado.web.Application([
    (r"/", tornado.web.RedirectHandler, url="/index.html"),  # 根路径重定向到首页
    (r"/index.html", tornado.web.StaticFileHandler, {"path": ".", "default_filename": "index.html"}), # 提供前端页面
    (r"/ws", ChatHandler),  # WebSocket 连接路径
])
if __name__ == "__main__":
    app.listen(8888)  # 监听 8888 端口
    print("聊天服务器已启动,访问 http://localhost:8888")
    tornado.ioloop.IOLoop.current().start()

代码解释:

python tornado 聊天-图2
(图片来源网络,侵删)
  • clients = []: 一个全局列表,用来保存所有当前连接的客户端实例。
  • ChatHandler(tornado.websocket.WebSocketHandler): 这是处理所有 WebSocket 逻辑的核心类。
    • open(): 当有新用户连接到 /ws 路径时,此方法被调用,我们将新的客户端实例添加到 clients 列表中,并广播一条欢迎消息。
    • on_message(message): 当服务器从客户端接收到消息时,此方法被调用,我们调用 broadcast_message 将消息发送给所有人。
    • on_close(): 当客户端断开连接时(例如关闭浏览器标签页),此方法被调用,我们从 clients 列表中移除该客户端,并广播一条离开消息。
    • broadcast_message(message): 一个辅助方法,遍历 clients 列表,并向每个客户端发送消息。
  • tornado.web.Application: 创建 Tornado 应用。
    • (r"/ws", ChatHandler): 将所有访问 /ws 的 WebSocket 请求交给 ChatHandler 处理。
    • (r"/index.html", ...): 配置静态文件服务,让用户能访问我们的 index.html
  • app.listen(8888): 启动服务器,监听本地的 8888 端口。

第 3 步:创建前端 HTML/JavaScript 页面

在同一目录下,创建一个名为 index.html 的文件,这个文件包含聊天室的界面和连接服务器的 JavaScript 代码。

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">Tornado 聊天室</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f0f2f5;
        }
        #chat-container {
            max-width: 600px;
            margin: 0 auto;
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            overflow: hidden;
            display: flex;
            flex-direction: column;
            height: 80vh;
        }
        #chat-header {
            background-color: #007bff;
            color: white;
            padding: 15px;
            text-align: center;
            font-weight: bold;
        }
        #chat-messages {
            flex-grow: 1;
            padding: 15px;
            overflow-y: auto;
            border-bottom: 1px solid #ddd;
        }
        .message {
            margin-bottom: 10px;
            padding: 8px 12px;
            border-radius: 15px;
            max-width: 80%;
            word-wrap: break-word;
        }
        .message.system {
            background-color: #e9ecef;
            text-align: center;
            font-style: italic;
            color: #6c757d;
        }
        #chat-input-container {
            padding: 15px;
            display: flex;
        }
        #chat-input {
            flex-grow: 1;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 20px;
            outline: none;
        }
        #send-button {
            margin-left: 10px;
            padding: 10px 20px;
            border: none;
            background-color: #007bff;
            color: white;
            border-radius: 20px;
            cursor: pointer;
            transition: background-color 0.2s;
        }
        #send-button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <div id="chat-container">
        <div id="chat-header">
            <h1>Tornado 聊天室</h1>
        </div>
        <div id="chat-messages"></div>
        <div id="chat-input-container">
            <input type="text" id="chat-input" placeholder="输入消息..." />
            <button id="send-button">发送</button>
        </div>
    </div>
    <script>
        // 连接到 Tornado 服务器
        // 使用 ws:// 协议,注意端口要和服务器一致
        const socket = new WebSocket("ws://localhost:8888/ws");
        const chatMessages = document.getElementById('chat-messages');
        const chatInput = document.getElementById('chat-input');
        const sendButton = document.getElementById('send-button');
        // 连接成功时触发
        socket.onopen = function(event) {
            console.log("WebSocket 连接已建立!");
            addSystemMessage("你已成功连接到聊天室。");
        };
        // 收到服务器消息时触发
        socket.onmessage = function(event) {
            console.log("收到服务器消息:", event.data);
            addMessage(event.data);
        };
        // 连接关闭时触发
        socket.onclose = function(event) {
            if (event.wasClean) {
                console.log(`WebSocket 连接已正常关闭,代码=${event.code} 原因=${event.reason}`);
            } else {
                console.error("WebSocket 连接异常关闭");
            }
            addSystemMessage("与服务器断开连接。");
        };
        // 连接出错时触发
        socket.onerror = function(error) {
            console.error("WebSocket 错误:", error);
            addSystemMessage("连接发生错误。");
        };
        // 发送消息
        function sendMessage() {
            const message = chatInput.value.trim();
            if (message && socket.readyState === WebSocket.OPEN) {
                socket.send(message);
                chatInput.value = ''; // 清空输入框
            }
        }
        // 将消息添加到聊天窗口
        function addMessage(message) {
            const messageElement = document.createElement('div');
            messageElement.classList.add('message');
            messageElement.textContent = message;
            chatMessages.appendChild(messageElement);
            chatMessages.scrollTop = chatMessages.scrollHeight; // 自动滚动到底部
        }
        // 将系统消息添加到聊天窗口
        function addSystemMessage(message) {
            const messageElement = document.createElement('div');
            messageElement.classList.add('message', 'system');
            messageElement.textContent = message;
            chatMessages.appendChild(messageElement);
            chatMessages.scrollTop = chatMessages.scrollHeight;
        }
        // 绑定发送按钮点击事件
        sendButton.addEventListener('click', sendMessage);
        // 绑定回车键发送消息
        chatInput.addEventListener('keypress', function(event) {
            if (event.key === 'Enter') {
                sendMessage();
            }
        });
    </script>
</body>
</html>

前端代码解释:

  • HTML/CSS: 创建了一个简单的聊天界面,包含消息显示区域 (chat-messages) 和消息输入框 (chat-input)。
  • JavaScript:
    • new WebSocket("ws://localhost:8888/ws"): 创建一个 WebSocket 连接,指向我们服务器的 /ws 路径。
    • socket.onopen, socket.onmessage, socket.onclose, socket.onerror: 这些是 WebSocket 对象的事件监听器,分别在连接建立、收到消息、连接关闭和发生错误时执行。
    • sendMessage(): 获取输入框中的文本,通过 socket.send() 发送到服务器,并清空输入框。
    • addMessage()addSystemMessage(): 动态创建 <div> 元素来显示消息,并将其添加到聊天窗口中。addSystemMessage 用于显示系统提示(如用户加入/离开)。
    • 事件监听器:为发送按钮和输入框绑定事件,实现点击发送和按回车发送的功能。

第 4 步:运行和测试

  1. 启动服务器: 打开终端,进入 chat_server.pyindex.html 所在的目录,运行:

    python tornado 聊天-图3
    (图片来源网络,侵删)
    python chat_server.py

    你会看到 "聊天服务器已启动,访问 http://localhost:8888" 的提示。

  2. 打开客户端: 打开一个或多个浏览器窗口(推荐 Chrome 或 Firefox),在地址栏输入 http://localhost:8888 并回车。

  3. 测试聊天:

    • 在第一个窗口中输入 "大家好!" 并发送。
    • 在第二个窗口中,你应该能看到第一条消息 "大家好!"。
    • 在第二个窗口中输入 "我是新来的",第一个窗口也应该能看到。
    • 关闭其中一个浏览器窗口,另一个窗口会立即收到 "用户 ... 离开了聊天室" 的系统消息。

这样,你就成功搭建了一个功能完整的、基于 Tornado 的实时聊天应用!

分享:
扫描分享到社交APP
上一篇
下一篇