国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

WebSocket 協(xié)議

2i18ns / 1483人閱讀

摘要:可以有消息體,指明消息原因,可作為日志進(jìn)行記錄。端點(diǎn)在接受到關(guān)閉幀后,可以延遲響應(yīng)關(guān)閉幀,繼續(xù)發(fā)送或接受數(shù)據(jù)幀,但不保證一個已經(jīng)發(fā)送關(guān)閉幀的端點(diǎn)繼續(xù)處理數(shù)據(jù)。發(fā)送并接收了關(guān)閉幀的端點(diǎn),被認(rèn)為是關(guān)閉了連接,其必須關(guān)閉底層的連接。

參考文章

websocket RFC github 中文翻譯

Websocket RFC 文檔

workerman websocket 協(xié)議實(shí)現(xiàn)

協(xié)議組成

協(xié)議由一個開放握手組成,其次是基本的消息成幀,分層的TCP.

解決的問題

基于瀏覽器的機(jī)制,實(shí)現(xiàn)客戶端與服務(wù)端的雙向通信.

協(xié)議概述

來自客戶端握手

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

來自服務(wù)端的握手

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
// 可選的頭,表示允許的通過的客戶端
Sec-WebSocket-Protocol: chat

以上,頭順序無所謂.

一旦客戶端和服務(wù)器都發(fā)送了握手信號,如果握手成功,數(shù)據(jù)傳輸部分啟動。這是雙方溝通的渠道,獨(dú)立于另一方,可隨意發(fā)送數(shù)據(jù)。

服務(wù)器的響應(yīng),不是隨意的,需要遵循一定的規(guī)則 請參考RFC 文檔 第 6/7頁:

獲取客戶端請求的 Sec-Weboscket-Key 字段值,去除收尾空白字符

與全球唯一標(biāo)識符拼接 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

sha1 加密(短格式)

base64 加密

PHP 程序描述:

$client_key = "dGhlIHNhbXBsZSBub25jZQ==";
$client_key = trim($client_key);
$guid       = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
$key        = $client_key . $guid;
$key        = sha1($key , true);
$key        = base64_encode($key);

上述結(jié)果得出的值即是服務(wù)端返回給客戶端握手的 Sec-Websocket-Accept 頭字段值.

關(guān)閉鏈接

接收到一個 0x8 控制幀后,鏈接也許立即斷開,也許在接收完剩下的數(shù)據(jù)后斷開。

可以有消息體,指明消息原因,可作為日志進(jìn)行記錄。

應(yīng)用發(fā)送關(guān)閉幀后必須不在發(fā)送更多數(shù)據(jù)幀。

如果一個端點(diǎn)接受到一個關(guān)閉幀且先前沒有發(fā)送關(guān)閉幀,則必須發(fā)送一個關(guān)閉幀。

端點(diǎn)在接受到關(guān)閉幀后,可以延遲響應(yīng)關(guān)閉幀,繼續(xù)發(fā)送或接受數(shù)據(jù)幀,但不保證一個已經(jīng)發(fā)送關(guān)閉幀的端點(diǎn)繼續(xù)處理數(shù)據(jù)。

發(fā)送并接收了關(guān)閉幀的端點(diǎn),被認(rèn)為是關(guān)閉了 websocket 連接,其必須關(guān)閉底層的 TCP 連接。

設(shè)計理念

基于框架而不是基于流/文本或二進(jìn)制幀.

鏈接要求 針對客戶端要求

握手必須是一個有效的 HTTP 請求

請求的方法必須為 GET,且 HTTP 版本必須是 1.1

請求的 REQUEST-URI 必須符合文檔規(guī)定的要求(詳情查看 Page 13)

請求必須包含 Host

請求必須包含 Upgrade: websocket 頭,值必須為 websocket

請求必須包含 Connection: Upgrade 頭,值必須為 Upgrade

請求必須包含 Sec-WebSocket-Key

