node 原生实现服务端 websocket
本文主要介紹 webSocket(下文簡寫為 ws),并使用 node 原生實現基本功能,難點主要是解析和組裝數據。需要的知識點:
- WebSocket
- Buffer
- 按位操作符
- 了解二進制
- 了解十六進制
首先我們看看 ws 數據幀格式:
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len | Extended payload length ||I|S|S|S| (4) |A| (7) | (16/64) ||N|V|V|V| |S| | (if payload len==126/127) || |1|2|3| |K| | |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len == 127 |+ - - - - - - - - - - - - - - - +-------------------------------+| |Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data |+-------------------------------- - - - - - - - - - - - - - - - +: Payload Data continued ... :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +| Payload Data continued ... |+---------------------------------------------------------------+復制代碼要理解 ws 就離不開上面這個圖,但是對數據幀不熟悉的,會完全搞不懂這個圖是表達的啥意思。所以我們先解釋下這個圖是干嘛的,我們應該看。
數據幀
- 位(bit) - 計算機最小數據存儲單位是,簡稱 b,也稱比特(bit)。每個 0 或 1 就是一個位。
 
- 字節(Byte) - 八個位表示一個字節
 
有上面這兩個概念再看上面的圖:
-  第一行(占 32 位) - 表格左上角有個 FIN,這個就表示一個位,在這個位上可能值就只能是 0 或者 1
- 接下來是 RSV1、RSV2、RSV3,它們也分別占用 1 位,
- 再后面是opcode(4)這里表示數據操作碼,占據 4 位,取值返回是:0000-1111,注意是二進制
- 然后是MASK掩碼標識,占 1 位,
- payload len(7),接受到的數據長度,占 7 位。
- Extended payload length(16/54)...第一行的最后一格,占 8 位這里的數據含義會有變化,稍后詳說。
 
-  第二行(占 32 位) -  Extended payload length continued, if payload len == 127擴展數據長度,這里為什么要分行呢? - 其實分行只是為了顯示方便而已,我們完全可以把第二行拼接到第一行后面,其實我們在處理數據時也是這么做的,沒有分行一說。
 
 
-  
所以后面幾行都是可以以此拼接到后面。
如果客戶端(瀏覽器)要發送一個hello給服務器,我們服務端收到的數據其實是一個二進制數據一系列的 0 或者 1,就像這樣10001000111...,我們要知道到底發給我們的是啥,就需要對這一些列的 0/1 做解析,上面的圖就解析這系列 0/1 的規則,我們按照上面的規則一步步解析就能得到我們想要的數據。
舉個例子:
假如收到客戶端發來的數據10000001(這里只是截取數據開始的一部分(第一個字節),后面還有很多),對應的值如下:
| 1 | 0 | 0 | 0 | 0001 | 
數據幀格式詳解
- FIN: 1bit  表示這是一個消息的最后的一幀。第一個幀也可能是最后一個。 
 %x0 : 還有后續幀
 %x1 : 最后一幀
- RSV1, RSV2, RSV3: 各占1bit  除非一個擴展經過協商賦予了非零值以某種含義,否則必須為0 如果沒有定義非零值,并且收到了非零的RSV,則websocket鏈接會失敗 
- Opcode: 4bit  解釋說明 “Payload data” 的用途/功能 如果收到了未知的opcode,最后會斷開鏈接 定義了以下幾個opcode值: %x0 : 代表連續的幀 %x1 : text幀 %x2 : binary幀 %x3-7 : 為非控制幀而預留的 %x8 : 關閉握手幀 %x9 : ping幀 %xA : pong幀 %xB-F : 為非控制幀而預留的 
- Mask: 1bit  定義“payload data”(實際提交的數據)是否被添加掩碼如果置1, “Masking-key”就會被賦值所有從客戶端發往服務器的幀都會被置1 
- Payload length: 7 bit | 7+16 bit | 7+64 bit  如果是0~125,它就是“payload length”(收到數據的長度,比如收到的是hello,那么就是5), 如果是126,緊隨其后的被表示為16 bits無符號整型就是“payload length”, 如果是127,緊隨其后的被表示為64 bits無符號整型就是“payload length” - 為什么會有這三種情況呢? 由于payload length只有7位,二級制最大是1111111轉換為十進制就是127,如果“payload length”大于127了,就沒法正確的表示。我們需要更多的位來表示“payload length”,所以我們在Payload length后面用另外的位來表示。那直接定義一個64位來表示不就行了么?雖然這樣能行,但是也得考慮到性能問題,如上面說的hello長度只有“5”,轉換為二進制是101,三位就可以了,如果用64位就有點太浪費了。所以分別定義了這三種情況。
 
- Masking-key: 0 or 32bit  所有從客戶端發送到服務器的幀都包含一個32 bits的掩碼(如果“mask bit”被設置成1),否則為0 bit。一旦掩碼被設置,所有接收到的payload data都必須與該值以一種算法做異或運算來獲取真實值。 
- Payload data: (x+y) bytes  它是"Extension data"和"Application data"的總和,一般擴展數據為空。 
- Extension data: x bytes  除非擴展被定義,否則就是0,任何擴展必須指定其Extension data的長度 
- Application data: y bytes  占據"Extension data"之后的剩余幀的空間 
實戰
知道了幀結構和含義,接下來就可以按照規則解析數據
- 解析數據
- 構建返回數據,返回數據就是解析數據的逆操作
上面兩段代碼都有很詳細的注釋,應該能看懂,就不再具體的解析,實現源碼見github
轉載于:https://juejin.im/post/5c923a8ae51d453ec10e563a
總結
以上是生活随笔為你收集整理的node 原生实现服务端 websocket的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 自动化测试|录制回放效果差异检测
- 下一篇: MySQL索引优化实战
