websocket協(xié)議是基于tcp的網(wǎng)絡(luò)協(xié)議,實(shí)現(xiàn)了瀏覽器與客戶端的全雙工通信,與http協(xié)議不同的是,它允許服務(wù)器主動(dòng)推送消息給客戶端。這樣便可以取代原來(lái)低效的輪詢。
背景
在websocket出現(xiàn)之前,客戶端獲取服務(wù)器消息,需要通過(guò)不停的輪詢。有如下缺陷:
1)服務(wù)器被迫為每個(gè)客戶端使用許多不同的底層TCP連接:一個(gè)用于向客戶端發(fā)送信息,其它用于接收每個(gè)傳入消息。
2)http協(xié)議有額外的開(kāi)銷,每個(gè)消息都有HTTP頭。
3)客戶端需要通過(guò)映射來(lái)維護(hù)傳出連接和傳人連接用以追蹤響應(yīng)。
Websocket協(xié)議的出現(xiàn)正是彌補(bǔ)了上述缺陷,實(shí)現(xiàn)全雙工通信,允許服務(wù)器主動(dòng)推送消息給客戶端。
協(xié)議分析
websocket協(xié)議分為兩部分,一是握手建立連接;二是數(shù)據(jù)傳輸。
建立連接;
websocket的連接建立是基于http協(xié)議的。
請(qǐng)求報(bào)文示例:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection:Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
響應(yīng)報(bào)文示例:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
下面來(lái)分析一下上述報(bào)文:
WebSocket借用http請(qǐng)求進(jìn)行握手,相比正常的http請(qǐng)求,多了一些內(nèi)容。其中,
HTTP/1.1 101表示websocket連接已成功建立,其他任何code表示連接建立失敗。
Upgrade: websocket
Connection: Upgrade
表示希望將http協(xié)議升級(jí)到Websocket協(xié)議。
Sec-WebSocket-Key是瀏覽器隨機(jī)生成的base64 encode的值,用來(lái)詢問(wèn)服務(wù)器是否是支持WebSocket。
服務(wù)器返回
Upgrade: websocket
Connection: Upgrade
告訴瀏覽器即將升級(jí)的是Websocket協(xié)議
Sec-WebSocket-Accept是將請(qǐng)求包"Sec-WebSocket-Key"的值,與"258EAFA5-E914-47DA-95CA-C5AB0DC85B11″這個(gè)字符串進(jìn)行拼接,然后對(duì)拼接后的字符串進(jìn)行sha-1運(yùn)算,再進(jìn)行base64編碼得到的。用來(lái)說(shuō)明自己是WebSocket助理服務(wù)器。
Sec-WebSocket-Version是WebSocket協(xié)議版本號(hào)。
若請(qǐng)求報(bào)文中,Sec-WebSocket-Version: 25,服務(wù)端可能的響應(yīng)會(huì)是
HTTP/1.1 400 Bad Request ... Sec-WebSocket-Version: 13, 8, 7
此時(shí)客戶端將會(huì)重新進(jìn)行握手,并將版本修改為 Sec-WebSocket-Version: 13
數(shù)據(jù)傳輸:
websocket協(xié)議中數(shù)據(jù)是通過(guò)一系列的幀來(lái)傳輸。出于安全性考慮,所有客戶端發(fā)往服務(wù)器的數(shù)據(jù)幀需要掩碼,若服務(wù)器收到未掩碼的數(shù)據(jù)幀將會(huì)主動(dòng)斷開(kāi)連接;所有服務(wù)器發(fā)往客戶端的數(shù)據(jù)幀不能掩碼,若客戶端收到掩碼的數(shù)據(jù)幀將會(huì)主斷開(kāi)連接。
數(shù)據(jù)幀的定義:

FIN: 1 bit --是否是消息的最后一個(gè)數(shù)據(jù)幀。一個(gè)消息可以通過(guò)一個(gè)或多個(gè)數(shù)據(jù)幀發(fā)送,第一個(gè)幀也可能是最后一個(gè)幀。
RSV1, RSV2, RSV3: 1 bit each-- 保留字段,如果有自定義的擴(kuò)展數(shù)據(jù)時(shí)使用。當(dāng)沒(méi)有擴(kuò)展數(shù)據(jù)時(shí),若保留字段非0,則會(huì)導(dǎo)致連接失敗。
Opcode: 4 bits--說(shuō)明數(shù)據(jù)的類型。eg:%x1表示文本幀;%x2表示二進(jìn)制幀;%x8表示關(guān)閉連接幀;%x9表示ping幀;%xA 表示 pong幀。從此可以看出,目前websocket支持的傳輸類型包含文本,二進(jìn)制和ping/pong。
Mask: 1 bit--是否掩碼。(規(guī)則上文已述)
Payload length: 7 bits, 7+16 bits, or 7+64 bits--數(shù)據(jù)長(zhǎng)度
Masking-key: 0 or 4 bytes--掩碼的key(若mask is 1)
Payload data: (x+y) bytes-- 數(shù)據(jù),包含擴(kuò)展數(shù)據(jù)和應(yīng)用數(shù)據(jù)。
Extension data: x bytes--私有定制協(xié)議。
Application data: y bytes--傳輸?shù)臄?shù)據(jù)
示例
o 非掩碼的Hello: * 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f
注解:
0x81 -->10000001
-->Fin=1(最后幀);RSV=000;Opcode=0001(文本幀)
0x05 -->00000101
--> mask=0(不掩碼);Payload length=0000101(5個(gè)字符)
0x48 0x65 0x6c 0x6c 0x6f -->Hello
o掩碼的Hello: * 0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (contains "Hello")
o多幀不掩碼的Hello * 0x01 0x03 0x48 0x65 0x6c (contains "Hel")
0x80 0x02 0x6c 0x6f (contains "lo")
256字節(jié)二進(jìn)制非掩碼數(shù)據(jù)* 0x82 0x7E 0x0100 [256 bytes of binary data]
64KiB單幀二進(jìn)制非掩碼數(shù)據(jù)
0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]
數(shù)據(jù)發(fā)送規(guī)則:
1)保證連接狀態(tài)為open。
2)不知大小或太大的數(shù)據(jù),會(huì)分為一系列的幀來(lái)發(fā)送。
3)數(shù)據(jù)發(fā)送幀中必須標(biāo)明數(shù)據(jù)類型
4)最后一個(gè)數(shù)據(jù)幀必須將Fin設(shè)置為1
5)客戶端發(fā)送的數(shù)據(jù)必須掩碼
6)擴(kuò)展數(shù)據(jù)必須有定義
7)數(shù)據(jù)必須通過(guò)下行連接發(fā)送。