請求必須包含 Sec-WebSocket-Version: 13 頭,值必須為 13

請求必須包含 Origin

請求可能包含 Sec-WebSocket-Protocol 頭,規(guī)定子協(xié)議

請求可能包含 Sec-WebSocket-Extensions ,規(guī)定協(xié)議擴(kuò)展

請求可能包含其他字段,如 cookie

不符合上述要求的服務(wù)器響應(yīng),客戶端都會斷開鏈接.

如果響應(yīng)不包含 Sec-WebSocket-Protocol 中指定的子協(xié)議,客戶端斷開

如果響應(yīng) HTTP/1.1 101 Switching Protocols 狀態(tài)碼不是 101,客戶端斷開

針對服務(wù)端要求

如果請求是 HTTP/1.1 或更高的 GET 請求,包含 REQUEST-URI 則應(yīng)正確地按照文檔要求進(jìn)行解析.

必須驗(yàn)證 Host 字段

Upgrade 頭字段值必須是大小寫不敏感的 websocket

Sec-WebSocket-keyd 解碼時長度為 16Byte

Sec-WebSocket-Version 值必須是 13

Host 如果沒有被包含,則鏈接不應(yīng)該被解釋為瀏覽器發(fā)起的行為

Sec-WebSocket-Protocol 中列出的客戶端請求的子協(xié)議,服務(wù)端應(yīng)按照優(yōu)先順序排列,響應(yīng)

任選的其他字段

響應(yīng)要求:

驗(yàn)證 Origin 字段,如果不符合要求的請求則返回適當(dāng)?shù)腻e誤代碼(例如:403)

Sec-WebSocket-Key 值是一個 base64 加密后的值,服務(wù)端不需要對其進(jìn)行解碼,而僅是用來創(chuàng)建服務(wù)器的握手.

驗(yàn)證 Sec-WebSocket-Version 值,如果不是 13,則返回一個適當(dāng)?shù)腻e誤代碼(例如:HTTP/1.1 426 Upgrade Required)

資源名驗(yàn)證

子協(xié)議驗(yàn)證

extensions 驗(yàn)證

如果通過了上述驗(yàn)證,則服務(wù)器表示接受該鏈接.那么起響應(yīng)必須符合以下要求詳情查看 Page 23:

必須,狀態(tài)行 HTTP/1.1 101 Switching Protocols

必須,協(xié)議升級頭 Upgrade: websocket

必須,表示連接升級的頭字段 Connection: Upgrade

必須,Sec-WebSocket-Accept 頭字段,詳情請查閱 協(xié)議概述 部分

可選:Sec-WebSocket-Protocols 頭部

完整的響應(yīng)代碼如下(嚴(yán)格按照如下格式響應(yīng)!!頭部順序無所謂!關(guān)鍵是后面的換行符注意了!嚴(yán)格控制數(shù)量!):

HTTP/1.1 101 Switching Protocols

Connection: Upgrade

Upgrade: websocket

Sec-WebSocket-Accept: 3nlEzv+LqVBYnTHclAqtk62uOTQ=

// 下面這個頭字段為可選字段
Sec-WebSocket-Protocols: chat

基本框架協(xié)議

數(shù)據(jù)傳輸部分對 進(jìn)行了分組!!由于是在bit層面上進(jìn)行的數(shù)據(jù)封裝,所以如果直接取出的話,獲取到的將是處理后的數(shù)據(jù),需要解密。下圖是傳輸數(shù)據(jù)格式

  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 ...                |
 +---------------------------------------------------------------+
1. 特殊名詞含義介紹

1bit,F(xiàn)IN

每個 1bit, RSV1、RSV2、RSV3

4bit,opcode(以下定義在ABNF中)

%x0 連續(xù)幀

%x1 文本幀

%x2 二進(jìn)制幀

%x3 - %x7 保留幀

%x8 鏈接關(guān)閉

%x9 ping

