- Tornado 服务器:负责处理 WebSocket 连接、维护在线用户列表、以及广播消息。
- 前端 HTML/JavaScript:提供一个简单的聊天界面,用于连接服务器、发送和接收消息。
我们将使用 WebSocket 协议,因为它能实现服务器与客户端之间的全双工、低延迟通信,非常适合聊天应用。

第 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()
代码解释:

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 步:运行和测试
-
启动服务器: 打开终端,进入
chat_server.py和index.html所在的目录,运行:
(图片来源网络,侵删)python chat_server.py
你会看到 "聊天服务器已启动,访问 http://localhost:8888" 的提示。
-
打开客户端: 打开一个或多个浏览器窗口(推荐 Chrome 或 Firefox),在地址栏输入
http://localhost:8888并回车。 -
测试聊天:
- 在第一个窗口中输入 "大家好!" 并发送。
- 在第二个窗口中,你应该能看到第一条消息 "大家好!"。
- 在第二个窗口中输入 "我是新来的",第一个窗口也应该能看到。
- 关闭其中一个浏览器窗口,另一个窗口会立即收到 "用户 ... 离开了聊天室" 的系统消息。
这样,你就成功搭建了一个功能完整的、基于 Tornado 的实时聊天应用!
