RSV1, RSV2, RSV3:各占 1bit
一般情況下全為 0。當(dāng)客戶端、服務(wù)端協(xié)商采用 WebSocket 擴展時,這三個標志位可以非
0,且值的含義由擴展進行定義。如果出現(xiàn)非零的值,且并沒有采用 WebSocket 擴展,連接出錯。
Opcode: 4bit
%x0:表示一個延續(xù)幀。當(dāng) Opcode 為 0 時,表示本次數(shù)據(jù)傳輸采用了數(shù)據(jù)分片,當(dāng)前收到的數(shù)據(jù)幀為其中一個數(shù)據(jù)分片;
%x1:表示這是一個文本幀(text frame);
%x2:表示這是一個二進制幀(binary frame);
%x3-7:保留的操作代碼,用于后續(xù)定義的非控制幀;
%x8:表示連接斷開;
%x9:表示這是一個心跳請求(ping);
%xA:表示這是一個心跳響應(yīng)(pong);
%xB-F:保留的操作代碼,用于后續(xù)定義的控制幀。
Mask: 1bit
表示是否要對數(shù)據(jù)載荷進行掩碼異或操作。
0:否
1:是
Payload length: 7bit or (7 + 16)bit or (7 + 64)bit
表示數(shù)據(jù)載荷的長度
0~126:數(shù)據(jù)的長度等于該值;
126:后續(xù) 2 個字節(jié)代表一個 16 位的無符號整數(shù),該無符號整數(shù)的值為數(shù)據(jù)的長度;
127:后續(xù) 8 個字節(jié)代表一個 64 位的無符號整數(shù)(最高位為 0),該無符號整數(shù)的值為數(shù)據(jù)的長度。
Masking-key: 0 or 4bytes
當(dāng) Mask 為 1,則攜帶了 4 字節(jié)的 Masking-key;
當(dāng) Mask 為 0,則沒有 Masking-key。
掩碼算法:按位做循環(huán)異或運算,先對該位的索引取模來獲得 Masking-key 中對應(yīng)的值 x,然后對該位與 x 做異或,從而得到真實的 byte 數(shù)據(jù)。
注意:掩碼的作用并不是為了防止數(shù)據(jù)泄密,而是為了防止早期版本的協(xié)議中存在的代理緩存污染攻擊(proxy cache poisoning attacks)等問題。
Payload Data: 載荷數(shù)據(jù)
解析 WebSocket 報文代碼如下:
def read_msg(data): logging.debug(data) msg_len = data[1] & 127 # 數(shù)據(jù)載荷的長度 if msg_len == 126: mask = data[4:8] # Mask 掩碼 content = data[8:] # 消息內(nèi)容 elif msg_len == 127: mask = data[10:14] content = data[14:] else: mask = data[2:6] content = data[6:] raw_str = '' # 解碼后的內(nèi)容 for i, d in enumerate(content): raw_str += chr(d ^ mask[i % 4]) return raw_str
服務(wù)端發(fā)送 WebSocket 報文
返回時不攜帶掩碼,所以 Mask 位為 0,再按載荷數(shù)據(jù)的大小寫入長度,最后寫入載荷數(shù)據(jù)。
struct 模塊解析
struct.pack(fmt, v1, v2, ...)
按照給定的格式 fmt,把數(shù)據(jù)封裝成字符串 ( 實際上是類似于 C 結(jié)構(gòu)體的字節(jié)流 )
struct 中支持的格式如下表:
Format | C Type | Python type | Standard size |
---|---|---|---|
x | pad byte | no value | |
c | char | bytes of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
n | ssize_t | integer | |
N | size_t | integer | |
e | -7 | float | 2 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | |
p | char[] | bytes | |
P | void * | integer |
為了同 C 語言中的結(jié)構(gòu)體交換數(shù)據(jù),還要考慮有的 C 或 C++ 編譯器使用了字節(jié)對齊,通常是以 4 個字節(jié)為單位的 32 位系統(tǒng),故而 struct 根據(jù)本地機器字節(jié)順序轉(zhuǎn)換。可以用格式中的第一個字符來改變對齊方式,定義如下:
Character | Byte order | Size | Alignment |
---|---|---|---|
@ | native | native | native |
= | native | standard | none |
< | little-endian | standard | none |
> | big-endian | standard | none |
! | network (= big-endian) | standard | none |
發(fā)送 WebSocket 報文代碼如下:
def write_msg(message): data = struct.pack('B', 129) # 寫入第一個字節(jié),10000001 # 寫入包長度 msg_len = len(message) if msg_len <= 125: data += struct.pack('B', msg_len) elif msg_len <= (2 ** 16 - 1): data += struct.pack('!BH', 126, msg_len) elif msg_len <= (2 ** 64 - 1): data += struct.pack('!BQ', 127, msg_len) else: logging.error('Message is too long!') return data += bytes(message, encoding='utf-8') # 寫入消息內(nèi)容 logging.debug(data) return data
總結(jié)
沒有其他能像 WebSocket 一樣實現(xiàn)全雙工傳輸?shù)募夹g(shù)了,迄今為止,大部分開發(fā)者還是使用 Ajax 輪詢來實現(xiàn),但這是個不太優(yōu)雅的解決辦法,WebSocket 雖然用的人不多,可能是因為協(xié)議剛出來的時候有安全性的問題以及兼容的瀏覽器比較少,但現(xiàn)在都有解決。如果你有這些需求可以考慮使用 WebSocket:
多個用戶之間進行交互;
需要頻繁地向服務(wù)端請求更新數(shù)據(jù)。
比如彈幕、消息訂閱、多玩家游戲、協(xié)同編輯、股票基金實時報價、視頻會議、在線教育等需要高實時的場景。
相信看了本文案例你已經(jīng)掌握了方法,更多精彩請關(guān)注Gxl網(wǎng)其它相關(guān)文章!
推薦閱讀:
ES6實現(xiàn)全屏滾動插件步驟詳解
Vue中watch使用方法總結(jié)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com