WebSocket

在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各不相同:
  1. Ping幀的Opcode是9,0x09
  2. Pong幀的Opcode是10,0x0A
  3. Close幀的Opcode是8,0x08
數據幀包含文本幀和二進制幀
  1. 文本幀的Opcode是1,0x01
  2. 二進制幀的Opcode是2,0x02

還有一個既非數據幀,也非控制幀,是0x00,代表繼續幀。
通過上面幾個數字看到,還缺了幾個數字,這幾個缺了的數字是協議保留,以備后用。

保留幀
  1. 0x03——0x07:保留用于未來的非控制幀
  2. 0x0B——0x0F:保留用于未來的控制幀

還有其他的具體解釋在:傳送門

ping幀的數據傳輸
ping

可以看到opcode是數字9

pong幀的數據傳輸
pong

可以看到opcode是數字10

close幀

下圖是服務器主動斷開連接,opcode為10,在紅框處,有一個十六進制的數字,還有一串英文解釋。
第一個Close表示的是關閉的代碼,第二個Close表示的是關閉的原因。
關閉的reason也可以自定義,但reason內容的長度別過長(之前試過大概在128個字節左右,具體的可以再試試),否則數據傳輸不了導致無法斷開的問題。
websocket的狀態碼具體詳解在這:傳送門

92701058-92E0-4E07-AFDA-2F394CD66CF1.png

在js的接口中,沒有主動發起ping接口的調用,這一般是在服務器那發起ping請求,瀏覽器接收到ping幀后,就會回個pong幀,從而達到心跳效果。

發送文本數據

從web界面向服務器發送32個‘f’,截取傳輸數據如下圖:


C4B0F5CA-49D3-4F27-8193-ABA7A52DB91D.png

Payload length代表數據長度。以字節的形式表示:7位、7+16位、或者7+64位。如果這個值以字節表示是0-125這個范圍,那這個值就表示傳輸數據的長度;如果這個值是126,則隨后的兩個字節表示的是一個16進制無符號數,用來表示傳輸數據的長度;如果這個值是127,則隨后的是8個字節表示的一個64位無符號數,這個數用來表示傳輸數據的長度。多字節長度的數量是以網絡字節的順序表示。負載數據的長度為擴展數據及應用數據之和,擴展數據的長度可能為0,因而此時負載數據的長度就為應用數據的長度。Payload length不包括Masking-key在內。
Masking-key,呃,掩碼鍵。
還有好多其他說明,都去傳送門里看吧。。

其他

之前在網上無意間看到這圖,覺得挺不錯的,描述建立連接時,websocket和TCP層的握手關系,很一目了然


handshake

在建立建立之后,每次端對端之間發送數據,另一端在TCP層都會回發個ACK表示確認。
WebSocket只是個協議,不是只能運行在瀏覽器上,自行用手機/其他客戶端也可以實現,只需遵循協議即可(目前協議文檔是RFC6455),個人覺得,假如沒有其他更好想法,用這個協議來代替很多TCP層的數據傳輸格式也是不錯的。

還有一個是IBM公司搞的MQTT協議,之前了解過其主要用于嵌入式設備,說是比較省電(相對于HTTP協議來比較那的確是),使用發布/訂閱消息模式,提供一對多的消息發布,解除應用程序耦合。它跟Websocket一樣也是基于TCP協議的,都有各自的協議頭格式。

呃,網上搜的時候,很多人都有對這些方面研究了,解釋的更好
張善友

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 本文為初入研究 Websocket協議,對于真正應用中,各種語言都有實現庫,建議采用庫,而不是自己實現,本文...
    MeIsLZHua閱讀 8,610評論 6 39
  • 上篇介紹了HTTP1.1協議的基本內容,這篇文章將繼續分析WebSocket協議,然后對這兩個進行簡單的比較。 W...
    TheAlchemist閱讀 36,603評論 15 113
  • 琪寶 暖風吹過我的窗臺 抬眸,隱約的綠影浮現 原來,一樹春意早已悄然襲來 我的熱情捱過寒冬的掩埋 冰雪的覆蓋 褪去...
    琪寶angel閱讀 274評論 1 1
  • 人大概都曾有這樣的感覺,遇到一個人有似曾相識的感覺,我胡亂猜想,也許是在街角的某次擦肩而過,我們四目相對,心中雖沒...
    檣腳摘蘑菇閱讀 517評論 0 0
  • http://www.exp99.com/htmlcss/htmlcss_229.htmlhttp://www.f...
    孫雪冬閱讀 190評論 0 0