%xA pong

%xB-F 保留的控制幀

以上表示的都是 16 進(jìn)制數(shù)值

1bit, mask

客戶端發(fā)送給服務(wù)端的數(shù)據(jù)都需要設(shè)置為 1

也就是說數(shù)據(jù)都是經(jīng)過掩碼處理過的

7bit、7 + 16bit、7 + 64bit,Payload length 具體范圍請參閱 RFC 文檔(Page 31)

Playload length = Extended Payload length + Application Payload length

有效載荷長度 = 擴(kuò)展數(shù)據(jù)長度 + 應(yīng)用程序數(shù)據(jù)長度

擴(kuò)展數(shù)據(jù)長度有可能為 0,所以當(dāng) 擴(kuò)展數(shù)據(jù)長度 = 0 的時候,有效載荷長度 = 應(yīng)用程序長度

有效載荷數(shù)據(jù)的長度單位為 Byte

0/4 byte, masking-key

客戶端發(fā)送給服務(wù)端的數(shù)據(jù)都是經(jīng)過掩碼處理的,長度為 32bit

服務(wù)端發(fā)送給客戶端的數(shù)據(jù)都是未經(jīng)過掩碼處理的,長度為 0bit

x + y Byte, Payload Data

有效載荷數(shù)據(jù)

x Byte, Extension Data

擴(kuò)展數(shù)據(jù)

y Byte, Application Data

應(yīng)用數(shù)據(jù)

2. 理解

圖中表示遵循 websocket 協(xié)議進(jìn)行傳輸?shù)臄?shù)據(jù),由于是經(jīng)過 websocket 協(xié)議處理后的數(shù)據(jù),所以無法直接獲取有效數(shù)據(jù)。如果想要獲取有效數(shù)據(jù),就需要按照 websocket 協(xié)議規(guī)定進(jìn)行解讀。

圖中從左往右,按高位到低位進(jìn)行排列。

什么是低位、高位??

就像是十進(jìn)制數(shù)字,如果有一個描述是這樣的:3表示個位,2 表示十位,1表示百位,請問這個數(shù)字是??答案:123

這就很好理解了,個位、十位、百位 描述了排列順序;同樣的,在程序領(lǐng)域,低位到高位描述的也是排列順序!不過 個位、十位、百位描述的是10進(jìn)制的排列順序,而 低位、高位描述的是 2進(jìn)制 的排列順序,具體描述是 位0、位1、位2.... 等(當(dāng)前舉例中的的排列順序?yàn)榈臀坏礁呶?/strong>),以下是圖片描述:

理解了低位、高位,就清楚了上圖描述的數(shù)據(jù)排列順序。

眾所周知,位(bit)是內(nèi)存中的最小存儲單位,僅能存 0、1兩個數(shù)值。所以要想獲取、設(shè)置某位的值,需要進(jìn)行位操作。由于是在上進(jìn)行操作者,所以,圖中描述的內(nèi)容是在補(bǔ)碼的基礎(chǔ)上進(jìn)行的。

客戶端發(fā)送給服務(wù)端的數(shù)據(jù)是經(jīng)過掩碼處理的! 需要進(jìn)行解析,解析數(shù)據(jù)流程:

