Tornado WebSocket实时聊天实例

在 Python 3 Tornado 中使用 WebSocket 非常直接。你需要创建一个继承自 tornado.websocket.WebSocketHandler 的类,并实现它的几个关键方法。

下面是一个简单的示例,演示了如何创建一个 WebSocket 服务器,该服务器会接收客户端发送的消息,并在其前面加上 "Echo: " 前缀后回显给客户端。同时,它还会将收到的消息广播给所有连接的客户端。

1. 服务器端 (Python - server.py)

import tornado.ioloop
import tornado.web
import tornado.websocket
import logging
import uuid # 用于给客户端一个唯一ID (可选)logging.basicConfig(level=logging.INFO)class ChatSocketHandler(tornado.websocket.WebSocketHandler):# 使用一个类级别的集合来存储所有活动的 WebSocket 连接clients = set()client_details = {} # 可选:存储客户端更多信息def open(self):"""当一个新的 WebSocket 连接建立时调用"""self.client_id = str(uuid.uuid4()) # 给每个连接一个唯一IDChatSocketHandler.clients.add(self)ChatSocketHandler.client_details[self] = {"id": self.client_id}logging.info(f"New client connected: {self.client_id} from {self.request.remote_ip}")self.write_message(f"Welcome! Your ID is {self.client_id}")self.broadcast(f"Client {self.client_id} has joined.", exclude_self=True)def on_message(self, message):"""当从客户端接收到消息时调用"""logging.info(f"Received message from {self.client_id}: {message}")# 简单的回显# self.write_message(f"You said: {message}")# 广播消息给所有客户端self.broadcast(f"{self.client_id} says: {message}")def on_close(self):"""当 WebSocket 连接关闭时调用"""ChatSocketHandler.clients.remove(self)if self in ChatSocketHandler.client_details:del ChatSocketHandler.client_details[self]logging.info(f"Client {self.client_id} disconnected.")self.broadcast(f"Client {self.client_id} has left.", exclude_self=True)def check_origin(self, origin):"""允许跨域 WebSocket 连接。在生产环境中,你应该更严格地检查 origin。例如:allowed_origins = ["http://localhost:8000", "https://yourdomain.com"]return origin in allowed_origins"""logging.info(f"Checking origin: {origin}")return True # 暂时允许所有来源@classmethoddef broadcast(cls, message, exclude_self=False, sender=None):"""辅助方法,向所有连接的客户端广播消息"""logging.info(f"Broadcasting message: {message}")for client in cls.clients:if exclude_self and sender and client == sender:continuetry:client.write_message(message)except tornado.websocket.WebSocketClosedError:logging.warning(f"Failed to send to a closed socket for client {cls.client_details.get(client, {}).get('id', 'unknown')}")except Exception as e:logging.error(f"Error sending message to client {cls.client_details.get(client, {}).get('id', 'unknown')}: {e}")def make_app():return tornado.web.Application([(r"/ws", ChatSocketHandler), # 将 /ws 路径映射到处理器])if __name__ == "__main__":app = make_app()port = 8888app.listen(port)logging.info(f"WebSocket server started on port {port}")tornado.ioloop.IOLoop.current().start()

2. 客户端 (HTML + JavaScript - client.html)

