在H5,js代碼新增了WebSocket的API。這玩意兒,從使用上來說,不難,也就onopen、onclose、onmessage、onerror這幾個接口而已,前段時間使用的時候,就還是不由自主地去看一些這方面的東西。
Websocket是基于TCP協議上實現的,也有借助HTTP協議,但與HTTP1.X協議不同地方是,WebSocket是長連接,雙方只需要一個握手動作(這里是借助HTTP協議),就可以建立一條連接通道,接著就能相互傳輸數據了;而HTTP1.X是短連接的,每次發起請求都要在TCP層來個3次握手,數據傳輸完或到指定的超時時間后就斷開連接,若要多次請求,就要不斷發起請求,這樣挺占帶寬。
Websocket在服務器方面,只需增加支持即可,如Python的tornado框架就支持,golang也挺多的,但我用的是大猩猩:gorilla。web服務器上,nginx在1.3.13版本也支持;其他web server,自行去搜索吧。瀏覽器方面除了老IE系,其他瀏覽器目前的新版本基本都支持WebSocket。
Javascript的接口
var conn = new WebSocket("ws://ip:port/");
conn.onclose = function(evt) {
console.log(evt);
}
conn.onmessage = function(evt) {
console.log(evt);
}
conn.onopen = function(evt) {
console.log(evt);
}
conn.onerror = function(evt) {
console.log(evt);
}
各接口看名稱就知道是什么意思了。
瀏覽器與服務端建立連接的時候,要經過一個Websocket握手協議
GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 172.16.1.11
Origin: http://example.com
Sec-WebSocket-Key: BVuKSx8KJKN2VAGM0i6gjw==
Sec-WebSocket-Version: 13
這是正常的HTTP的GET請求,然后在請求頭里增加了
Upgrade: websocket
Connection: Upgrade
這等于告訴web服務器,發起的請求是Websocket協議的,要用Websocket協議方式來處理請求。
在請求頭里,還增加了
Sec-WebSocket-Key: BVuKSx8KJKN2VAGM0i6gjw==
看值的話,就應該能猜到,是通過BASE64編碼而成的值,這是瀏覽器隨機生成的。
HTTP響應頭
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: sr0Tsn4bYggO1mzRZHpiOQO+FYE=
Sec-WebSocket-Accept這個是通過服務器確認,并加密后的Sec-WebSocket-Key。下面的這個響應頭代表告訴客戶端,升級成Websocket協議,屬于HTTP最后負責地方。
Upgrade: websocket
Connection: Upgrade
為什么說是HTTP最后負責的地方?因為WebSocket只是借助了HTTP協議去握手,僅此而已。此后信息通訊時,并不基于HTTP協議,而是基于更底層的TCP協議。WebSocket是一個和HTTP協議一樣是基于TCP協議上的應用層協議。
數據收發
Websocket是可以發數據類型消息,也可以發控制類型消息。
數據類型消息包含:純文本消息,二進制消息
控制類型消息包含:Ping,Pong,Close
更準確的說,WebSocket消息傳輸包含數據幀和控制幀,控制幀包含Ping,Pong,Close
這3個幀的Opcode各不相同:
- Ping幀的Opcode是9,0x09
- Pong幀的Opcode是10,0x0A
- Close幀的Opcode是8,0x08
數據幀包含文本幀和二進制幀
- 文本幀的Opcode是1,0x01
- 二進制幀的Opcode是2,0x02
還有一個既非數據幀,也非控制幀,是0x00,代表繼續幀。
通過上面幾個數字看到,還缺了幾個數字,這幾個缺了的數字是協議保留,以備后用。
保留幀
- 0x03——0x07:保留用于未來的非控制幀
- 0x0B——0x0F:保留用于未來的控制幀
還有其他的具體解釋在:傳送門
ping幀的數據傳輸
可以看到opcode是數字9
pong幀的數據傳輸
可以看到opcode是數字10
close幀
下圖是服務器主動斷開連接,opcode為10,在紅框處,有一個十六進制的數字,還有一串英文解釋。
第一個Close表示的是關閉的代碼,第二個Close表示的是關閉的原因。
關閉的reason也可以自定義,但reason內容的長度別過長(之前試過大概在128個字節左右,具體的可以再試試),否則數據傳輸不了導致無法斷開的問題。
websocket的狀態碼具體詳解在這:傳送門
在js的接口中,沒有主動發起ping接口的調用,這一般是在服務器那發起ping請求,瀏覽器接收到ping幀后,就會回個pong幀,從而達到心跳效果。
發送文本數據
從web界面向服務器發送32個‘f’,截取傳輸數據如下圖:
Payload length代表數據長度。以字節的形式表示:7位、7+16位、或者7+64位。如果這個值以字節表示是0-125這個范圍,那這個值就表示傳輸數據的長度;如果這個值是126,則隨后的兩個字節表示的是一個16進制無符號數,用來表示傳輸數據的長度;如果這個值是127,則隨后的是8個字節表示的一個64位無符號數,這個數用來表示傳輸數據的長度。多字節長度的數量是以網絡字節的順序表示。負載數據的長度為擴展數據及應用數據之和,擴展數據的長度可能為0,因而此時負載數據的長度就為應用數據的長度。Payload length不包括Masking-key在內。
Masking-key,呃,掩碼鍵。
還有好多其他說明,都去傳送門里看吧。。
其他
之前在網上無意間看到這圖,覺得挺不錯的,描述建立連接時,websocket和TCP層的握手關系,很一目了然
在建立建立之后,每次端對端之間發送數據,另一端在TCP層都會回發個ACK表示確認。
WebSocket只是個協議,不是只能運行在瀏覽器上,自行用手機/其他客戶端也可以實現,只需遵循協議即可(目前協議文檔是RFC6455),個人覺得,假如沒有其他更好想法,用這個協議來代替很多TCP層的數據傳輸格式也是不錯的。
還有一個是IBM公司搞的MQTT協議,之前了解過其主要用于嵌入式設備,說是比較省電(相對于HTTP協議來比較那的確是),使用發布/訂閱消息模式,提供一對多的消息發布,解除應用程序耦合。它跟Websocket一樣也是基于TCP協議的,都有各自的協議頭格式。
呃,網上搜的時候,很多人都有對這些方面研究了,解釋的更好
張善友