websocket協議的解析與實現(一)
HTTP協議是短連線的,通訊模式如圖:
如何實現伺服器主動向客戶端傳送資料呢?!websocket協議就是解決這個問題的。
通訊過程大致如下:
本文中要有兩部分組成
一、建立連線請求過程二、資料幀格式
一、建立連線請求過程
在建立連線請求過程中使用的協議是HTTP協議。
1、客戶端傳送建立連線請求
使用的是HTTP協議頭,伺服器端按照HTTP協議正常解析,但這塊不同的是伺服器程式必須判斷是否是websocket請求,其實伺服器只需要檢視該http協議頭中是否包含了websockt等字串,請求頭如下
websockt握手請求協議包含Upgrade: websocket欄位,伺服器通過該欄位就可以知道客戶端想要建立websockt連線請求。
2、伺服器響應連線請求
伺服器從請求協議包中獲取Sec-WebSocket-Key欄位值,如下圖:
然後將該key值與字串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”拼接形成新的字串,如"d359Fdo6omyqfxyYF7Yacw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11";然後伺服器對該字串進行SHA-1加密,之後再進行一次base64加密,將加密之後的結果作為最終的響應key傳送給客戶端,伺服器相依協議頭如下:
其中Sec-WebSocket-Accept:欄位填充由請求中的Sec-WebSocket-Key欄位值與字串”258EAFA5-E914-47DA-95CA-C5AB0DC85B11“拼接而成的字串經過加密之後的字串組成。
按照以上兩步即可建立websocket連線。接下來客戶端與伺服器就可以互相非同步的傳送資料了。
二、資料幀格式
連線建立之後,客戶端與伺服器就可以相互發送資料了,資料包格式如下:
注意:
如果條件不滿足,則擴充套件位元組不出現在資料幀中。
列 明 |
位數 |
含義 |
取值 |
FIN |
1 bit |
是否是最後一幀資料 |
1表示後面沒資料了 0:表示後面還有資料 |
RSV |
3 bit |
保留 |
|
OPCODE |
4 bit |
訊息型別 |
0x0表示附加資料幀 0x1表示文字資料幀 0x2表示二進位制資料幀 0x3-7暫時無定義,為以後的非控制幀保留 0x8表示連線關閉 0x9表示 0xA表示pong 0xB-F暫時無定義,為以後的控制幀保留 |
MASK |
1 bit |
是否經過掩碼處理; 客戶端必須對其傳送到伺服器的所有幀進行掩碼處理。 伺服器一旦收到無掩碼幀,將關閉連線。伺服器可能傳送一個狀態碼是1002(表示協議錯誤)的Close幀。 而伺服器傳送客戶端的資料幀不做掩碼處理,一旦客戶端發現經過掩碼處理的幀,將關閉連線。客戶端可能使用狀態碼1002 |
1:表示處理過 0:表示沒處理過 用於標識PayloadData是否經過掩碼處理。如果是1,Masking-key域的資料即是掩碼金鑰,用於解碼PayloadData。客戶端發出的資料幀需要進行掩碼處理,所以此位是1。 |
Payload length |
7 bit 7+16bit 7+64 bit |
PayloadData 資料長度 Payload長度是ExtensionData長度與ApplicationData長度之和 |
0-125:則是payload資料的真實長度 126:之後的2個位元組的16位無符號正數值表示真是長度 127:之後的8個位元組形成的64位無符號整型數的值是payload的真實長度 (注意網路位元組序) |
Masking-key |
32 bit |
掩碼 |
|
data |
.... |
資料 |
..................... |
1、客戶端到伺服器的資料必須通過掩碼處理
2、伺服器收到資料包之後需要判斷是否有掩碼,即MASK是否有效,無效則傳送關閉連線資料包,如果掩碼位有效,則解掩碼來獲取真正的資料
3、伺服器傳送給客戶端的資料不需要掩碼處理,相對簡單
協議示例:
客戶端傳送給伺服器的文字訊息”hello”
0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58(包含 "Hello")
掩碼與解掩碼過程:
1)、計算j
j = i MOD 4
2)、計算掩碼後的資料,解掩碼方法相同 transformed-octet-i = original-octet-i XOR masking-key-octet-j
original-octet-i表示原始資料中下標為i的資料,如[0x7f 0x9f 0x4d 0x51 0x58]
masking-key-octet-j 表示四個位元組組成的掩碼陣列中下標為j的掩碼值,
如[0x37 0xfa 0x21 0x3d ]
transformed-octet-i:表示掩碼或解掩碼之後的資料
示例解碼過程如下:
Unsigned char original-octet[] = {0x7f,0x9f,0x4d,0x51,0x58};
Unsigned char masking-key-octet[] = {0x37,0xfa,0x21,0x3d};
//LENGTH = sizeof(original-octet);
Unsigned char Transformed-octer[LENGTH];
0x7f: i = 0; => j = i % 4 = 0;
Transformed-octer[i] = original-octet[i] XOR masking-key-octet[j];
= 0x7F XOR 0x37 = 0x48
0x48 = ‘h’
其他數字掩碼或解掩碼過程同上
下一篇文章將給出伺服器和客戶端主要程式碼,伺服器是Linux下用C語言編寫的,客戶端使用js指令碼實現。