websocket協(xié)議是基于tcp的網(wǎng)絡(luò)協(xié)議瓢宦,實(shí)現(xiàn)了瀏覽器與客戶(hù)端的全雙工通信,與http協(xié)議不同的是送挑,它允許服務(wù)器主動(dòng)推送消息給客戶(hù)端茫陆。這樣便可以取代原來(lái)低效的輪詢(xún)颤芬。
背景
在websocket出現(xiàn)之前慰丛,客戶(hù)端獲取服務(wù)器消息卓囚,需要通過(guò)不停的輪詢(xún)。有如下缺陷:
1)服務(wù)器被迫為每個(gè)客戶(hù)端使用許多不同的底層TCP連接:一個(gè)用于向客戶(hù)端發(fā)送信息诅病,其它用于接收每個(gè)傳入消息哪亿。
2)http協(xié)議有額外的開(kāi)銷(xiāo),每個(gè)消息都有HTTP頭贤笆。
3)客戶(hù)端需要通過(guò)映射來(lái)維護(hù)傳出連接和傳人連接用以追蹤響應(yīng)蝇棉。
Websocket協(xié)議的出現(xiàn)正是彌補(bǔ)了上述缺陷,實(shí)現(xiàn)全雙工通信芥永,允許服務(wù)器主動(dòng)推送消息給客戶(hù)端篡殷。
協(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)詢(xún)問(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í)客戶(hù)端將會(huì)重新進(jìn)行握手,并將版本修改為 Sec-WebSocket-Version: 13
數(shù)據(jù)傳輸:
websocket協(xié)議中數(shù)據(jù)是通過(guò)一系列的幀來(lái)傳輸细办。出于安全性考慮橙凳,所有客戶(hù)端發(fā)往服務(wù)器的數(shù)據(jù)幀需要掩碼,若服務(wù)器收到未掩碼的數(shù)據(jù)幀將會(huì)主動(dòng)斷開(kāi)連接笑撞;所有服務(wù)器發(fā)往客戶(hù)端的數(shù)據(jù)幀不能掩碼岛啸,若客戶(hù)端收到掩碼的數(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ù)的類(lèi)型。eg:%x1表示文本幀郁稍;%x2表示二進(jìn)制幀赦政;%x8表示關(guān)閉連接幀;%x9表示ping幀;%xA 表示 pong幀恢着。從此可以看出桐愉,目前websocket支持的傳輸類(lèi)型包含文本,二進(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ù)類(lèi)型
4)最后一個(gè)數(shù)據(jù)幀必須將Fin設(shè)置為1
5)客戶(hù)端發(fā)送的數(shù)據(jù)必須掩碼
6)擴(kuò)展數(shù)據(jù)必須有定義
7)數(shù)據(jù)必須通過(guò)下行連接發(fā)送绽诚。