摘要:數(shù)據(jù)幀協(xié)議正文概覽在協(xié)議中,數(shù)據(jù)是通過(guò)一系列數(shù)據(jù)幀來(lái)進(jìn)行傳輸?shù)?。注意無(wú)論協(xié)議是否使用了,幀都需要添加掩碼。服務(wù)端收到?jīng)]有添加掩碼的數(shù)據(jù)幀以后,必須立即關(guān)閉連接。服務(wù)端禁止在發(fā)送數(shù)據(jù)幀給客戶端時(shí)添加掩碼。
概述
本文為WebSocket協(xié)議的第五章,本文翻譯的主要內(nèi)容為WebSocket傳輸?shù)臄?shù)據(jù)相關(guān)內(nèi)容。
數(shù)據(jù)幀(協(xié)議正文) 5.1 概覽在WebSocket協(xié)議中,數(shù)據(jù)是通過(guò)一系列數(shù)據(jù)幀來(lái)進(jìn)行傳輸?shù)摹榱吮苊庥捎诰W(wǎng)絡(luò)中介(例如一些攔截代理)或者一些在第10.3節(jié)討論的安全原因,客戶端必須在它發(fā)送到服務(wù)器的所有幀中添加掩碼(Mask)(具體細(xì)節(jié)見(jiàn)5.3節(jié))。(注意:無(wú)論WebSocket協(xié)議是否使用了TLS,幀都需要添加掩碼)。服務(wù)端收到?jīng)]有添加掩碼的數(shù)據(jù)幀以后,必須立即關(guān)閉連接。在這種情況下,服務(wù)端可以發(fā)送一個(gè)在7.4.1節(jié)定義的狀態(tài)碼為1002(協(xié)議錯(cuò)誤)的關(guān)閉幀。服務(wù)端禁止在發(fā)送數(shù)據(jù)幀給客戶端時(shí)添加掩碼??蛻舳巳绻盏搅艘粋€(gè)添加了掩碼的幀,必須立即關(guān)閉連接。在這種情況下,它可以使用第7.4.1節(jié)定義的1002(協(xié)議錯(cuò)誤)狀態(tài)碼。(這些規(guī)則可能會(huì)在將來(lái)的規(guī)范中放開(kāi))。
基礎(chǔ)的數(shù)據(jù)幀協(xié)議使用操作碼、有效負(fù)載長(zhǎng)度和在“有效負(fù)載數(shù)據(jù)”中定義的放置“擴(kuò)展數(shù)據(jù)”與“引用數(shù)據(jù)”的指定位置來(lái)定義幀類型。特定的bit位和操作碼為將來(lái)的協(xié)議擴(kuò)展做了保留。
一個(gè)數(shù)據(jù)幀可以在開(kāi)始握手完成之后和終端發(fā)送了一個(gè)關(guān)閉幀之前的任意一個(gè)時(shí)間通過(guò)客戶端或者服務(wù)端進(jìn)行傳輸(第5.5.1節(jié))。
5.2 基礎(chǔ)幀協(xié)議在這節(jié)中的這種數(shù)據(jù)傳輸部分的有線格式是通過(guò)ABNFRFC5234來(lái)進(jìn)行詳細(xì)說(shuō)明的。(注意:不像這篇文檔中的其他章節(jié)內(nèi)容,在這節(jié)中的ABNF是對(duì)bit組進(jìn)行操作。每一個(gè)bit組的長(zhǎng)度是在評(píng)論中展示的。在線上編碼時(shí),最高位的bit是在ABNF最左邊的)。對(duì)于數(shù)據(jù)幀的高級(jí)的預(yù)覽可以見(jiàn)下圖。如果下圖指定的內(nèi)容和這一節(jié)中后面的ABNF指定的內(nèi)容有沖突的話,以下圖為準(zhǔn)。
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 bit
? 表示這是消息的最后一個(gè)片段。第一個(gè)片段也有可能是最后一個(gè)片段。
RSV1,RSV2,RSV3: 每個(gè)1 bit
? 必須設(shè)置為0,除非擴(kuò)展了非0值含義的擴(kuò)展。如果收到了一個(gè)非0值但是沒(méi)有擴(kuò)展任何非0值的含義,接收終端必須斷開(kāi)WebSocket連接。
Opcode: 4 bit
? 定義“有效負(fù)載數(shù)據(jù)”的解釋。如果收到一個(gè)未知的操作碼,接收終端必須斷開(kāi)WebSocket連接。下面的值是被定義過(guò)的。
? %x0 表示一個(gè)持續(xù)幀
? %x1 表示一個(gè)文本幀
? %x2 表示一個(gè)二進(jìn)制幀
? %x3-7 預(yù)留給以后的非控制幀
? %x8 表示一個(gè)連接關(guān)閉包
? %x9 表示一個(gè)ping包
? %xA 表示一個(gè)pong包
? %xB-F 預(yù)留給以后的控制幀
Mask: 1 bit
? mask標(biāo)志位,定義“有效負(fù)載數(shù)據(jù)”是否添加掩碼。如果設(shè)置為1,那么掩碼的鍵值存在于Masking-Key中,根據(jù)5.3節(jié)描述,這個(gè)一般用于解碼“有效負(fù)載數(shù)據(jù)”。所有的從客戶端發(fā)送到服務(wù)端的幀都需要設(shè)置這個(gè)bit位為1。
Payload length: 7 bits, 7+16 bits, or 7+64 bits
? 以字節(jié)為單位的“有效負(fù)載數(shù)據(jù)”長(zhǎng)度,如果值為0-125,那么就表示負(fù)載數(shù)據(jù)的長(zhǎng)度。如果是126,那么接下來(lái)的2個(gè)bytes解釋為16bit的無(wú)符號(hào)整形作為負(fù)載數(shù)據(jù)的長(zhǎng)度。如果是127,那么接下來(lái)的8個(gè)bytes解釋為一個(gè)64bit的無(wú)符號(hào)整形(最高位的bit必須為0)作為負(fù)載數(shù)據(jù)的長(zhǎng)度。多字節(jié)長(zhǎng)度量以網(wǎng)絡(luò)字節(jié)順序表示(譯注:應(yīng)該是指大端序和小端序)。在所有的示例中,長(zhǎng)度值必須使用最小字節(jié)數(shù)來(lái)進(jìn)行編碼,例如:長(zhǎng)度為124字節(jié)的字符串不可用使用序列126,0,124進(jìn)行編碼。有效負(fù)載長(zhǎng)度是指“擴(kuò)展數(shù)據(jù)”+“應(yīng)用數(shù)據(jù)”的長(zhǎng)度?!皵U(kuò)展數(shù)據(jù)”的長(zhǎng)度可能為0,那么有效負(fù)載長(zhǎng)度就是“應(yīng)用數(shù)據(jù)”的長(zhǎng)度。
Masking-Key: 0 or 4 bytes
? 所有從客戶端發(fā)往服務(wù)端的數(shù)據(jù)幀都已經(jīng)與一個(gè)包含在這一幀中的32 bit的掩碼進(jìn)行過(guò)了運(yùn)算。如果mask標(biāo)志位(1 bit)為1,那么這個(gè)字段存在,如果標(biāo)志位為0,那么這個(gè)字段不存在。在5.3節(jié)中會(huì)介紹更多關(guān)于客戶端到服務(wù)端增加掩碼的信息。
Payload data: (x+y) bytes
? “有效負(fù)載數(shù)據(jù)”是指“擴(kuò)展數(shù)據(jù)”和“應(yīng)用數(shù)據(jù)”。
Extension data: x bytes
? 除非協(xié)商過(guò)擴(kuò)展,否則“擴(kuò)展數(shù)據(jù)”長(zhǎng)度為0 bytes。在握手協(xié)議中,任何擴(kuò)展都必須指定“擴(kuò)展數(shù)據(jù)”的長(zhǎng)度,這個(gè)長(zhǎng)度如何進(jìn)行計(jì)算,以及這個(gè)擴(kuò)展如何使用。如果存在擴(kuò)展,那么這個(gè)“擴(kuò)展數(shù)據(jù)”包含在總的有效負(fù)載長(zhǎng)度中。
Application data: y bytes
? 任意的“應(yīng)用數(shù)據(jù)”,占用“擴(kuò)展數(shù)據(jù)”后面的剩余所有字段?!皯?yīng)用數(shù)據(jù)”的長(zhǎng)度等于有效負(fù)載長(zhǎng)度減去“擴(kuò)展應(yīng)用”長(zhǎng)度。
基礎(chǔ)數(shù)據(jù)幀協(xié)議通過(guò)ABNF進(jìn)行了正式的定義。需要重點(diǎn)知道的是,這些數(shù)據(jù)都是二進(jìn)制的,而不是ASCII字符。例如,長(zhǎng)度為1 bit的字段的值為%x0 / %x1代表的是一個(gè)值為0/1的多帶帶的bit,而不是一整個(gè)字節(jié)(8 bit)來(lái)代表ASCII編碼的字符“0”和“1”。一個(gè)長(zhǎng)度為4 bit的范圍是%x0-F的字段值代表的是4個(gè)bit,而不是字節(jié)(8 bit)對(duì)應(yīng)的ASCII碼的值。不要指定字符編碼:“規(guī)則解析為一組最終的值,有時(shí)候是字符。在ABNF中,字符僅僅是一個(gè)非負(fù)的數(shù)字。在特定的上下文中,會(huì)根據(jù)特定的值的映射(編碼)編碼集(例如ASCII)”。在這里,指定的編碼類型是將每個(gè)字段編碼為特定的bits數(shù)組的二進(jìn)制編碼的最終數(shù)據(jù)。
ws-frame =
frame-fin; 長(zhǎng)度為1 bit
frame-rsv1; 長(zhǎng)度為1 bit
frame-rsv2; 長(zhǎng)度為1 bit
frame-rsv3; 長(zhǎng)度為1 bit
frame-opcode; 長(zhǎng)度為4 bit
frame-masked; 長(zhǎng)度為1 bit
frame-payload-length; 長(zhǎng)度為7或者7+16或者7+64 bit
[frame-masking-key]; 長(zhǎng)度為32 bit
frame-payload-data; 長(zhǎng)度為大于0的n*8 bit(其中n>0)
frame-fin =
%x0,除了以下為1的情況
%x1,最后一個(gè)消息幀
長(zhǎng)度為1 bit
frame-rsv1 =
%x0 / %x1,長(zhǎng)度為1 bit,如果沒(méi)有協(xié)商則必須為0
frame-rsv2 =
%x0 / %x1,長(zhǎng)度為1 bit,如果沒(méi)有協(xié)商則必須為0
frame-rsv3 =
%x0 / %x1,長(zhǎng)度為1 bit,如果沒(méi)有協(xié)商則必須為0
frame-opcode =
frame-opcode-non-control
frame-opcode-control
frame-opcode-cont
frame-opcode-non-control
%x1,文本幀
%x2,二進(jìn)制幀
%x3-7,保留給將來(lái)的非控制幀
長(zhǎng)度為4 bit
frame-opcode-control
%x8,連接關(guān)閉
%x9,ping幀
%xA,pong幀
%xB-F,保留給將來(lái)的控制幀
長(zhǎng)度為4 bit
frame-masked
%x0,不添加掩碼,沒(méi)有frame-masking-key
%x1,添加掩碼,存在frame-masking-key
長(zhǎng)度為1 bit
frame-payload-length
%x00-7D,長(zhǎng)度為7 bit
%x7E frame-payload-length-16,長(zhǎng)度為7+16 bit
%x7F frame-payload-length-63,長(zhǎng)度為7+64 bit
frame-payload-length-16
%x0000-FFFF,長(zhǎng)度為16 bit
frame-payload-length-63
%x0000000000000000-7FFFFFFFFFFFFFFF,長(zhǎng)度為64 bit
frame-masking-key
4(%x00-FF),當(dāng)frame-mask為1時(shí)存在,長(zhǎng)度為32 bit
frame-payload-data
frame-masked-extension-data frame-masked-application-data,當(dāng)frame-masked為1時(shí)
frame-unmasked-extension-data frame-unmasked-application-data,當(dāng)frame-masked為0時(shí)
frame-masked-extension-data
*(%x00-FF),保留給將來(lái)的擴(kuò)展,長(zhǎng)度為n*8,其中n>0
frame-masked-application-data
*(%x00-FF),長(zhǎng)度為n*8,其中n>0
frame-unmasked-extension-data
*(%x00-FF),保留給將來(lái)的擴(kuò)展,長(zhǎng)度為n*8,其中n>0
frame-unmasked-application-data
*(%x00-FF),長(zhǎng)度為n*8,其中n>0
5.3 客戶端到服務(wù)端添加掩碼添加掩碼的數(shù)據(jù)幀必須像5.2節(jié)定義的一樣,設(shè)置frame-masked字段為1。
掩碼值像第5.2節(jié)說(shuō)到的完全包含在幀中的frame-masking-key上。它是用于對(duì)定義在同一節(jié)中定義的幀負(fù)載數(shù)據(jù)Payload data字段中的包含Extension data和Application data的數(shù)據(jù)進(jìn)行添加掩碼。
掩碼字段是一個(gè)由客戶端隨機(jī)選擇的32bit的值。當(dāng)準(zhǔn)備掩碼幀時(shí),客戶端必須從允許的32bit值中須知你咋一個(gè)新的掩碼值。掩碼值必須是不可被預(yù)測(cè)的;因此,掩碼必須來(lái)自強(qiáng)大的熵源(entropy),并且給定的掩碼不能讓服務(wù)器或者代理能夠很容易的預(yù)測(cè)到后續(xù)幀。掩碼的不可預(yù)測(cè)性對(duì)于預(yù)防惡意應(yīng)用作者在網(wǎng)上暴露相關(guān)的字節(jié)數(shù)據(jù)至關(guān)重要。RFC 4086討論了安全敏感的應(yīng)用需要一個(gè)什么樣的合適的強(qiáng)大的熵源。
掩碼不影響Payload data的長(zhǎng)度。進(jìn)行掩碼的數(shù)據(jù)轉(zhuǎn)換為非掩碼數(shù)據(jù),或者反過(guò)來(lái),根據(jù)下面的算法即可。這個(gè)同樣的算法適用于任意操作方向的轉(zhuǎn)換,例如:對(duì)數(shù)據(jù)進(jìn)行掩碼操作和對(duì)數(shù)據(jù)進(jìn)行反掩碼操作所涉及的步驟是相同的。
表示轉(zhuǎn)換后數(shù)據(jù)的八位字節(jié)的i(transformed-octet-i?)是表示的原始數(shù)據(jù)的i(original-octet-i)與索引i模4得到的掩碼值(masking-key-octet-j)經(jīng)過(guò)異或操作(XOR)得到的:
j = i MOD 4
transfromed-octed-i = original-octet-i XOR masking-key-octet-j
在規(guī)范中定義的位于frame-payload-length字段的有效負(fù)載的長(zhǎng)度,不包括掩碼值的長(zhǎng)度。它只是Payload data的長(zhǎng)度。如跟在掩碼值后面的字節(jié)數(shù)組的數(shù)。
5.4 消息分片消息分片的主要目的是允許發(fā)送一個(gè)未知長(zhǎng)度且消息開(kāi)始發(fā)送后不需要緩存的消息。如果消息不能被分片,那么一端必須在緩存整個(gè)消息,因此這個(gè)消息的長(zhǎng)度必須在第一個(gè)字節(jié)發(fā)送前就需要計(jì)算出來(lái)。如果有消息分片,服務(wù)端或者代理可以選擇一個(gè)合理的緩存長(zhǎng)度,當(dāng)緩存區(qū)滿了以后,就想網(wǎng)絡(luò)發(fā)送一個(gè)片段。
第二個(gè)消息分片使用的場(chǎng)景是不適合在一個(gè)邏輯通道內(nèi)傳輸一個(gè)大的消息占滿整個(gè)輸出頻道的多路復(fù)用場(chǎng)景。多路復(fù)用需要能夠?qū)⑾⑦M(jìn)行自由的切割成更小的片段來(lái)共享輸出頻道。(注意:多路復(fù)用的擴(kuò)展不在這個(gè)文檔中討論)。
除非在擴(kuò)展中另有規(guī)定,否則幀沒(méi)有語(yǔ)義的含義。如果客戶端和服務(wù)的沒(méi)有協(xié)商擴(kuò)展字段,或者服務(wù)端和客戶端協(xié)商了一些擴(kuò)展字段,并且代理能夠完全識(shí)別所有的協(xié)商擴(kuò)展字段,在這些擴(kuò)展字段存在的情況下知道如何進(jìn)行幀的合并和拆分,代理就可能會(huì)合并或者拆分幀。這個(gè)的一個(gè)含義是指在缺少擴(kuò)展字段的情況下,發(fā)送者和接收者都不能依賴特定的幀邊界的存在。
消息分片相關(guān)的規(guī)則如下:
一個(gè)未分片的消息包含一個(gè)設(shè)置了FIN字段(標(biāo)記為1)的多帶帶的幀和一個(gè)除0以外的操作碼。
一個(gè)分片的消息包含一個(gè)未設(shè)置的FIN字段(標(biāo)記為0)的多帶帶的幀和一個(gè)除0以外的操作碼,然后跟著0個(gè)或者多個(gè)未設(shè)置FIN字段的幀和操作碼為0的幀,然后以一個(gè)設(shè)置了FIN字段以及操作碼為0的幀結(jié)束。一個(gè)分片的消息內(nèi)容按幀順序組合后的payload字段,是等價(jià)于一個(gè)多帶帶的更大的消息payload字段中包含的值;然而,如果擴(kuò)展字段存在,因?yàn)閿U(kuò)展字段定義了Extension data的解析方式,因此前面的結(jié)論可能不成立。例如:Extension data可能只出現(xiàn)在第一個(gè)片段的開(kāi)頭,并適用于接下來(lái)的片段,或者可能每一個(gè)片段都有Extension data,但是只適用于特定的片段。在Extension data不存在時(shí),下面的示例演示了消息分片是如何運(yùn)作的。
示例:一個(gè)文本需要分成三個(gè)片段進(jìn)行發(fā)送,第一個(gè)片段包含的操作碼為0x1并且未設(shè)置FIN字段,第二個(gè)片段的操作碼為0x0并且未設(shè)置FIN字段,第三個(gè)片段的操作碼為0x0并且設(shè)置了FIN字段。
控制幀(見(jiàn)5.5節(jié))可能被插入到分片消息的中間??刂茙荒鼙环制?/p>
消息片段必須在發(fā)送端按照順序發(fā)送給接收端。
除非在擴(kuò)展中定義了這種嵌套的邏輯,否則一條消息分的片不能與另一條消息分的片嵌套傳輸。
終端必須有能力來(lái)處理在分片的消息中的控制幀。
發(fā)送端可能會(huì)創(chuàng)建任意大小的非控制消息片段。
客戶端和服務(wù)端必須同時(shí)支持分片和不分片消息。
控制幀不能被分片,并且代理不允許改變控制幀的片段。
如果有保留字段被使用并且代理不能理解這些字段的值時(shí),那么代理不能改變消息的片段。
在擴(kuò)展字段已經(jīng)被協(xié)商過(guò),但是代理不知道協(xié)商擴(kuò)展字段的具體語(yǔ)義時(shí),代理不能改變?nèi)我庀⒌钠巍M瑯拥?,擴(kuò)展不能看到WebSocket握手(并且得不到通知內(nèi)容)導(dǎo)致WebSocket的連接禁止改變連接過(guò)程中任意的消息片段。
作為這些規(guī)則的結(jié)論,所有的消息片段都是同類型的,并且設(shè)置了第一個(gè)片段的操作碼(opccode)字段。控制幀不能被分片,所有的消息分片類型必須是文本或者二進(jìn)制,或者是保留的任意一個(gè)操作碼。
注:如果控制幀沒(méi)有被打斷,心跳(ping)的等待時(shí)間可能會(huì)變很長(zhǎng),例如在一個(gè)很大的消息之后。因此,在分片的消息傳輸中插入控制幀是有必要的。
實(shí)踐說(shuō)明:如果擴(kuò)展字段不存在,接收者不需要使用緩存來(lái)存儲(chǔ)下整個(gè)消息片段來(lái)進(jìn)行處理。例如:如果使用一個(gè)流式API,再收到部分幀的時(shí)候就可以將數(shù)據(jù)交給上層應(yīng)用。然而,這個(gè)假設(shè)對(duì)以后所有的WebSocket擴(kuò)展可能不一定成立。
5.5 控制幀控制幀是通過(guò)操作碼最高位的值為1來(lái)進(jìn)行區(qū)分的。當(dāng)前已經(jīng)定義的控制幀操作碼包括0x8(關(guān)閉),0x9(心跳Ping)和0xA(心跳Pong)。操作碼0xB-0xF沒(méi)有被定義,當(dāng)前被保留下來(lái)做為以后的控制幀。
控制幀是用于WebSocket的通信狀態(tài)的。控制幀可以被插入到消息片段中進(jìn)行傳輸。
所有的控制幀必須有一個(gè)126字節(jié)或者更小的負(fù)載長(zhǎng)度,并且不能被分片。
5.5.1 關(guān)閉(Close)控制幀的操作碼值是0x8。
關(guān)閉幀可能包含內(nèi)容(body)(幀的“應(yīng)用數(shù)據(jù)”部分)來(lái)表明連接關(guān)閉的原因,例如終端的斷開(kāi),或者是終端收到了一個(gè)太大的幀,或者是終端收到了一個(gè)不符合預(yù)期的格式的內(nèi)容。如果這個(gè)內(nèi)容存在,內(nèi)容的前兩個(gè)字節(jié)必須是一個(gè)無(wú)符號(hào)整型(按照網(wǎng)絡(luò)字節(jié)序)來(lái)代表在7.4節(jié)中定義的狀態(tài)碼。跟在這兩個(gè)整型字節(jié)之后的可以是UTF-8編碼的的數(shù)據(jù)值(原因),數(shù)據(jù)值的定義不在此文檔中。數(shù)據(jù)值不一定是要人可以讀懂的,但是必須對(duì)于調(diào)試有幫助,或者能傳遞有關(guān)于當(dāng)前打開(kāi)的這條連接有關(guān)聯(lián)的信息。數(shù)據(jù)值不保證人一定可以讀懂,所以不能把這些展示給終端用戶。
從客戶端發(fā)送給服務(wù)端的控制幀必須添加掩碼,具體見(jiàn)5.3節(jié)。
應(yīng)用禁止在發(fā)送了關(guān)閉的控制幀后再發(fā)送任何的數(shù)據(jù)幀。
如果終端收到了一個(gè)關(guān)閉的控制幀并且沒(méi)有在以前發(fā)送一個(gè)關(guān)閉幀,那么終端必須發(fā)送一個(gè)關(guān)閉幀作為回應(yīng)。(當(dāng)發(fā)送一個(gè)關(guān)閉幀作為回應(yīng)時(shí),終端通常會(huì)輸出它收到的狀態(tài)碼)響應(yīng)的關(guān)閉幀應(yīng)該盡快發(fā)送。終端可能會(huì)推遲發(fā)送關(guān)閉幀直到當(dāng)前的消息都已經(jīng)發(fā)送完成(例如:如果大多數(shù)分片的消息已經(jīng)發(fā)送了,終端可能會(huì)在發(fā)送關(guān)閉幀之前將剩余的消息片段發(fā)送出去)。然而,已經(jīng)發(fā)送關(guān)閉幀的終端不能保證會(huì)繼續(xù)處理收到的消息。
在已經(jīng)發(fā)送和收到了關(guān)閉幀后,終端認(rèn)為WebSocket連接以及關(guān)閉了,并且必須關(guān)閉底層的TCP連接。服務(wù)端必須馬上關(guān)閉底層的TCP連接,客戶端應(yīng)該等待服務(wù)端關(guān)閉連接,但是也可以在收到關(guān)閉幀以后任意時(shí)間關(guān)閉連接。例如:如果在合理的時(shí)間段內(nèi)沒(méi)有收到TCP關(guān)閉指令。
如果客戶端和服務(wù)端咋同一個(gè)時(shí)間發(fā)送了關(guān)閉幀,兩個(gè)終端都會(huì)發(fā)送和接收到一條關(guān)閉的消息,并且應(yīng)該認(rèn)為WebSocket連接已經(jīng)關(guān)閉,同時(shí)關(guān)閉底層的TCP連接。
5.5.2 心跳Ping心跳Ping幀包含的操作碼是0x9。
關(guān)閉幀可能包含“應(yīng)用數(shù)據(jù)”。
如果收到了一個(gè)心跳Ping幀,那么終端必須發(fā)送一個(gè)心跳Pong 幀作為回應(yīng),除非已經(jīng)收到了一個(gè)關(guān)閉幀。終端應(yīng)該盡快恢復(fù)Pong幀。Pong幀將會(huì)在5.5.3節(jié)討論。
終端可能會(huì)在建立連接后與連接關(guān)閉前中間的任意時(shí)間發(fā)送Ping幀。
注意:Ping幀可能是用于?;罨蛘哂脕?lái)驗(yàn)證遠(yuǎn)端是否仍然有應(yīng)答。
5.5.3 心跳Pong心跳Ping幀包含的操作碼是0xA。
5.5.2節(jié)詳細(xì)說(shuō)明了Ping幀和Pong幀的要求。
作為回應(yīng)發(fā)送的Pong幀必須完整攜帶Ping幀中傳遞過(guò)來(lái)的“應(yīng)用數(shù)據(jù)”字段。
如果終端收到一個(gè)Ping幀但是沒(méi)有發(fā)送Pong幀來(lái)回應(yīng)之前的pong幀,那么終端可能選擇用Pong幀來(lái)回復(fù)最近處理的那個(gè)Ping幀。
Pong幀可以被主動(dòng)發(fā)送。這會(huì)作為一個(gè)單項(xiàng)的心跳。預(yù)期外的Pong包的響應(yīng)沒(méi)有規(guī)定。
5.6 數(shù)據(jù)幀數(shù)據(jù)幀(例如非控制幀)的定義是操作碼的最高位值為0。當(dāng)前定義的數(shù)據(jù)幀操作嗎包含0x1(文本)、0x2(二進(jìn)制)。操作碼0x3-0x7是被保留作為非控制幀的操作碼。
數(shù)據(jù)幀會(huì)攜帶應(yīng)用層/擴(kuò)展層數(shù)據(jù)。操作碼決定了攜帶的數(shù)據(jù)解析方式:
文本
“負(fù)載字段”是用UTF-8編碼的文本數(shù)據(jù)。注意特殊的文本幀可能包含部分UTF-8序列;然而,整個(gè)消息必須是有效的UTF-8編碼數(shù)據(jù)。重新組合消息后無(wú)效的UTF-8編碼數(shù)據(jù)處理見(jiàn)8.1節(jié)。
二進(jìn)制
“負(fù)載字段”是任意的二進(jìn)制數(shù)據(jù),二進(jìn)制數(shù)據(jù)的解析僅僅依靠應(yīng)用層。
5.7 示例一個(gè)單幀未添加掩碼的文本消息
0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (內(nèi)容為"Hello")
一個(gè)單幀添加掩碼的文本消息
0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (內(nèi)容為Hello")
一個(gè)分片的未添加掩碼的文本消息
0x01 0x03 0x48 0x65 0x6c (內(nèi)容為"Hel")
0x80 0x02 0x6c 0x6f (內(nèi)容為”lo")
未添加掩碼的Ping請(qǐng)求和添加掩碼的Ping響應(yīng)(譯者注:即Pong)
0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (包含內(nèi)容為”Hello", 但是文本內(nèi)容是任意的)
0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (包含內(nèi)容為”Hello", 匹配ping的內(nèi)容)
256字節(jié)的二進(jìn)制數(shù)據(jù)放入一個(gè)未添加掩碼數(shù)據(jù)幀
0x82 0x7E 0x0100 [256 bytes of binary data]
64KB二進(jìn)制數(shù)據(jù)在一個(gè)非掩碼幀中
0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]
這個(gè)協(xié)議的設(shè)計(jì)初衷是允許擴(kuò)展的,可以在基礎(chǔ)協(xié)議上增加能力。終端的連接必須在握手的過(guò)程中協(xié)商使用的所有擴(kuò)展。在規(guī)范中提供了從0x3-0x7和0xB-0xF的操作碼,在數(shù)據(jù)幀Header中的“擴(kuò)展數(shù)據(jù)”字段、frame-rsv1、frame-rsv2、frame-rsv3字段都可以用于擴(kuò)展。擴(kuò)展的協(xié)商討論將在以后的9.1節(jié)中詳細(xì)討論。下面是一些符合預(yù)期的擴(kuò)展用法。下面的列表不完整,也不是規(guī)范中內(nèi)容。
“擴(kuò)展數(shù)據(jù)”可以放置在“負(fù)載數(shù)據(jù)“中的應(yīng)用數(shù)據(jù)”之前的位置。
保留的字段可以在每一幀需要時(shí)被使用。
保留的操作碼的值可以被定義。
如果需要更多的操作碼,那么保留的操作碼字段可以被定義。
保留的字段或者“擴(kuò)展”操作碼可以在“負(fù)載數(shù)據(jù)”之中的分配額外的位置來(lái)定義,這樣可以定義更大的操作碼或者更多的每一幀的字段。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101884.html
摘要:概述本文為協(xié)議的第五章,本文翻譯的主要內(nèi)容為傳輸?shù)臄?shù)據(jù)相關(guān)內(nèi)容。注意無(wú)論協(xié)議是否使用了,幀都需要添加掩碼。服務(wù)端收到?jīng)]有添加掩碼的數(shù)據(jù)幀以后,必須立即關(guān)閉連接。服務(wù)端禁止在發(fā)送數(shù)據(jù)幀給客戶端時(shí)添加掩碼。基礎(chǔ)數(shù)據(jù)幀協(xié)議通過(guò)進(jìn)行了正式的定義。 概述 本文為WebSocket協(xié)議的第五章,本文翻譯的主要內(nèi)容為WebSocket傳輸?shù)臄?shù)據(jù)相關(guān)內(nèi)容。 有興趣了解該文檔之前幾張內(nèi)容的同學(xué)可以見(jiàn): ...
摘要:概述經(jīng)過(guò)半年的搗鼓,終于將協(xié)議全篇翻譯完成?,F(xiàn)在將所有章節(jié)全部整理到一篇文章中,方便大家閱讀。如果大家想看具體的翻譯文檔,可以去我的中查看。大家有相關(guān)類型的需要,建議大家可以嘗試下。 概述 經(jīng)過(guò)半年的搗鼓,終于將 WebSocket 協(xié)議(RFC6455)全篇翻譯完成。現(xiàn)在將所有章節(jié)全部整理到一篇文章中,方便大家閱讀。如果大家想看具體的翻譯文檔,可以去我的GitHub中查看。 具體章節(jié)...
摘要:預(yù)備工作序最近正在研究相關(guān)的知識(shí),想著如何能自己實(shí)現(xiàn)協(xié)議。監(jiān)聽(tīng)事件就是協(xié)議的抽象,直接在上面監(jiān)聽(tīng)已有的事件和事件這兩個(gè)事件。表示當(dāng)前數(shù)據(jù)幀為消息的最后一個(gè)數(shù)據(jù)幀,此時(shí)接收方已經(jīng)收到完整的消息,可以對(duì)消息進(jìn)行處理。 A、預(yù)備工作 1、序 最近正在研究 Websocket 相關(guān)的知識(shí),想著如何能自己實(shí)現(xiàn) Websocket 協(xié)議。到網(wǎng)上搜羅了一番資料后用 Node.js 實(shí)現(xiàn)該協(xié)議,倒也沒(méi)...
摘要:用實(shí)現(xiàn)簡(jiǎn)單協(xié)議從瀏覽器說(shuō)起瀏覽器提供的非常簡(jiǎn)潔。創(chuàng)建連接連接建立時(shí)的回調(diào)收到消息時(shí)的回調(diào)連接出錯(cuò)時(shí)的回調(diào)連接終止時(shí)的回調(diào)發(fā)送消息告訴我們也就是說(shuō),在創(chuàng)建對(duì)象時(shí),瀏覽器嘗試與服務(wù)端建立連接發(fā)送請(qǐng)求建立個(gè)服務(wù)端一旦收到數(shù)據(jù),就會(huì)觸發(fā)。 用 Node 實(shí)現(xiàn)簡(jiǎn)單 WebSocket 協(xié)議 從瀏覽器 WebSocket API 說(shuō)起 瀏覽器提供的 WebSocket API 非常簡(jiǎn)潔。 let...
摘要:幀協(xié)議讓我們深入了解下幀協(xié)議。目前可用的值該幀接續(xù)前面一幀的有效載荷。該幀包含二進(jìn)制數(shù)據(jù)。幀有以下幾類長(zhǎng)度表示有效載荷的長(zhǎng)度。數(shù)據(jù)分片有效載荷數(shù)據(jù)可以被分成多個(gè)獨(dú)立的幀。接收端會(huì)緩沖這些幀直到位有值。 原文請(qǐng)查閱這里,略有改動(dòng),本文采用知識(shí)共享署名 3.0 中國(guó)大陸許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原...
閱讀 2226·2021-11-22 13:54
閱讀 3384·2019-08-29 12:25
閱讀 3448·2019-08-28 18:29
閱讀 3594·2019-08-26 13:40
閱讀 3284·2019-08-26 13:32
閱讀 970·2019-08-26 11:44
閱讀 2238·2019-08-23 17:04
閱讀 2979·2019-08-23 17:02