背景
以前,很多網站使用輪詢實現推送技術。輪詢是在特定的的時間間隔(比如1秒),由瀏覽器對服務器發出HTTP request,然后由服務器返回最新的數據給瀏覽器。輪詢的缺點很明顯,瀏覽器需要不斷的向服務器發出請求,然而HTTP請求的header是非常長 的,而實際傳輸的數據可能很小,這就造成了帶寬和服務器資源的浪費。
Comet使用了AJAX改進了輪詢,可以實現雙向通信。但是Comet依然需要發出請求,而且在Comet中,普遍采用了長鏈接,這也會大量消耗服務器帶寬和資源。
於是,WebSocket協議應運而生。
WebSocket協議
瀏覽器通過 JavaScript 向服務器發出建立 WebSocket 連接的請求,連接建立以后,客戶端和服務器通過 TCP 連接直接交換數據。WebSocket 連接本質上是一個 TCP 連接。
WebSocket在數據傳輸的穩定性和數據傳輸量的大小方面,具有很大的性能優勢。Websocket.org 比較了輪詢和WebSocket的性能優勢:
從上圖可以看出,WebSocket具有很大的性能優勢,流量和負載增大的情況下,優勢更加明顯。
例子
瀏覽器請求
GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: example.com Origin: null Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== Sec-WebSocket-Version: 13
服務器回應
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s= Sec-WebSocket-Origin: null Sec-WebSocket-Location: ws://example.com/
在請求中的Sec-WebSocket-Key
是隨機的,服務器端會用這些數據來構造出一個SHA-1的信息摘要。把Sec-WebSocket-Key
加上一個魔幻字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11
。使用 SHA-1 加密,之后進行 BASE-64編碼,將結果作為 Sec-WebSocket-Accept
頭的值,返回給客戶端。
python代碼實現
def ws_accept_key(ws_key): """calc the Sec-WebSocket-Accept key by Sec-WebSocket-key come from client, the return value used for handshake :ws_key: Sec-WebSocket-Key come from client :returns: Sec-WebSocket-Accept """ import hashlib import base64 try: magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' sha1 = hashlib.sha1() sha1.update(ws_key + magic) return base64.b64encode(sha1.digest()) except Exception as e: return None print ws_accept_key('sN9cRrP/n9NdMgdcy2VJFQ==')