tornado.websocket — 與瀏覽器雙向通訊

WebSocket 協議的實作。

WebSockets 允許瀏覽器和伺服器之間的雙向通訊。所有主流瀏覽器的當前版本都支援 WebSockets。

此模組實作了 RFC 6455 中定義的最終版本 WebSocket 協議。

在 4.0 版本變更: 移除對 draft 76 協議版本的支援。

class tornado.websocket.WebSocketHandler(application: Application, request: HTTPServerRequest, **kwargs: Any)[原始碼]

繼承此類別以建立基本的 WebSocket 處理器。

覆寫 on_message 以處理傳入訊息,並使用 write_message 將訊息傳送至客戶端。您也可以覆寫 openon_close 以處理已開啟和已關閉的連線。

可以覆寫 set_default_headersprepare 來傳送自訂升級回應標頭。

有關 JavaScript 介面的詳細資訊,請參閱 http://dev.w3.org/html5/websockets/。該協議在 http://tools.ietf.org/html/rfc6455 中指定。

以下是一個 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 連線。「交握」是 HTTP,但在交握之後,協議是以訊息為基礎的。因此,Tornado HTTP 的大多數功能在此類型的處理器中都不可用。您唯一可用的通訊方法是 write_message()ping()close()。同樣地,您的請求處理器類別應該實作 open() 方法,而不是 get()post()

如果您在應用程式中將上面的處理器對應到 /websocket,您可以使用 JavaScript 以以下方式呼叫它

var ws = new WebSocket("ws://127.0.0.1:8888/websocket");
ws.onopen = function() {
   ws.send("Hello, world");
};
ws.onmessage = function (evt) {
   alert(evt.data);
};

此腳本會彈出一個警告框,顯示「You said: Hello, world」。

Web 瀏覽器允許任何網站開啟與任何其他網站的 websocket 連線,而不是使用管理來自 JavaScript 的其他網路存取的同源策略。這可能會令人驚訝,並且是一個潛在的安全漏洞,因此自 Tornado 4.0 起,WebSocketHandler 需要希望接收跨域 websockets 的應用程式,透過覆寫 check_origin 方法來選擇加入(有關詳細資訊,請參閱該方法的說明文件)。當建立 websocket 連線時,未能這麼做是最有可能導致 403 錯誤的原因。

