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