<!DOCTYPE html>
<html>
<head><title>Tornado WebSocket Chat</title><style>#chatbox {width: 400px;height: 300px;border: 1px solid #ccc;overflow-y: scroll;padding: 10px;margin-bottom: 10px;}.message {margin-bottom: 5px;}</style>
</head>
<body><h1>Tornado WebSocket Chat</h1><div id="chatbox"></div><input type="text" id="messageInput" placeholder="Type your message here..." size="50"><button onclick="sendMessage()">Send</button><script>const chatbox = document.getElementById('chatbox');const messageInput = document.getElementById('messageInput');// 确保 WebSocket URL 与服务器端配置一致const socket = new WebSocket("ws://localhost:8888/ws"); socket.onopen = function(event) {addMessageToChatbox("System: Connected to WebSocket server.");console.log("WebSocket connection opened:", event);};socket.onmessage = function(event) {console.log("Message from server:", event.data);addMessageToChatbox("Server: " + event.data);};socket.onclose = function(event) {if (event.wasClean) {addMessageToChatbox(`System: Connection closed cleanly, code=${event.code} reason=${event.reason}`);} else {addMessageToChatbox('System: Connection died');}console.log("WebSocket connection closed:", event);};socket.onerror = function(error) {addMessageToChatbox("System: WebSocket Error: " + error.message);console.error("WebSocket Error:", error);};function sendMessage() {const message = messageInput.value;if (message.trim() !== "") {socket.send(message);// addMessageToChatbox("You: " + message); // 也可以等服务器广播回来messageInput.value = "";}}messageInput.addEventListener("keypress", function(event) {if (event.key === "Enter") {sendMessage();}});function addMessageToChatbox(message) {const messageElement = document.createElement('div');messageElement.classList.add('message');messageElement.textContent = message;chatbox.appendChild(messageElement);chatbox.scrollTop = chatbox.scrollHeight; // 自动滚动到底部}</script>
</body>
</html>

