WebSocket 協(xié)議的實現(xiàn),WebSockets 允許瀏覽器和服務器之間的雙向通信。
所有主流瀏覽器的當前版本都支持 WebSockets,盡管不支持 WebSockets 的舊版本仍在使用
該模塊實現(xiàn)了 RFC 6455 中定義的 WebSocket 協(xié)議的最終版本。某些瀏覽器版本(尤其是 Safari 5.x)實現(xiàn)了該協(xié)議的早期草案(稱為“draft 76”)并且與該模塊不兼容。
在 4.0 版更改: 刪除了對草案 76 協(xié)議版本的支持。
子類化此類以創(chuàng)建基本的 WebSocket 處理程序。
重寫 ?on_message
來處理傳入的消息,并使用 ?write_message
將消息發(fā)送到客戶端。 您還可以覆蓋 ?open
和 ?on_close
來處理打開和關閉的連接。
可以通過覆蓋 ?set_default_headers
或 ?prepare
來發(fā)送自定義升級響應表頭。
這是一個示例 WebSocket 處理程序,它將所有接收到的消息返回給客戶端:
class EchoWebSocket(tornado.websocket.WebSocketHandler):
def open(self):
print("WebSocket opened")
def on_message(self, message):
self.write_message(u"You said: " + message)
def on_close(self):
print("WebSocket closed")
WebSockets 不是標準的 HTTP 連接。WebSocket只是使用HTTP協(xié)議來完成一部分握手,但在握手之后,協(xié)議是基于消息的。 因此,大多數(shù) Tornado HTTP 工具在這種類型的處理程序中不可用。 您可用的唯一通信方法是 ?write_message()
?、?ping()
? 和 ?close()
?。 同樣,您的請求處理程序類應該實現(xiàn) ?open()
? 方法而不是 ?get()
? 或 ?post()
?。
如果您將上面的處理程序映射到應用程序中的 ?/websocket
?,您可以在 JavaScript 中調(diào)用它:
var ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = function() {
ws.send("Hello, world");
};
ws.onmessage = function (evt) {
alert(evt.data);
};
這個腳本會彈出一個警告框,上面寫著“You said:hello,world”。
Web 瀏覽器允許任何站點打開與任何其他站點的 websocket 連接,而不是使用同源策略來管理來自 JavaScript 的其他網(wǎng)絡訪問。 這可能令人驚訝并且是一個潛在的安全漏洞,因此由于 Tornado 4.0 ?WebSocketHandler
要求希望接收跨域 websocket 的應用程序通過覆蓋 ?check_origin
方法來選擇加入。 不這樣做是建立 websocket 連接時最可能導致 403 錯誤的原因。
當使用帶有自簽名證書的安全 websocket 連接 (?wss://
?) 時,來自瀏覽器的連接可能會失敗,因為它想要顯示“接受此證書”對話框但無處顯示。 在 websocket 連接成功之前,您必須首先使用相同的證書訪問常規(guī) HTML 頁面以接受它。
如果應用程序設置 ?websocket_ping_interval
的值為非零值,則會定期發(fā)送 ping,如果在 ?websocket_ping_timeout
之前沒有收到響應,則連接將關閉。
大于 ?websocket_max_message_size
應用程序設置(默認 10MiB)的消息將不被接受。
在 4.5 版更改:添加了 ?websocket_ping_interval
、?websocket_ping_timeout
和 ?websocket_max_message_size
。
打開新的 WebSocket 時調(diào)用。
?open
的參數(shù)是從?tornado.web.URLSpec
正則表達式中提取的,就像tornado.web.RequestHandler.get
的參數(shù)一樣。
?open
可能是一個協(xié)程。 在 ?open
返回之前不會調(diào)用 ?on_message
?。
在 5.1 版更改: ?open
可能是一個協(xié)程。
處理 WebSocket 上的傳入消息
必須重寫此方法。
在 4.5 版更改: ?on_message
?可以是協(xié)程。
當 WebSocket 關閉時調(diào)用。
如果連接完全關閉并且提供了狀態(tài)代碼或原因短語,則這些值將作為屬性用于 ?self.close_code
? 和 ?self.close_reason
?
在 4.0 版更改: 添加了 ?close_code
和 ?close_reason
屬性。
重寫以實現(xiàn)子協(xié)議協(xié)商。
?subprotocols
是標識客戶端提出的子協(xié)議的字符串列表。 可以重寫此方法以返回其中一個字符串以選擇它,或者返回 ?None
以不選擇子協(xié)議。
未能選擇子協(xié)議不會自動中止連接,盡管如果沒有選擇任何建議的子協(xié)議,客戶端可能會關閉連接。
列表可能為空,在這種情況下,此方法必須返回 ?None
?。 即使沒有提出子協(xié)議,該方法也總是只調(diào)用一次,以便可以將這一事實告知處理程序。
在 5.1 版更改: 以前,如果客戶端沒有提出子協(xié)議,則使用包含空字符串而不是空列表的列表調(diào)用此方法。
?select_subprotocol
返回的子協(xié)議。
5.1 版中的新功能。
在收到 ping 幀時調(diào)用。
將給定消息發(fā)送到此 Web Socket 的客戶端。
消息可以是字符串或字典(將被編碼為 json)。 如果 ?binary
參數(shù)為 false,則消息將作為 utf8 發(fā)送; 在二進制模式下,任何字節(jié)串都是允許的。
如果連接已經(jīng)關閉,則引發(fā) ?WebSocketClosedError
?。 返回可用于流量控制的 ?Future
?。
在 3.2 版更改:添加了 ?WebSocketClosedError
?(以前關閉的連接會引發(fā) ?AttributeError
?)
在 4.3 版更改: 返回可用于流量控制的 ?Future
?。
在 5.0 版更改: 持續(xù)引發(fā) ?WebSocketClosedError
?。 以前有時會引發(fā) ?StreamClosedError
?。
關閉此 Web Socket。
一旦關閉握手成功,套接字將被關閉。
?code
可以是數(shù)字狀態(tài)代碼,取自 RFC 6455 第 7.4.1 節(jié)中定義的值。?reason
?可能是關于連接關閉原因的文本消息。 這些值可供客戶端使用,但不會被 websocket 協(xié)議解釋。
在 4.0 版更改: 添加了?code
?和?reason
?參數(shù)。
覆蓋以啟用對允許備用來源的支持。
?origin
參數(shù)是 ?Origin
HTTP 表頭的值,即負責發(fā)起此請求的 url。 不發(fā)送此表頭的客戶端不會調(diào)用此方法; 這樣的請求總是被允許的(因為所有實現(xiàn) WebSockets 的瀏覽器都支持這個表頭,并且非瀏覽器客戶端沒有相同的跨站點安全問題)。
應該返回 ?True
來接受請求或 ?False
來拒絕它。 默認情況下,拒絕所有來自主機以外的主機的請求。
這是針對瀏覽器上的跨站點腳本攻擊的安全保護,因為允許 WebSocket 繞過通常的同源策略并且不使用 CORS 表頭。
注意:這是一項重要的安全措施;不要在不了解安全隱患的情況下禁用它。 特別是,如果您的身份驗證是基于 cookie 的,您要么限制 ?check_origin()
允許的來源,要么為 websocket 連接實現(xiàn)類似 XSRF 的保護。
要接受所有跨域流量(這是 Tornado 4.0 之前的默認設置),只需重寫此方法以始終返回 ?True
?:
def check_origin(self, origin):
return True
要允許來自您網(wǎng)站的任何子域的連接,您可以執(zhí)行以下操作:
def check_origin(self, origin):
parsed_origin = urllib.parse.urlparse(origin)
return parsed_origin.netloc.endswith(".mydomain.com")
覆蓋以返回連接的壓縮選項。
如果此方法返回 None(默認值),壓縮將被禁用。 如果它返回一個 dict(甚至是一個空的),它將被啟用。 dict 的內(nèi)容可用于控制以下壓縮選項:
?compression_level
指定壓縮級別。
?mem_level
指定用于內(nèi)部壓縮狀態(tài)的內(nèi)存量。
為此流設置無延遲標志。
默認情況下,小消息可能會被延遲組合以最小化發(fā)送的數(shù)據(jù)包數(shù)量。 由于 Nagle 的算法和 TCP 延遲 ACK 之間的交互,這有時會導致 200-500 毫秒的延遲。 要減少此延遲(以可能增加帶寬使用為代價),請在建立 websocket 連接后調(diào)用 ?self.set_nodelay(True)
?。
向遠端發(fā)送 ping 幀。
data 參數(shù)允許作為 ping 消息的一部分發(fā)送少量數(shù)據(jù)(最多 125 個字節(jié))。 請注意,并非所有 websocket 實現(xiàn)都會將此數(shù)據(jù)公開給應用程序。
考慮使用 ?websocket_ping_interval
應用程序設置,而不是手動發(fā)送 ping。
在 5.1 版更改: data 參數(shù)現(xiàn)在是可選的。
在收到對 ping 幀的響應時調(diào)用。
由關閉連接上的操作引發(fā)。
接受一個 url 并返回一個 Future,其結果是一個 ?WebSocketClientConnection
?。
?compression_options
?的執(zhí)行方式與 ?WebSocketHandler.get_compression_options
? 的返回值相同。
該連接支持兩種操作方式。 在協(xié)程風格中,應用程序通常會在循環(huán)中調(diào)用 ?read_message
?:
conn = yield websocket_connect(url)
while True:
msg = yield conn.read_message()
if msg is None: break
# Do something with msg
在回調(diào)樣式中,將 ?on_message_callback
?傳遞給 ?websocket_connect
?。 在這兩種樣式中,?None
?消息表示連接已關閉。
?subprotocols
可以是指定提議的子協(xié)議的字符串列表。 當連接完成時,可以在連接對象的 ?selected_subprotocol
屬性上找到所選協(xié)議。
在 3.2 版更改: 也接受 ?HTTPRequest
對象代替 url。
在 4.1 版更改:添加了 ?compression_options
和 ?on_message_callback
。
在 4.5 版更改:添加了 ?ping_interval
、?ping_timeout
和 ?max_message_size
參數(shù),它們與 ?WebSocketHandler
中的含義相同。
在 5.0 版中更改: ?io_loop
參數(shù)(自 4.1 版以來已棄用)已被刪除。
在 5.1 版更改: 添加了 ?subprotocols
參數(shù)。
WebSocket 客戶端連接。
這個類不應該直接實例化; 請改用 ?websocket_connect
?函數(shù)。
關閉 websocket 連接。
?code
?和?reason
?記錄在 ?WebSocketHandler.close
? 下。
向 WebSocket 服務器發(fā)送消息。
如果流已關閉,則引發(fā) ?WebSocketClosedError
?。 返回可用于流量控制的 ?Future
。
在 5.0 版更改:在關閉的流上引發(fā)的異常從 ?StreamClosedError
?更改為 ?WebSocketClosedError
?。
從 WebSocket 服務器讀取消息。
如果在 WebSocket 初始化時指定了 ?on_message_callback
?,則此函數(shù)將永遠不會返回消息
返回結果為?future
?的消息,如果連接已關閉,則返回 ?None
?。 如果給定了回調(diào)參數(shù),它將在準備好時與?future
?一起調(diào)用。
向遠端發(fā)送 ping 幀。
?data
參數(shù)允許作為 ping 消息的一部分發(fā)送少量數(shù)據(jù)(最多 125 個字節(jié))。 請注意,并非所有 websocket 實現(xiàn)都會將此數(shù)據(jù)公開給應用程序。
考慮對 ?websocket_connect
使用 ?ping_interval
參數(shù),而不是手動發(fā)送 ping。
服務器選擇的子協(xié)議
更多建議: