WebScoket 规范 + WebSocket 协议
WebSocket握手協議
1、客戶端握手請求(注意:鍵值之間有一個空格,行間有換行符號0x13x10或者說\r\n)
GET /WebSocket/LiveVideo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: localhost:8080???????????? (客戶端請求主機)
Origin: http://127.0.0.1???????? (來源網頁地址)
Sec-WebSocket-Key1: 23 asdfJKj,asdjk
Sec_WebSocket-Key2: wewerw234 jij998
0x13x10 + 8個字節Sec_WebSocket-Key3值,沒有鍵名(注意,這里的0x13x10的額外的,也就說有兩個連續的0x13x10)
2、服務端握手回復
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://127.0.0.1???? (來源網頁地址)
Sec-WebSocket-Location: ws://localhost:8080/WebSocket/LiveVideo
16個字節的加密KEY
加密KEY算法:
Sec_WebSocket-Key1的產生方式:
(1)提取客戶端請求的Sec_WebSocket-Key1中的數字符組成字符串k1
(2)轉換字符串為8個字節的長整型intKey1
(3)統計客戶端請求的Sec_WebSocket-Key1中的空格數k1Spaces
(4)intK1/k1Spaces取整k1FinalNum
(5)將k1FinalNum轉換成字節數組再反轉最終形成4個字節的Sec_WebSocket-Key1
Sec_WebSocket-Key2的產生方式:
(1)提取客戶端請求的Sec_WebSocket-Key2中的數字符組成字符串k2
(2)轉換字符串為8個字節的長整型intKey2
(3)統計客戶端請求的Sec_WebSocket-Key2中的空格數k2Spaces
(4)intK2/k2Spaces取整k2FinalNum
(5)將k2FinalNum轉換成字節數組再反轉最終形成4個字節的Sec_WebSocket-Key2
Sec_WebSocket-Key3的產生方式:
客戶端握手請求的最后8個字節
將Sec_WebSocket-Key1、Sec_WebSocket-Key2、Sec_WebSocket-Key3合并成一個16字節數組
再進行MD5加密形成最終的16個字節的加密KEY
3、消息發送接收
客戶端和服務端發送非握手文本消息時,消息以utf-8編碼,并以0x00開頭,0xFF結尾。
?
?WebScoket 規范
4.1 握手協議
websocket 是 獨立的基于TCP的協議, 其跟http協議的關系僅僅是 WebSocket 的握手被http 服務器當做 Upgrade request http包處理。 websocket 有自己的握手處理。 TCP連接建立后,client 發送websocket 握手請求. 請求包需求如下:
必須是有效的http request 格式
HTTP request method 必須是GET,協議應不小于1.1 如: Get /chat HTTP/1.1
必須包括Upgrade 頭域,并且其值為“websocket”
必須包括"Connection" 頭域,并且其值為 "Upgrade"
必須包括"Sec-WebSocket-Key"頭域,其值采用base64編碼的隨機16字節長的字符序列, 服務器端根據該域來判斷client 確實是websocket請求而不是冒充的,如http。響應方式是,首先要獲取到請求頭中的Sec-WebSocket-Key的值,再把這一段 GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"加到獲取到的Sec-WebSocket-Key的值的后面,然后拿這個 字符串做SHA-1 hash計算,然后再把得到的結果通過base64加密,就得到了返回給客戶端的Sec-WebSocket-Accept的http響應頭的值。
如果請求來自瀏覽器客戶端,還必須包括Origin頭域 。 該頭域用于防止未授權的跨域腳本攻擊,服務器可以從Origin決定是否接受該WebSocket連接。
必須包括"Sec-webSocket-Version" 頭域,當前值必須是13.
可能包括"Sec-WebSocket-Protocol",表示client(應用程序)支持的協議列表,server選擇一個或者沒有可接受的協議響應之。
可能包括"Sec-WebSocket-Extensions", 協議擴展, 某類協議可能支持多個擴展,通過它可以實現協議增強
可能包括任意其他域,如cookie
示例如下:
??????? 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
Server 接手到握手請求后應處理該請求包括:
處理請求包括處理GET 方法
驗證Upgrader頭域
驗證Connection 頭域
處理Sec-WebSocket-Key頭域,方法見上;
處理Sec-WebSocket-Version
處理Origin頭域,可選, 瀏覽器必須發送該頭域
處理Sec-WebSocket-Protocol頭域,可選
處理Sec-WebSocket-Extensions 頭域,可選
處理其他頭域,可選
Server 發送握手響應,這里只介紹服務器接受該連接情況下,包括:
http Status-Line
Upgrade 頭域 ,值必須是"websocket"
Conntion頭域,值必須是:“Upgrade”
Sec-WebSocket-Accept” 頭域,該頭域的值即處理Sec-WebSocket-Key" 域后的結果。
可選的"Sec-WebSocket-Protocol"頭域
可選的"Sec-WebSocket-Extensions"頭域
響應可能如下:
??????? HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
4.2 數據傳輸
?? 在WebSocket 協議中,使用序列frames方式來傳輸數據。一個frame的標準格式如下:
????? 0?????????????????? 1?????????????????? 2?????????????????? 3
0 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 ...??????????????? |
+---------------------------------------------------------------+
FIN:1位,是否是消息的結束幀(分片)
RSV1, RSV2, RSV3: 分別都是1位, 預留,用于約定自定義協議。 如果雙方之間沒有約定自定義協議,那么這幾位的值都必須為0,否則必須斷掉WebSocket連接;
Opcode:4位操作碼,定義有效負載數據,如果收到了一個未知的操作碼,連接也必須斷掉,以下是定義的操作碼:
%x0 表示連續消息分片
%x1 表示文本消息分片
%x2 表未二進制消息分片
%x3-7 為將來的非控制消息片斷保留的操作碼
%x8 表示連接關閉? %x9 表示心跳檢查的ping
%xA 表示心跳檢查的pong
%xB-F 為將來的控制消息片斷的保留操作碼
Mask: 定義傳輸的數據是否有加掩碼,如果設置為1,掩碼鍵必須放在masking-key區域,客戶端發送給服務端的所有消息,此位的值都是1;
Payload length: 傳輸數據的長度,以字節的形式表示:7位、7+16位、或者7+64位。如果這個值以字節表示是0-125這個范圍,那這個值就表示傳輸數據的長度;如果 這個值是126,則隨后的兩個字節表示的是一個16進制無符號數,用來表示傳輸數據的長度;如果這個值是127,則隨后的是8個字節表示的一個64位無符 合數,這個數用來表示傳輸數據的長度。多字節長度的數量是以網絡字節的順序表示。負載數據的長度為擴展數據及應用數據之和,擴展數據的長度可能為0,因而 此時負載數據的長度就為應用數據的長度。注意Payload length不包括Masking-key在內。
Masking-key: 0或4個字節,客戶端發送給服務端的數據,都是通過內嵌的一個32位值作為掩碼的;掩碼鍵只有在掩碼位設置為1的時候存在。 數據Mask方法是,第 i byte 數據 = orig-data ^ (i % 4) .
Payload data: (x+y)位,負載數據為擴展數據及應用數據長度之和。
Extension data:x位,如果客戶端與服務端之間沒有特殊約定,那么擴展數據的長度始終為0,任何的擴展都必須指定擴展數據的長度,或者長度的計算方式,以及在握手時如何確定正確的握手方式。如果存在擴展數據,則擴展數據就會包括在負載數據的長度之內。
Application data:y位,任意的應用數據,放在擴展數據之后,應用數據的長度=負載數據的長度-擴展數據的長度。
把消息分片處理主要是處于以下兩個原因:
消息接收方事先并不知道消息大小, 而且也沒必要預留一個足夠大的buffer來處理;
multiplexing
消息分片一些規則如下(不全):
為分片消息(single-frame) 其FIN置為1,并且opcode code 不是 0;
分片消息序列如下, 第一幀FIN置為0,opcode code不是0; 接著是FIN置為0,opcode code也是0; 最后幀 FIN為1,opcode code為0.
在分片消息發送期間可能插入了控制幀
控制幀不能分片
控制幀的opcode符號位為1, 目前控制幀包括 0×8(Close), 0×9(Ping) 0xA (Pong). 0xB-0xF 被預留。
??? ws-frame??????????????? = frame-fin
frame-rsv1
frame-rsv2
frame-rsv3
frame-opcode
frame-masked
frame-payload-length
[ frame-masking-key ]
frame-payload-data
frame-fin?????????????? = %x0 ; 表示這不是當前消息的最后一幀,后面還有消息
/ %x1 ; 表示這是當前消息的最后一幀
frame-rsv1????????????? = %x0
; 1 bit, 如果沒有擴展約定,該值必須為0
frame-rsv2????????????? = %x0
; 1 bit, 如果沒有擴展約定,該值必須為0
frame-rsv3????????????? = %x0
; 1 bit, 如果沒有擴展約定,該值必須為0
frame-opcode??????????? = %x0 ; 表示這是一個連續幀消息
/ %x1 ; 表示文本消息
/ %x2 ; 表示二進制消息
/ %x3-7 ; 保留
/ %x8 ; 表示客戶端發起的關閉
/ %x9 ; ping(用于心跳)
/ %xA ; pong(用于心跳)
/ %xB-F ; 保留
frame-masked??????????? = %x0 ; 數據幀沒有加掩碼,后面沒有掩碼key
/ %x1 ; 數據幀加了掩碼,后面有掩碼key
frame-payload-length??? = %x00-7D
/ %x7E frame-payload-length-16
/ %x7F frame-payload-length-63
; 表示數據幀的長度
frame-payload-length-16 = %x0000-FFFF
; 表示數據幀的長度
frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
; 表示數據幀的長度
frame-masking-key?????? = 4( %0x00-FF ) ; 掩碼key,只有當掩碼位為1時出現
frame-payload-data????? = (frame-masked-extension-data
frame-masked-application-data)?
????????????????? ; 當掩碼位為1時,這里的數據為帶掩碼的數據,擴展數據及應用數據都帶掩碼
/ (frame-unmasked-extension-data
frame-unmasked-application-data) ;
???????????????? 當掩碼位為0時,這里的數據為不帶掩碼的數據,擴展數據及應用數據都不帶掩碼
frame-masked-extension-data???? = *( %x00-FF ) ; 目前保留,以后定義
frame-masked-application-data?? = *( %x00-FF )
frame-unmasked-extension-data?? = *( %x00-FF ) ; 目前保留,以后定義
frame-unmasked-application-data = *( %x00-FF )
?
Close 處理
Close 幀的opcode是0×8. 接收到 Close 幀后,如果之前沒發送過Close幀,則其必須發送Close 幀響應,但其可以延遲發送Close響應幀,例如在其發送完數據之后發送;但是,協議不保證對方在發送Close 幀后仍會處理其后續的數據。Close幀可能Client發起也可能是Server發起。
Ping-Pong 幀
接收到Ping幀后將響應Pong幀, 主要用于檢測網絡連接情況。
Extensions
WebSocket 支持協議擴展。 例如增加一個認證處理或者速率控制等,這通過client-server 協商完成。在WebSocket 握手處理時,通過頭域Sec-WebSocket-Extensions來完成協商。 例如:
Sec-WebSocket-Extensions: mux; max-channels=4; flow-control,
deflate-stream
服務器接收一個或多個extensiions 通過再起響應的Sec-WebSocket-Extensions頭域增加一個或多個extension完成。
?
說明:
?
服務器建立成功之后,如果有客戶端請求連接本服務器,需要用socket_accept等方法建立一個新的socket連接,并接收客戶端的請求信息,處理之后,返回響應信息,然后握手成功。?
接下來是字符串通信,客戶端send過來一段字符串信息,服務器端接收到并返回給客戶端這個字符串。?? 首先我們處理接收到的信息,根據上篇文章介紹的數據傳輸格式,并firefox的FIN一直為1,RSV1,2,3為0,如果是文本消息,那么 opcode為1,所以數據包的第一個數據是0x81,然后是一位mask值,firefox發來的數據是加了掩碼的,所以mask值為1,后面跟7位是 數據信息長度,我們以客戶端發送hi為例,那么長度就是2個字節,則第二個數據就是0x82,這里沒有約定擴展數據,所以不存在擴展數據長度字節,接下來 是4個數據的掩碼(因為我們這里是發送hi,2個字節的信息,小于125個字節,所以掩碼是第3-第6個數據,根據數據長度的不同,掩碼的位置也不同,如 果取到那7位表示的值是126,則掩碼為第5-第8個數據,如果取到那7位表示的值是127,則掩碼為第11-第14個數據),后面跟客戶端發送的內容數 據,處理接收到的數據我們需要用取到的掩碼依次輪流跟內容數據做異或(^)運算,第一個內容數據與第一個掩碼異或,第二個內容數據與第二個掩碼異或……第 五個內容數據與第一個掩碼異或……以此類推,一直到結束,然后對內容進行編碼。
?
舉例:
?1 /// <summary>
?2 ///判斷傳入數據是否存在掩碼
?3 /// 傳入數據:hi
?4 /// socket接收到的二進制數據:
?5 ///????? 1000000110000010 1101011011101001
?6 ///????? 111110 111000 10111110 10000000
?7 /// 掩碼異或的操作:
?8 ///???????????? 111110 111000 10111110 10000000
?9 ///?? 進行異或^ 111110 111001 11010110 11101001
10 ///??? 結果:?????????????????? 1101000? 1101001
11 /// 數據樣例:
12 ///??????? [0]??? 129??? byte
13 ///??????? [1]??? 130??? byte
14 ///??????? [2]??? 214??? byte
15 ///??????? [3]??? 233??? byte
16 ///??????? [4]??? 62???? byte
17 ///??????? [5]??? 56???? byte
18 ///??????? [6]??? 190???? byte
19 ///??????? [7]??? 128???? byte
20 /// </summary>
21 /// <returns></returns>
22???????? private string UnWrap()
23???????? {
24???????????? string result = string.Empty;
25
26???????????? // 計算非空位置
27???????????? int lastStation = GetLastZero();
28
29???????????? // 利用掩碼對org-data進行異或
30???????????? int frame_masking_key = 1;
31???????????? for (int i = 6; i <= lastStation; i++)
32???????????? {
33???????????????? frame_masking_key = i % 4;
34???????????????? frame_masking_key = frame_masking_key == 0 ? 4 : frame_masking_key;
35???????????????? frame_masking_key = frame_masking_key == 1 ? 5 : frame_masking_key;
36???????????????? receivedDataBuffer[i] = Convert.ToByte(receivedDataBuffer[i] ^ receivedDataBuffer[frame_masking_key]);
37???????????? }
38
39???????????? System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
40???????????? result = decoder.GetString(receivedDataBuffer, 6, lastStation - 6 + 1);
41
42???????????? return result;
43
44???????? }
?
?
?WebSocket 協議:
?
public enum WebSocketProtocol
??? {
??????? /*
???????? *
???????? * Request
??????????? GET /WebIM5?uaid=200513807p8912-8de8c7e2-c963-4f67-8aca-8028797efbc1&re=0 HTTP/1.1
??????????? Upgrade: WebSocket
??????????? Connection: Upgrade
??????????? Host: 10.10.150.60:5002
??????????? Origin: https://localhost:444
??????????? Sec-WebSocket-Key1: 3+3 1? 8kgV"m 0 8? 64u43
??????????? Sec-WebSocket-Key2: 3_7891 6 4 `50 `8
???????? *
???????? * Response
??????????? HTTP/1.1 101 WebSocket Protocol Handshake
??????????? Upgrade: WebSocket
??????????? Connection: Upgrade
??????????? Sec-WebSocket-Origin: https://localhost:444
??????????? Sec-WebSocket-Location: ws://192.168.110.....
??????????? Sec-WebSocket-Protocol: WebIM5
???????? *
???????? *? asdfalskdfa
???????? * */
??????? draft_00 = 0,
??????? /*
???????? *
???????? * Request
??????????? GET /WebIM5?uaid=200513807p8912-2e695e5b-9b46-4511-b59e-28981b4ab327&re=0 HTTP/1.1
??????????? Upgrade: websocket
??????????? Connection: Upgrade
??????????? Host: 10.10.150.60:5002
??????????? Origin: https://localhost:444
??????????? Sec-WebSocket-Key: 1o4Jk9XPGvTX66OxmNMaww==
??????????? Sec-WebSocket-Version: 13
???????? *
???????? * Response
??????????? HTTP/1.1 101 Switching Protocols
??????????? Upgrade: websocket
??????????? Connection: Upgrade
??????????? Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
??????????? Sec-WebSocket-Protocol: WebIM5
???????? * */
??????? draft_17 = 17
??? }
轉載于:https://www.cnblogs.com/lchb/articles/3324250.html
總結
以上是生活随笔為你收集整理的WebScoket 规范 + WebSocket 协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [VirtaulBox]网络连接设置
- 下一篇: 如何卸载rpm