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