如何运行:

  1. 保存文件: 将 Python 代码保存为 server.py,将 HTML 代码保存为 client.html
  2. 安装 Tornado: 如果你还没有安装,请执行:
    pip install tornado
    
  3. 运行服务器: 打开终端或命令提示符,导航到保存 server.py 的目录,然后运行:
    python server.py
    
    你应该会看到类似 “WebSocket server started on port 8888” 的输出。
  4. 打开客户端: 在你的 Web 浏览器中打开 client.html 文件 (可以直接双击文件,或者通过 file:///path/to/client.html 访问)。你可以打开多个浏览器窗口或标签页来模拟多个客户端。

关键点解释:

  • tornado.websocket.WebSocketHandler: 这是处理 WebSocket 连接的核心类。
  • open(): 当客户端成功建立 WebSocket 连接后,此方法被调用。你可以在这里进行初始化操作,比如将客户端实例添加到一个列表中以便后续广播。
  • on_message(message): 当服务器从客户端接收到一条消息时,此方法被调用。message 参数是客户端发送的数据(通常是字符串,也可以配置为接收二进制数据)。
  • on_close(): 当连接关闭时(无论是客户端主动关闭还是服务器关闭,或者由于网络错误),此方法被调用。在这里进行清理工作,比如从活动客户端列表中移除该连接。
  • write_message(message): 此方法用于向连接的客户端发送消息。
  • check_origin(self, origin): 这个方法用于安全目的,决定是否接受来自特定源(origin)的 WebSocket 连接。默认情况下,Tornado 会拒绝跨域的 WebSocket 连接。在开发环境中,返回 True 可以方便测试。在生产环境中,你应该仔细配置允许的源列表以防止 CSRF 类型的攻击。
  • clients = set(): 这是一个类级别的集合,用于跟踪所有当前连接的客户端 WebSocketHandler 实例。这使得向所有客户端广播消息成为可能。
  • broadcast() (自定义方法): 这是一个我们添加的类方法,用于方便地向 clients 集合中的所有客户端发送消息。
  • 客户端 WebSocket API:
    • new WebSocket("ws://localhost:8888/ws"): 创建一个新的 WebSocket 连接。ws:// 表示普通的 WebSocket,如果是加密的,则使用 wss://
    • socket.onopen: 连接成功建立时的回调。
    • socket.onmessage: 收到服务器消息时的回调。event.data 包含消息内容。
    • socket.onclose: 连接关闭时的回调。
    • socket.onerror:发生错误时的回调。
    • socket.send(message): 向服务器发送消息。

进一步的考虑:

  • 错误处理: 更健壮的错误处理,例如 write_message 可能会因为客户端突然断开而失败。
  • 消息格式: 对于复杂应用,通常使用 JSON 作为消息格式,方便传输结构化数据。你需要在服务器端 json.loads() 接收到的消息,并在发送前 json.dumps()
  • 认证与授权: 对于需要用户登录的应用,你需要在 WebSocket 连接建立时(可能通过 open() 或在 HTTP 升级握手阶段)进行用户认证。
  • 状态管理: 对于更复杂的应用,你可能需要在服务器端为每个客户端维护更复杂的状态。
  • 扩展性: 对于大量并发连接,单个 Tornado 进程可能不够。你可能需要考虑使用多个进程(例如通过 supervisord 运行多个 Tornado 实例)并使用像 Redis Pub/Sub 这样的消息队列来在进程间广播 WebSocket 消息。
  • SSL/TLS (wss://): 在生产环境中,务必使用 wss:// (WebSocket Secure) 来加密通信。这需要在 Tornado 应用启动时配置 SSL 选项。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908673.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

模块化架构下的前端调试体系建设:WebDebugX 与多工具协同的工程实践

随着前端工程化的发展&#xff0c;越来越多的项目采用模块化架构&#xff1a;单页面应用&#xff08;SPA&#xff09;、微前端、组件化框架等。这类架构带来了良好的可维护性和复用性&#xff0c;但也带来了新的调试挑战。 本文结合我们在多个模块化项目中的真实经验&#xff…

高考:如何合理选择学科、专业以及职业

如何合理选择学科、专业以及职业 一、自我认知&#xff1a;明确自身兴趣与优势&#xff08;一&#xff09;兴趣探索&#xff08;二&#xff09;能力评估&#xff08;三&#xff09;价值观与目标 二、外部调研&#xff1a;深入了解学科、专业与职业&#xff08;一&#xff09;学…

【新品解读】一板多能,AXRF49 定义新一代 RFSoC FPGA 开发平台

“硬件系统庞杂、调试周期长” “高频模拟前端不稳定&#xff0c;影响采样精度” “接收和发射链路难以同步&#xff0c;难以扩展更多通道” “数据流量大&#xff0c;处理与存储跟不上” 这些是大部分客户在构建多通道、高频宽的射频采样链路时&#xff0c;面临的主要问题。…

实现仿中国婚博会微信小程序

主要功能&#xff1a; 1、完成底部标签导航设计、首页海报轮播效果设计和宫格导航设计&#xff0c;如图1所示 2、在首页里&#xff0c;单击全部分类宫格导航的时候&#xff0c;会进入到全部分类导航界面&#xff0c;把婚博会相关内容的导航集成到一个界面里&#xff0c;如图2…

MySQL强化关键_020_SQL 优化

目 录 一、order by 优化 1.未添加索引 2.添加索引 3.复合索引默认升序排列 4.复合索引降序排列 5.复合索引升序降序排列并用 6.总结 二、group by 优化 1.未添加索引 2.添加索引 3.添加复合索引 三、limit 优化 四、主键优化 1.主键设计原则 五、insert 优化…

湖北理元理律师事务所视角:企业债务优化的三维平衡之道

核心提示&#xff1a;债务优化的本质不是消灭债务&#xff0c;而是在法律框架内重建财务可持续性。 一、企业债务危机的典型误区 某制造企业主曾向我坦言&#xff1a;“用新贷还旧贷3年&#xff0c;债务从200万滚到500万。”这类案例暴露出企业债务处置的共性痛点&#xff1a…

【Ragflow】27.RagflowPlus(v0.4.1):小版本迭代,问题修复与功能优化

概述 RagflowPlus v0.4.0 在发布后&#xff0c;收到了积极的反馈&#xff0c;同时也包含一些问题。 本次进行一轮小版本更新&#xff0c;发布 v0.4.1 版本&#xff0c;对已知问题进行修复&#xff0c;并对部分功能进行进一步优化。 开源地址&#xff1a;https://github.com/…

【hadoop】Flink安装部署

一、单机模式 步骤&#xff1a; 1、使用XFTP将Flink安装包flink-1.13.5-bin-scala_2.11.tgz发送到master机器的主目录。 2、解压安装包&#xff1a; tar -zxvf ~/flink-1.13.5-bin-scala_2.11.tgz 3、修改文件夹的名字&#xff0c;将其改为flume&#xff0c;或者创建软连接…

Linux 下 ChromeDriver 安装

个人博客地址&#xff1a;Linux 下 ChromeDriver 安装 | 一张假钞的真实世界 Selenium 是一个用于 Web 应用程序测试的工具。可以通过它驱动浏览器执行特定的操作&#xff0c;如点击、下滑、资源加载与渲染等。该工具在爬虫开发中也非常有帮助。Selenium 需要通过浏览器驱动操…

Canal环境搭建并实现和ES数据同步

作者&#xff1a;田超凡 日期&#xff1a;2025年6月7日 Canal安装&#xff0c;启动端口11111、8082&#xff1a; 安装canal-deployer服务端&#xff1a; https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…

STM32使用土壤湿度传感器

1.1 介绍&#xff1a; 土壤湿度传感器是一种传感装置&#xff0c;主要用于检测土壤湿度的大小&#xff0c;并广泛应用于汽车自动刮水系统、智能灯光系统和智能天窗系统等。传感器采用优质FR-04双料&#xff0c;大面积5.0 * 4.0厘米&#xff0c;镀镍处理面。 它具有抗氧化&…

锁的艺术:深入浅出讲解乐观锁与悲观锁

在多线程和分布式系统中&#xff0c;数据一致性是一个核心问题。锁机制作为解决并发冲突的重要手段&#xff0c;被广泛应用于各种场景。乐观锁和悲观锁是两种常见的锁策略&#xff0c;它们在设计理念、实现方式和适用场景上各有特点。本文将深入探讨乐观锁和悲观锁的原理、实现…

Jinja2深度解析与应用指南

1. 概念与用途 1.1 核心概念 Jinja2是Python生态中功能强大的模板引擎&#xff0c;采用逻辑与表现分离的设计思想&#xff1a; 模板&#xff1a;包含静态内容和动态占位符的文本文件&#xff08;.j2后缀&#xff09;渲染&#xff1a;将模板与数据结合生成最终文本的过程上下…

Ubuntu20.04中 Redis 的安装和配置

Ubuntu20.04 中 Redis 的安装和配置 Ubuntu 安装 MySQL 及其配置 1. Redis 的安装 更新系统包列表并安装 Redis &#xff1a; # 更新包管理工具 sudo apt update# -y&#xff1a;自动确认所有提示&#xff08;非交互式安装&#xff09; sudo apt install -y redis-server测…

Sklearn 机器学习 缺失值处理 填充数据列的缺失值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 💡使用 Scikit-learn 处理数据缺失值的完整指南 在机器学习项目中,数据缺失是不可避…

Unity中如何播放视频

1.创建一个原始图像并调整布局平铺整个画布 2.创建自定义纹理并调整自定义纹理大小 3.添加视频播放组件 4.将准备好的视频素材拖入到视频剪辑中 5.将自定义纹理拖入到目标纹理中 6.将自定义纹理拖入到原始图像的纹理中 最后运行游戏&#xff0c;即可播放视频 总结&#xff1a;

Spring通用类型转换的实现原理

Spring通用类型转换的实现原理 设计思路实现逻辑ConversionService&#xff1a;类型转换服务入口ConverterRegister&#xff1a;转换器注册接口GenericConversionService1. Map<ConvertiblePair, GenericConverter> converters2. canConvert() 与 convert()&#xff1a;服…

红黑树完全指南:为何工程都用它?原理、实现、场景、误区全解析

红黑树完全指南&#xff1a;为何工程都用它&#xff1f;原理、实现、场景、误区全解析 作者&#xff1a;星之辰 标签&#xff1a;#红黑树 #平衡二叉查找树 #工程实践 #数据结构 #面试宝典 引子&#xff1a;工程师的“性能焦虑”与树的进化史 你以为树只是算法题里的配角&#…

阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库

阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库 最近帮朋友 完成一些运维工作 &#xff0c;这里记录一下。 文章目录 阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库最近帮朋友 完成一些运维工作 &#xff0c;这里记录一下。 阿里云 RDS MySQL 5.7 添加白名单1. 登录…

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…