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
繁體地址,請注明出處:http://hk.pswp.cn/news/908673.shtml
英文地址,請注明出處:http://en.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…