// 按照 websocket 規(guī)范解析客戶端加密數(shù)據(jù)
function decode(string $buffer){
    // buffer[0] 獲取第一個字節(jié),8bit
    // 對照那張圖,表示的是 fin + rsv1 + rsv2 + rsv 3 + opcode
    // 之所以要轉(zhuǎn)換為 ASCII 碼值
    // 是為了確保位運(yùn)算結(jié)果正確!
    // php 位運(yùn)算詳情參考:https://note.youdao.com/share/?id=927bfc2f40a8d62f4c9165de30a41e75&type=note#/
    // 這邊做一點(diǎn)簡單解釋
    // 后面的代碼會有 $first_byte >> 7 這樣的代碼
    // php 中 << >> 都會將操作數(shù)當(dāng)成是整型數(shù)(int) 
    // 所以如果不轉(zhuǎn)換成 ascii 值的話,過程將會是
    // (int) $buffer[0] >> 7
    // 這樣的結(jié)果將是錯誤的!!
    // ord((int) $buffer[0]) !== ord($buffer[0]) 就是最好的證明
    // 因?yàn)?ascii 值不一樣,則二進(jìn)制值(嚴(yán)格一點(diǎn),我認(rèn)為應(yīng)該說成是:補(bǔ)碼)也不一樣
    // 這違反了 websocket 規(guī)定的協(xié)議
    // 會導(dǎo)致解析錯誤
    $first_byte  = ord($buffer[0]);
    // buffer[1] 獲取第二個字節(jié),8bit
    // 對照那張圖,表示的是 mask + payload len
    $second_byte = ord($buffer[1]);
    
    // 獲取左邊第一位值
    $fin = $first_byte >> 7;
    // 對照那張圖,要想獲取 payload len 表示的值
    // 需要設(shè)置 位 7 為 0
    // 因?yàn)槲?7 表示的是掩碼,位 0 - 6 表示的是 paylaod len 的補(bǔ)碼
    // 所以要想獲取 payload len 的值
    // 0111 1111 => 127
    $payload_len = $second_byte & 127;
    
    // 客戶端發(fā)送給服務(wù)端的數(shù)據(jù)是經(jīng)過掩碼處理的
    // 所以要獲取 掩碼鍵 + 掩碼處理過后的客戶端數(shù)據(jù)
    // 獲取 mask-key + payload data
    if ($payload_len === 127) {
        // 如果 payload len = 127 byte
        // payload len 本身占據(jù) 7bit
        // extended payload lenght 占據(jù) 64bit
        $mask_key       = substr($buffer , 10 , 4);
        $encoded_data   = substr($buffer , 14);
    } else if ($payload_len === 126) {
        // 如果 payload len = 126 byte
        // payload length 本身占據(jù) 7bit
        // extended payload lenght 占據(jù) 16bit
        $mask_key       = substr($buffer , 4 , 4);
        $encoded_data   = substr($buffer , 8);
    } else {
        // 如果 payload len = 126 byte
        // payload length 本身占據(jù) 7bit
        // extended payload lenght 占據(jù) 0bit
        $mask_key       = substr($buffer , 2 , 4);
        $encoded_data   = substr($buffer , 6);
    }
    
    // 對 payload data 進(jìn)行解碼
    $decoded_data = "";
    
    // 對每一個有效載荷數(shù)據(jù)進(jìn)行解碼操作
    // 解碼規(guī)則在 RFC 文檔中有詳細(xì)描述
    for ($index = 0; $index < count($encoded_data); ++$index)
    {
        $k              = $index % 4;
        $valid_data     = $encoded_data[$index] ^ $mask_data[$k];
        $decoded_data  .= $valid_data;
    }
    
    // 這個就是客戶端發(fā)送的真實(shí)數(shù)據(jù)!!
    return $decoded_data;
}

相反,如果服務(wù)器想要發(fā)送數(shù)據(jù)給 websocket 客戶端,則也要對數(shù)據(jù)進(jìn)行相應(yīng)處理!處理流程:

// 按照 websocket 規(guī)范封裝發(fā)送給客戶端的消息
function encode($msg){
    if (!is_scalar($msg)) {
        print_r("只允許發(fā)送標(biāo)量數(shù)據(jù)");
    }
    
    // 數(shù)據(jù)長度
    $len = strlen($msg);
    
    // 這邊僅實(shí)現(xiàn)傳輸文本幀!第一個字節(jié),文本幀 1000 0001 => 129
    // 如果需要例如二進(jìn)制幀,用于傳輸大文件,請另行實(shí)現(xiàn)
    $first_byte = chr(129);
    
    if ($len <= 125) {
        // payload length = 7bit 支持的最大范圍!
        $second_byte = chr($len);
    } else {
        if ($len <= 65535) {
            // payload length = 7 , extended payload length = 16bit,支持的最大范圍 65535
            // 最后16bit 被解釋為無符號整數(shù),排序?yàn)椋捍蠖俗止?jié)序(網(wǎng)絡(luò)字節(jié)序)
            $second_byte = chr(126) . pack("n" , $len);
        } else {
            // payload length = 7,extended payload length = 64bit
            // 最后 64 位被解釋為無符號整數(shù),大端字節(jié)序(網(wǎng)絡(luò)字節(jié)序)
            $second_byte = chr(127) . pack("J" , $len);
        }
    }
    
    // 注意了,發(fā)送給客戶端的數(shù)據(jù)不需要處理
    // 詳情查看 websocket 文檔!!
    $encoded_data = $first_byte . $second_byte . $buffer;
    
    // 這個就是發(fā)送給客戶端的數(shù)據(jù)!   
    return $encoded_data;
}
消息分片 分片目的

消息分片的主要目的是允許消息開始但不必緩沖整個消息時,發(fā)送一個未知大小的消息;未分片的消息需要緩沖整個消息,以便獲取消息大小;

分片要求:

首個分片 Fin = 0,opcode != 0x0,其后跟隨多個 Fin = 0,opcode = 0x0的分片,終止于 Fin = 1,opcode = 0x0的片段

擴(kuò)展數(shù)據(jù)可能發(fā)生在分片中的任意一個分片中

控制幀可能被注入到分片消息的中間,控制幀本身必須不被分割

消息分片必須按照發(fā)送者發(fā)送順序交付給收件人

片段中的一個消息必須不能與片段中的另一個消息交替,除非已協(xié)商了一個能解釋交替的擴(kuò)展。

websocket服務(wù)器應(yīng)能夠處理分片消息中間的控制幀

一個發(fā)送者可以為非控制消息(非控制幀)創(chuàng)建任何大小的片段

不能處理控制幀

如果使用了任何保留的位值且這些值的意思對中間件是未知的,一個中間件必須不改變一個消息的分片。

在一個連接上下文中,已經(jīng)協(xié)商了擴(kuò)展且中間件不知道協(xié)商的擴(kuò)展的語義,一個中間件必須不改變?nèi)魏蜗⒌姆制M瑯樱瑳]有看見WebSocket握手(且沒被通知有關(guān)它的內(nèi)容)、導(dǎo)致一個WebSocket連接的一個中間件,必須不改變這個鏈接的任何消息的分片。

由于這些規(guī)則,一個消息的所有分片是相同類型,以第一個片段的操作碼設(shè)置。因?yàn)榭刂茙荒鼙环制糜谝粋€消息中的所有分片的類型必須或者是文本、或者二進(jìn)制、或者一個保留的操作碼。

ping

接受到一個 ping(0x9) 控制幀,必須返回一個 pong(0xa) 控制幀,表示進(jìn)程還在!!實(shí)際就是心跳檢查

pong

可以在接收到 ping(0x9) 控制幀后,作為響應(yīng)消息返回。

也可以單向發(fā)送 pong 幀,表示發(fā)送方進(jìn)程還在,作為單向心跳

狀態(tài)碼

1000,正常關(guān)閉

1001,正在離開

1003,正在關(guān)閉連接

1004,保留

1005,保留

1006,保留

1007,端點(diǎn)正在終止連接,因?yàn)樗盏降南⒅袥]有與消息類型一致。