當使用帶有自我簽署憑證的安全 websocket 連線 (wss://) 時,來自瀏覽器的連線可能會失敗,因為它想要顯示「接受此憑證」對話方塊,但沒有地方可以顯示它。您必須先使用相同的憑證瀏覽一般的 HTML 頁面以接受它,websocket 連線才會成功。

如果應用程式設定 websocket_ping_interval 有非零的值,將會定期傳送 ping,並且如果在 websocket_ping_timeout 之前沒有收到回應,則連線將會關閉。

不接受大於 websocket_max_message_size 應用程式設定(預設值為 10MiB)的訊息。

在 4.5 版本變更: 新增 websocket_ping_intervalwebsocket_ping_timeoutwebsocket_max_message_size

事件處理器

WebSocketHandler.open(*args: str, **kwargs: str) Optional[Awaitable[None]][原始碼]

當新的 WebSocket 開啟時呼叫。

open 的參數是從 tornado.web.URLSpec 正規表示式中擷取的,就像 tornado.web.RequestHandler.get 的參數一樣。

open 可以是協程。on_message 將在 open 傳回之後才會被呼叫。

在 5.1 版本變更: open 可以是協程。

WebSocketHandler.on_message(message: Union[str, bytes]) Optional[Awaitable[None]][原始碼]

處理 WebSocket 上的傳入訊息

必須覆寫此方法。

在 4.5 版本變更: on_message 可以是協程。

WebSocketHandler.on_close() None[原始碼]

當 WebSocket 關閉時呼叫。

如果連線已正常關閉,且提供了狀態碼或原因短語,這些值將可作為屬性 self.close_codeself.close_reason 使用。

在 4.0 版本中變更:新增了 close_codeclose_reason 屬性。

WebSocketHandler.select_subprotocol(subprotocols: List[str]) Optional[str][原始碼]

覆寫此方法以實作子協定的協商。

subprotocols 是由客戶端提出的子協定字串列表。可以覆寫此方法以傳回其中一個字串來選擇它,或者傳回 None 表示不選擇子協定。

未能選擇子協定不會自動中止連線,儘管如果沒有選擇客戶端提出的任何子協定,客戶端可能會關閉連線。

列表可以為空,在這種情況下,此方法必須傳回 None。即使沒有提出任何子協定,此方法也會被呼叫一次,以便處理程序可以得知此事實。

在 5.1 版本中變更:先前,如果客戶端沒有提出任何子協定,則會使用包含空字串的列表而不是空列表呼叫此方法。

WebSocketHandler.selected_subprotocol

select_subprotocol 傳回的子協定。

在 5.1 版本中新增。

WebSocketHandler.on_ping(data: bytes) None[原始碼]

當收到 ping 幀時調用。

輸出

WebSocketHandler.write_message(message: Union[bytes, str, Dict[str, Any]], binary: bool = False) Future[None][原始碼]

將指定訊息傳送給此 WebSocket 的客戶端。

訊息可以是字串或字典(將會編碼為 json)。如果 binary 引數為 false,則訊息將以 utf8 傳送;在二進位模式下,允許任何位元組字串。

如果連線已關閉,則會引發 WebSocketClosedError。傳回可用於流程控制的 Future

在 3.2 版本中變更:新增了 WebSocketClosedError(先前關閉的連線會引發 AttributeError)。

在 4.3 版本中變更:傳回可用於流程控制的 Future

在 5.0 版本中變更:一致地引發 WebSocketClosedError。先前有時會引發 StreamClosedError

WebSocketHandler.close(code: Optional[int] = None, reason: Optional[str] = None) None[原始碼]

關閉此 WebSocket。

一旦關閉交握成功,socket 就會關閉。

code 可以是數值狀態碼,取自 RFC 6455 第 7.4.1 節中定義的值。reason 可以是關於連線關閉原因的文字訊息。這些值會提供給客戶端,但不會被 WebSocket 協定以其他方式解譯。

在 4.0 版本中變更:新增了 codereason 引數。

組態

WebSocketHandler.check_origin(origin: str) bool[原始碼]

覆寫此方法以啟用對允許替代來源的支持。

origin 參數是 Origin HTTP 標頭的值,即啟動此請求的 URL。對於未傳送此標頭的客戶端,不會呼叫此方法;這些請求始終被允許(因為所有實作 WebSocket 的瀏覽器都支援此標頭,而且非瀏覽器客戶端沒有相同的跨網站安全考量)。

應返回 True 以接受請求,或返回 False 以拒絕請求。預設情況下,會拒絕所有來自非此主機的來源的請求。

這是一種安全保護,可防止瀏覽器上的跨網站腳本攻擊,因為 WebSocket 允許繞過通常的同源策略,並且不使用 CORS 標頭。

警告

這是一項重要的安全措施;請務必瞭解其安全含義,切勿停用它。特別是,如果您的身份驗證是基於 Cookie 的,則您必須限制 check_origin() 允許的來源,或為 WebSocket 連接實作您自己的類似 XSRF 的保護。請參閱這些文章以獲取更多資訊。

若要接受所有跨來源流量(這是 Tornado 4.0 之前的預設行為),只需覆寫此方法使其始終返回 True 即可。

def check_origin(self, origin):
    return True

若要允許來自您網站任何子網域的連線,您可以執行如下操作:

def check_origin(self, origin):
    parsed_origin = urllib.parse.urlparse(origin)
    return parsed_origin.netloc.endswith(".mydomain.com")

4.0 版本新增。

WebSocketHandler.get_compression_options() Optional[Dict[str, Any]][原始碼]

覆寫此方法以返回連線的壓縮選項。

如果此方法返回 None(預設值),則會停用壓縮。如果它返回一個字典(即使是空的),則會啟用壓縮。字典的內容可用於控制以下壓縮選項

compression_level 指定壓縮級別。

mem_level 指定用於內部壓縮狀態的記憶體量。

4.1 版本新增。

在 4.5 版本中變更:新增了 compression_levelmem_level

WebSocketHandler.set_nodelay(value: bool) None[原始碼]

為此串流設定無延遲標誌。

預設情況下,可能會延遲和/或合併小訊息,以盡量減少傳送的封包數量。由於 Nagle 演算法和 TCP 延遲 ACK 之間的交互作用,這有時會導致 200-500 毫秒的延遲。若要減少此延遲(可能會以增加頻寬使用量為代價),請在建立 WebSocket 連線後呼叫 self.set_nodelay(True)

有關其他詳細資訊,請參閱 BaseIOStream.set_nodelay

3.1 版本新增。

其他

WebSocketHandler.ping(data: Union[str, bytes] = b'') None[原始碼]

傳送 ping 框架到遠端。

data 參數允許傳送少量資料(最多 125 個位元組)作為 ping 訊息的一部分。請注意,並非所有 WebSocket 實作都會將此資料公開給應用程式。

請考慮使用 websocket_ping_interval 應用程式設定,而不是手動傳送 ping。

在 5.1 版本中變更:data 參數現在是選填的。

WebSocketHandler.on_pong(data: bytes) None[原始碼]

當收到對 ping 框架的回應時呼叫。

exception tornado.websocket.WebSocketClosedError[原始碼]

對已關閉的連線執行操作時引發。

3.2 版本新增。

用戶端支援

tornado.websocket.websocket_connect(url: Union[str, HTTPRequest], callback: Optional[Callable[[Future[WebSocketClientConnection]], None]] = None, connect_timeout: Optional[float] = None, on_message_callback: Optional[Callable[[Union[None, str, bytes]], None]] = None, compression_options: Optional[Dict[str, Any]] = None, ping_interval: Optional[float] = None, ping_timeout: Optional[float] = None, max_message_size: int = 10485760, subprotocols: Optional[List[str]] = None, resolver: Optional[Resolver] = None) Awaitable[WebSocketClientConnection][原始碼]

客戶端 WebSocket 支援。

接收一個 URL 並回傳一個 Future,其結果為一個 WebSocketClientConnection

compression_options 的解讀方式與 WebSocketHandler.get_compression_options 的回傳值相同。

此連線支援兩種操作方式。在協程模式中,應用程式通常會在迴圈中呼叫 read_message

conn = yield websocket_connect(url)
while True:
    msg = yield conn.read_message()
    if msg is None: break
    # Do something with msg

在回呼模式中,將一個 on_message_callback 傳遞給 websocket_connect。在這兩種模式中,None 的訊息表示連線已關閉。

subprotocols 可以是一個字串列表,指定建議的子協定。當連線完成時,可以在連線物件的 selected_subprotocol 屬性上找到選定的協定。

在 3.2 版本變更: 也接受 HTTPRequest 物件來取代 URL。

在 4.1 版本變更: 新增了 compression_optionson_message_callback

在 4.5 版本變更: 新增了 ping_intervalping_timeoutmax_message_size 引數,其含義與 WebSocketHandler 中相同。

在 5.0 版本變更: 已移除 io_loop 引數(自 4.1 版本起已棄用)。

在 5.1 版本變更: 新增了 subprotocols 引數。

在 6.3 版本變更: 新增了 resolver 引數。

class tornado.websocket.WebSocketClientConnection(request: HTTPRequest, on_message_callback: Optional[Callable[[Union[None, str, bytes]], None]] = None, compression_options: Optional[Dict[str, Any]] = None, ping_interval: Optional[float] = None, ping_timeout: Optional[float] = None, max_message_size: int = 10485760, subprotocols: Optional[List[str]] = None, resolver: Optional[Resolver] = None)[source]

WebSocket 用戶端連線。

此類別不應直接實例化;請改用 websocket_connect 函數。

close(code: Optional[int] = None, reason: Optional[str] = None) None[原始碼]

關閉 WebSocket 連線。

codereason 的說明文件請見 WebSocketHandler.close

3.2 版本新增。

在 4.0 版本中變更:新增了 codereason 引數。

write_message(message: Union[str, bytes, Dict[str, Any]], binary: bool = False) Future[None][原始碼]

傳送訊息到 WebSocket 伺服器。

如果串流已關閉,則會引發 WebSocketClosedError。傳回一個 Future,可用於流程控制。

在 5.0 版本變更: 在關閉的串流上引發的例外狀況,從 StreamClosedError 變更為 WebSocketClosedError

read_message(callback: Optional[Callable[[Future[Union[None, str, bytes]]], None]] = None) Awaitable[Union[None, str, bytes]][原始碼]

從 WebSocket 伺服器讀取訊息。

如果在 WebSocket 初始化時指定了 on_message_callback,則此函數永遠不會回傳訊息。

返回一個 Future,其結果為訊息,如果連線關閉則返回 None。如果提供了 callback 參數,則會在 Future 準備好時使用該 Future 呼叫它。

ping(data: bytes = b'') None[原始碼]

傳送 ping 框架到遠端。

data 參數允許傳送少量資料(最多 125 個位元組)作為 ping 訊息的一部分。請注意,並非所有 WebSocket 實作都會將此資料公開給應用程式。

請考慮使用 ping_interval 參數到 websocket_connect,而不是手動發送 ping。

在 5.1 版本中新增。

property selected_subprotocol: Optional[str]

伺服器選定的子協定。

在 5.1 版本中新增。