1008,端點(diǎn)正在終止鏈接,因?yàn)榻邮盏搅诉`反其規(guī)則的消息。

1009,端點(diǎn)正在終止鏈接,因?yàn)榻邮艿降南⑻?/p>

1010,端點(diǎn)正在終止鏈接,因?yàn)閿U(kuò)展問題

1011,端點(diǎn)正在終止鏈接,發(fā)生了以外錯誤

1015,保留

.....省略了部分,詳情參考 rfc 文檔

尾部

以上個人理解,僅供參考,有錯歡迎糾正,未完待續(xù) ....

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/107519.html

相關(guān)文章

  • 【譯】 WebSocket 協(xié)議第十一章——IANA 注意事項(xiàng)(IANA Consideration

    摘要:概述本文為協(xié)議的第十一章,本文翻譯的主要內(nèi)容為的相關(guān)注意事項(xiàng)。應(yīng)用協(xié)議使用這個協(xié)議規(guī)范互操作性注意事項(xiàng)使用時需要使用或者更高版本的協(xié)議。安全性注意事項(xiàng)見安全性注意事項(xiàng)一節(jié)。 概述 本文為 WebSocket 協(xié)議的第十一章,本文翻譯的主要內(nèi)容為 WebSocket 的 IANA 相關(guān)注意事項(xiàng)。 IANA 注意事項(xiàng)(協(xié)議正文) 11.1 注冊新 URI 協(xié)議 11.1.1 注冊 ws 協(xié)...

    amc 評論0 收藏0
  • WebSocket系列之二進(jìn)制數(shù)據(jù)設(shè)計與傳輸

    摘要:本文作為系列的第四篇內(nèi)容,將會用一個簡單的聊天應(yīng)用把整個傳輸二進(jìn)制數(shù)據(jù)類型的內(nèi)容連接起來,讓用戶對整個傳輸二進(jìn)制數(shù)據(jù)的方法有個了解。如何發(fā)送二進(jìn)制數(shù)據(jù)通過如何設(shè)計一個二進(jìn)制協(xié)議一章,我們知道了如何定義傳輸?shù)亩M(jìn)制數(shù)據(jù)格式。 概述 通過前三篇博客,我們能夠了解在通過WebSocket發(fā)送數(shù)據(jù)之前,我們需要傳遞的數(shù)據(jù)是如何變成ArrayBuffer二進(jìn)制數(shù)據(jù)的;在我們收到二進(jìn)制數(shù)據(jù)之后,我...

    amc 評論0 收藏0
  • webSocket原理探索

    摘要:幀是發(fā)送數(shù)據(jù)的基本單位,下邊是它的報文格式報文內(nèi)容中規(guī)定了數(shù)據(jù)標(biāo)示操作代碼掩碼數(shù)據(jù)數(shù)據(jù)長度等格式。首先我們明白了客戶端和服務(wù)端進(jìn)行消息傳遞是這樣的客戶端將消息切割成多個幀,并發(fā)送給服務(wù)端。服務(wù)端接收消息幀,并將關(guān)聯(lián)的幀重新組裝成完整的消息。 本文概述 Web Sockets的目標(biāo)是在一個單獨(dú)的持久連接上提供全雙工、雙向通信。在Javascript創(chuàng)建了Web Socket之后,會有一個...

    baukh789 評論0 收藏0
  • webSocket原理探索

    摘要:幀是發(fā)送數(shù)據(jù)的基本單位,下邊是它的報文格式報文內(nèi)容中規(guī)定了數(shù)據(jù)標(biāo)示操作代碼掩碼數(shù)據(jù)數(shù)據(jù)長度等格式。首先我們明白了客戶端和服務(wù)端進(jìn)行消息傳遞是這樣的客戶端將消息切割成多個幀,并發(fā)送給服務(wù)端。服務(wù)端接收消息幀,并將關(guān)聯(lián)的幀重新組裝成完整的消息。 本文概述 Web Sockets的目標(biāo)是在一個單獨(dú)的持久連接上提供全雙工、雙向通信。在Javascript創(chuàng)建了Web Socket之后,會有一個...

    WelliJhon 評論0 收藏0

發(fā)表評論

0條評論

2i18ns

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<