HTTP/2筆記之幀
零。前言
客戶端和服務(wù)器端一旦握手協(xié)商成功接建立連接,端點(diǎn)之間可以基于HTTP/2協(xié)議傳遞交換幀數(shù)據(jù)了。
一。幀通用格式
下圖為HTTP/2幀通用格式:幀頭+負(fù)載的比特位通用結(jié)構(gòu):
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | Type (8) | Flags (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0...) ... +---------------------------------------------------------------+
幀頭為固定的9個(gè)字節(jié)((24+8+8+1+31)/8=9)呈現(xiàn),變化的為幀的負(fù)載(payload),負(fù)載內(nèi)容是由幀類型(Type)定義。
- 幀長(zhǎng)度Length:無符號(hào)的自然數(shù),24個(gè)比特表示,僅表示幀負(fù)載所占用字節(jié)數(shù),不包括幀頭所占用的9個(gè)字節(jié)。默認(rèn)大小區(qū)間為為0~16,384(2^14),一旦超過默認(rèn)最大值2^14(16384),發(fā)送方將不再允許發(fā)送,除非接收到接收方定義的SETTINGS_MAX_FRAME_SIZE(一般此值區(qū)間為2^14 ~ 2^24)值的通知。
- 幀類型Type:8個(gè)比特表示,定義了幀負(fù)載的具體格式和幀的語義,HTTP/2規(guī)范定義了10個(gè)幀類型,這里不包括實(shí)驗(yàn)類型幀和擴(kuò)展類型幀
- 幀的標(biāo)志位Flags:8個(gè)比特表示,服務(wù)于具體幀類型,默認(rèn)值為0x0。有一個(gè)小技巧需要注意,一般來講,8個(gè)比特可以容納8個(gè)不同的標(biāo)志,比如,PADDED值為0x8,二進(jìn)制表示為00001000;END_HEADERS值為0x4,二進(jìn)制表示為00000100;END_STREAM值為0X1,二進(jìn)制為00000001。可以同時(shí)在一個(gè)字節(jié)中傳達(dá)三種標(biāo)志位,二進(jìn)制表示為00001101,即0x13。因此,后面的幀結(jié)構(gòu)中,標(biāo)志位一般會(huì)使用8個(gè)比特表示,若某位不確定,使用問號(hào)?替代,表示此處可能會(huì)被設(shè)置標(biāo)志位
- 幀保留比特為R:在HTTP/2語境下為保留的比特位,固定值為0X0
- 流標(biāo)識(shí)符Stream Identifier:無符號(hào)的31比特表示無符號(hào)自然數(shù)。0x0值表示為幀僅作用于連接,不隸屬于單獨(dú)的流。
關(guān)于幀長(zhǎng)度,需要稍加關(guān)注: - 0 ~ 2^14(16384)為默認(rèn)約定長(zhǎng)度,所有端點(diǎn)都需要遵守 - 2^14 (16,384) ~ 2^24-1(16,777,215)此區(qū)間數(shù)值,需要接收方設(shè)置SETTINGS_MAX_FRAME_SIZE參數(shù)單獨(dú)賦值 - 一端接收到的幀長(zhǎng)度超過設(shè)定上限或幀太小,需要發(fā)送FRAME_SIZE_ERR錯(cuò)誤 - 當(dāng)幀長(zhǎng)錯(cuò)誤會(huì)影響到整個(gè)連接狀態(tài)時(shí),須以連接錯(cuò)誤對(duì)待之;比如HEADERS,PUSH_PROMISE,CONTINUATION,SETTINGS,以及幀標(biāo)識(shí)符不該為0的幀等,都需要如此處理 - 任一端都沒有義務(wù)必須使用完一個(gè)幀的所有可用空間 - 大幀可能會(huì)導(dǎo)致延遲,針對(duì)時(shí)間敏感的幀,比如RST_STREAM, WINDOW_UPDATE, PRIORITY,需要快速發(fā)送出去,以免延遲導(dǎo)致性能等問題
二。報(bào)文頭壓縮和解壓
和HTTP/1一樣,HTTP/2報(bào)頭字段包含一個(gè)或多個(gè)相關(guān)的鍵值對(duì)。報(bào)頭字段會(huì)在HTTP請(qǐng)求/響應(yīng)報(bào)頭和服務(wù)器推送操作中使用。原先為文本字段,現(xiàn)在需要使用HTTP報(bào)頭壓縮進(jìn)行序列化成報(bào)頭分塊,作為HEADERS 、 PUSH_PROMISE、CONTINUATION等幀的負(fù)載傳輸出去。
解壓縮采用的HPACK協(xié)議,具體可參考:http://http2.github.com/http2-spec/compression.html
接收端合并接收到的幀組裝成報(bào)頭分塊,解壓縮還原報(bào)頭集合。
一個(gè)完整的報(bào)頭分塊包含: - 單個(gè)包含報(bào)頭終止標(biāo)記END_HEADERS的HEADERS、PUSH_PROMISE幀,或者 - HEADERS、PUSH_PROMISE幀不包含的END_HEADERS標(biāo)記,后續(xù)跟隨一個(gè)或多個(gè)CONTINUATION幀,最后一個(gè)CONTINUATION幀包含了END_HEADERS標(biāo)記。
報(bào)頭壓縮是有狀態(tài)的,在一個(gè)完整的連接中,一方的壓縮上下文環(huán)境,另一方的解壓的上下文環(huán)境,都是需要具備的。報(bào)頭解碼失敗需要作為連接錯(cuò)誤COMPRESSION_ERROR對(duì)待。
報(bào)頭塊彼此之間離散,作為連續(xù)的同一類型幀序列存在,不存在交錯(cuò)幀以及來自其他類型幀或流。舉一個(gè)例子,一個(gè)連續(xù)的HEADERS/CONTINUATION/PUSH_PROMISE幀序列,最后一個(gè)幀包含了END_HEADERS標(biāo)記,表示一個(gè)報(bào)頭完結(jié)。一個(gè)報(bào)頭塊邏輯上是一個(gè)幀,但是否完整取決于同類型連續(xù)的幀的最后一個(gè)包含END_HEADERS標(biāo)記。
報(bào)頭塊作為HEADERS/PUSH_PROMISE/CONTINUATION等幀負(fù)載被一端發(fā)向另一端。接收端需要從HEADERS/PUSH_PROMISE/CONTINUATION等幀負(fù)載中進(jìn)行組裝報(bào)頭塊,執(zhí)行解壓還原報(bào)頭集合,不管幀需要不需要被丟棄。接收端在解壓時(shí)若不能夠正常解壓報(bào)頭塊,需要回應(yīng)COMPRESSION_ERROR錯(cuò)誤,然后終止連接。
三。HTTP/2定義的幀
規(guī)范定義了10個(gè)正式使用到幀類型,擴(kuò)展實(shí)驗(yàn)類型的ALTSVC、BLOCKED等不在介紹之列。下面按照優(yōu)先使用順序重新排排序。
1. SETTINGS
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0x4 (8) | 0000 000? (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier/0x0 (32) | +=+=============================+===============================+ | Identifier (16) | +-------------------------------+-------------------------------+ | Value (32) | +---------------------------------------------------------------+ | Identifier (16) | +-------------------------------+-------------------------------+ | Value (32) | +---------------------------------------------------------------+
設(shè)置幀,接收者向發(fā)送者通告己方設(shè)定,服務(wù)器端在連接成功后必須第一個(gè)發(fā)送的幀。
字段Identifier定義了如下參數(shù): - SETTINGS_HEADER_TABLE_SIZE (0x1),通知接收者報(bào)頭表的字節(jié)數(shù)最大值,報(bào)頭塊解碼使用;初始值為4096個(gè)字節(jié),默認(rèn)可不用設(shè)置 - SETTINGS_ENABLE_PUSH (0x2),0:禁止服務(wù)器推送,1:允許推送;其它值非法,PROTOCOL_ERROR錯(cuò)誤 - SETTINGS_MAX_CONCURRENT_STREAMS (0x3),發(fā)送者允許可打開流的最大值,建議值100,默認(rèn)可不用設(shè)置;0值為禁止創(chuàng)建新流 - SETTINGS_INITIAL_WINDOW_SIZE (0x4),發(fā)送端流控窗口大小,默認(rèn)值2^16-1 (65,535)個(gè)字節(jié)大小;最大值為2^31-1個(gè)字節(jié)大小,若溢出需要報(bào)FLOW_CONTROL_ERROR錯(cuò)誤 - SETTINGS_MAX_FRAME_SIZE (0x5),單幀負(fù)載最大值,默認(rèn)為2^14(16384)個(gè)字節(jié),兩端所發(fā)送幀都會(huì)收到此設(shè)定影響;值區(qū)間為2^14(16384)-2^24-1(16777215) - SETTINGS_MAX_HEADER_LIST_SIZE (0x6),發(fā)送端通告自己準(zhǔn)備接收的報(bào)頭集合最大值,即字節(jié)數(shù)。此值依賴于未壓縮報(bào)頭字段,包含字段名稱、字段值以及每一個(gè)報(bào)頭字段的32個(gè)字節(jié)的開銷等;文檔里面雖說默認(rèn)值不受限制,因?yàn)槭艿綀?bào)頭集合大小不限制的影響,個(gè)人認(rèn)為不要多于2 SETTINGS_MAX_FRAME_SIZE(即2^142=32768),否則包頭太大,隱患多多。
標(biāo)志位: * ACK (0x1),表示接收者已經(jīng)接收到SETTING幀,作為確認(rèn)必須設(shè)置此標(biāo)志位,此時(shí)負(fù)載為空,否則需要報(bào)FRAME_SIZE_ERROR錯(cuò)誤
注意事項(xiàng): - 在連接開始階段必須允許發(fā)送SETTINGS幀,但不一定要發(fā)送 - 在連接的生命周期內(nèi)可以允許任一端點(diǎn)發(fā)送 - 接收者不需要維護(hù)參數(shù)的狀態(tài),只需要記錄當(dāng)前值即可 - SETTINGS幀僅作用于當(dāng)前連接,不針對(duì)單個(gè)流,因此流標(biāo)識(shí)符為0x0 - 不完整或不合規(guī)范的SETTINGS幀需要拋出PROTOCOL_ERROR類型連接錯(cuò)誤 - 負(fù)載字節(jié)數(shù)為6個(gè)字節(jié)的倍數(shù),6*N (N>=0)
處理流程: - 發(fā)送端發(fā)送需要兩端都需要攜帶有遵守的SETTINGS設(shè)置幀,不能夠帶有ACK標(biāo)志位 - 接收端接收到無ACK標(biāo)志位的SETTINGS幀,必須按照幀內(nèi)字段出現(xiàn)順序一一進(jìn)行處理,中間不能夠處理其它幀 - 接收端處理時(shí),針對(duì)不受支持的參數(shù)需忽略 - 接收端處理完畢之后,必須響應(yīng)一個(gè)包含有ACK確認(rèn)標(biāo)志位、無負(fù)載的SETTINGS幀 - 發(fā)送端接收到確認(rèn)的SETTINGS幀,表示兩端設(shè)置已生效 - 發(fā)送端等待確認(rèn)若超時(shí),報(bào)SETTINGS_TIMEOUT類型連接錯(cuò)誤
2. HEADER
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0x1 (8) | 00?0 ??0? (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============+===============================================+ |Pad Length? (8)| +-+-------------+-----------------------------------------------+ |E| Stream Dependency? (31) | +-+-------------+-----------------------------------------------+ | Weight? (8) | +-+-------------+-----------------------------------------------+ | Header Block Fragment (*) ... +---------------------------------------------------------------+ | Padding (*) ... +---------------------------------------------------------------+
報(bào)頭主要載體,請(qǐng)求頭或響應(yīng)頭,同時(shí)呢也用于打開一個(gè)流,在流處于打開"open"或者遠(yuǎn)程半關(guān)閉"half closed (remote)"狀態(tài)都可以發(fā)送。
字段列表: - Pad Length:受制于PADDED標(biāo)志控制是否顯示,8個(gè)比特表示填充的字節(jié)數(shù)。 - E:一個(gè)比特表示流依賴是否專用,可選項(xiàng),只在流優(yōu)先級(jí)PRIORITY被設(shè)置時(shí)有效 - Stream Dependency:31個(gè)比特表示流依賴,只在流優(yōu)先級(jí)PRIORITY被設(shè)置時(shí)有效 Weight:8個(gè)比特(一個(gè)字節(jié))表示無符號(hào)的自然數(shù)流優(yōu)先級(jí),值范圍自然是(1~256),或稱之為權(quán)重。只在流優(yōu)先級(jí)PRIORITY被設(shè)置時(shí)有效 - Header Block Fragment:報(bào)頭塊分片 - Padding:填充的字節(jié),受制于PADDED標(biāo)志控制是否顯示,長(zhǎng)度由Pad Length字段決定
所需標(biāo)志位: END_STREAM (0x1): 報(bào)頭塊為最后一個(gè),意味著流的結(jié)束。后續(xù)可緊接著CONTINUATION幀在當(dāng)前的流中,需要把CONTINUATION幀作為HEADERS幀的一部分對(duì)待 END_HEADERS (0x4): 此報(bào)頭幀不需分片,完整的一個(gè)幀。后續(xù)不再需要CONTINUATION幀幫忙湊齊。若沒有此標(biāo)志的HEADER幀,后續(xù)幀必須是以CONTINUATION幀傳遞在當(dāng)前的流中,否則接收者需要響應(yīng)PROTOCOL_ERROR類型的連接錯(cuò)誤。 PADDED (0x8): 需要填充的標(biāo)志 PRIORITY (0x20): 優(yōu)先級(jí)標(biāo)志位,控制獨(dú)立標(biāo)志位E,流依賴,和流權(quán)重。
注意事項(xiàng): - 其負(fù)載為報(bào)頭塊分片,若內(nèi)容過大,需要借助于CONTINUATION幀繼續(xù)傳輸。若流標(biāo)識(shí)符為0x0,結(jié)束段需要返回PROTOCOL_ERROR連接異常。HEADERS幀包含優(yōu)先級(jí)信息是為了避免潛在的不同流之間優(yōu)先級(jí)順序的干擾。 - 其實(shí)一般來講,報(bào)文頭部不大的情況下,一個(gè)HEADERS就可以完成了,特殊情況就是Cookie字段超過16KiB大小,不常見。
3. CONTINUATION
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0x9 (8) | 0x0/0x4 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (32) | +=+=============================================================+ | Header Block Fragment (*) | +---------------------------------------------------------------+
字段列表: - Header Block Fragment,用于協(xié)助HEADERS/PUSH_PROMISE等單幀無法包含完整的報(bào)頭剩余部分?jǐn)?shù)據(jù)。
注意事項(xiàng): - 一個(gè)HEADERS/PUSH_PROMISE幀后面會(huì)跟隨零個(gè)或多個(gè)CONTINUATION,只要上一個(gè)幀沒有設(shè)置END_HEADERS標(biāo)志位,就不算一個(gè)幀完整數(shù)據(jù)的結(jié)束。 - 接收端處理此種情況,從開始的HEADERS/PUSH_PROMISE幀到最后一個(gè)包含有END_HEADERS標(biāo)志位幀結(jié)束,合并的數(shù)據(jù)才算是一份完整數(shù)據(jù)拷貝 - 在HEADERS/PUSH_PROMISE(沒有END_HEADERS標(biāo)志位)和CONTINUATION幀中間,是不能夠摻雜其它幀的,否則需要報(bào)PROTOCOL_ERROR錯(cuò)誤
標(biāo)志位: * END_HEADERS(0X4):表示報(bào)頭塊的最后一個(gè)幀,否則后面還會(huì)跟隨CONTINUATION幀。
4. DATA
一個(gè)或多個(gè)DATA幀作為請(qǐng)求、響應(yīng)內(nèi)容載體,較為完整的結(jié)構(gòu)如下:
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0x0 (8) | 0000 ?00? (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============+===============================================+ |Pad Length? (8)| +---------------+-----------------------------------------------+ | Data (*) ... +---------------------------------------------------------------+ | Padding? (*) ... +---------------------------------------------------------------+
字段: Pad Length: 一個(gè)字節(jié)表示填充的字節(jié)長(zhǎng)度。取決于PADDED標(biāo)志是否被設(shè)置. Data: 這里是應(yīng)用數(shù)據(jù),真正大小需要減去其他字段(比如填充長(zhǎng)度和填充內(nèi)容)長(zhǎng)度。 * Padding: 填充內(nèi)容為若干個(gè)0x0字節(jié),受PADDED標(biāo)志控制是否顯示。接收端處理時(shí)可忽略驗(yàn)證填充內(nèi)容。若驗(yàn)證,可以對(duì)非0x0內(nèi)容填充回應(yīng)PROTOCOL_ERROR類型連接異常。
標(biāo)志位: END_STREAM (0x1): 標(biāo)志此幀為對(duì)應(yīng)標(biāo)志流最后一個(gè)幀,流進(jìn)入了半關(guān)閉/關(guān)閉狀態(tài)。 PADDED (0x8): 負(fù)載需要填充,Padding Length + Data + Padding組成。
注意事項(xiàng): - 若流標(biāo)識(shí)符為0x0,接收者需要響應(yīng)PROTOCOL_ERROR連接錯(cuò)誤 - DATA幀只能在流處于"open" or "half closed (remote)"狀態(tài)時(shí)被發(fā)送出去,否則接收端必須響應(yīng)一個(gè)STREAM_CLOSED的連接錯(cuò)誤。若填充長(zhǎng)度不小于負(fù)載長(zhǎng)度,接收端必須響應(yīng)一個(gè)PROTOCOL_ERROR連接錯(cuò)誤。
5. PUSH_PROMISE
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0x5 (8) | 0000 ??00 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (32) | +=+=============================================================+ |Pad Length? (8)| +-+-------------+-----------------------------------------------+ |R| Promised Stream ID (31) | +-+-------------------------------------------------------------+ | Header Block Fragment (*) . . . +---------------------------------------------------------------+ | Padding (*) . . . +---------------------------------------------------------------+
服務(wù)器端通知對(duì)端初始化一個(gè)新的推送流準(zhǔn)備稍后推送數(shù)據(jù): - 要求推送流為打開或遠(yuǎn)端半關(guān)閉(half closed (remote))狀態(tài),否則報(bào)PROTOCOL_ERROR錯(cuò)誤: - 承諾的流不一定要按照其流打開順序進(jìn)行使用,僅用作稍后使用 - 受對(duì)端所設(shè)置SETTINGS_ENABLE_PUSH標(biāo)志位決定是否發(fā)送,否則作為PROTOCOL_ERROR錯(cuò)誤對(duì)待 - 接收端一旦拒絕接收推送,會(huì)發(fā)送RST_STREAM幀告知對(duì)方推送無效
字段列表: - Promised Stream ID,31個(gè)比特表示無符號(hào)的自然數(shù),為推送保留的流標(biāo)識(shí)符,后續(xù)適用于發(fā)送推送數(shù)據(jù) - Header Block Fragment,請(qǐng)求頭部字段值,可看做是服務(wù)器端模擬客戶端發(fā)起一次資源請(qǐng)求
標(biāo)志位: END_HEADERS(0x4/00000010),此幀包含完整的報(bào)頭塊,不用后面跟隨CONTINUATION幀了 PADDED(0x8/00000100),填充開關(guān),決定了下面的Pad Length和Padding是否要填充,具體和HEADERS幀內(nèi)容一致,不多說
6. PING
優(yōu)先級(jí)幀,類型值為0x6,8個(gè)字節(jié)表示。發(fā)送者測(cè)量最小往返時(shí)間,心跳機(jī)制用于檢測(cè)空閑連接是否有效。
+-----------------------------------------------+ | 0x8 (24) | +---------------+---------------+---------------+ | 0x6 (8) | 0000 000? (8) | +-+-------------+---------------+-------------------------------+ |R| 0x0 (32) | +=+=============================================================+ | Opaque Data (64) | +---------------------------------------------------------------+
字段列表: - Opaque Data:8個(gè)字節(jié)負(fù)載,值隨意填寫。
標(biāo)志位: * ACK(0x1):一旦設(shè)置,表示此PING幀為接收者響應(yīng)的PING幀,非發(fā)送者。
注意事項(xiàng): - PING幀發(fā)送方ACK標(biāo)志位為0x0,接收方響應(yīng)的PING幀ACK標(biāo)志位為0x1。否則直接丟棄。其優(yōu)先級(jí)要高于其它類型幀。 - PING幀不和具體流相關(guān)聯(lián),若流標(biāo)識(shí)符為0x0,接收方需要響應(yīng)PROTOCOL_ERROR類型連接錯(cuò)誤。 - 超過負(fù)載長(zhǎng)度,接收者需要響應(yīng)FRAME_SIZE_ERROR類型連接錯(cuò)誤。
7. PRIORITY
優(yōu)先級(jí)幀,類型值為0x2,5個(gè)字節(jié)表示。表達(dá)了發(fā)送方對(duì)流優(yōu)先級(jí)權(quán)重的建議值,在流的任何狀態(tài)下都可以發(fā)送,包括空閑或關(guān)閉的流。
+-----------------------------------------------+ | 0x5 (24) | +---------------+---------------+---------------+ | 0x2 (8) | 0x0 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ |E| Stream Dependency (31) | +-+-------------+-----------------------------------------------+ | Weight (8) | +---------------+
字段列表: - E:流是否獨(dú)立 - Stream Dependency:流依賴,值為流的標(biāo)識(shí)符,自然也是31個(gè)比特表示。 - Weight:權(quán)重/優(yōu)先級(jí),一個(gè)字節(jié)表示自然數(shù),范圍1~256
注意事項(xiàng): - PRIORITY幀其流標(biāo)識(shí)符為0x0,接收方需要響應(yīng)PROTOCOL_ERROR類型的連接錯(cuò)誤。 - PRIORITY幀可在流的任何狀態(tài)下發(fā)送,但限制是不能夠在一個(gè)包含有報(bào)頭塊連續(xù)的幀里面出現(xiàn),其發(fā)送時(shí)刻需要,若流已經(jīng)結(jié)束,雖然可以發(fā)送,但已經(jīng)沒有什么效果。 - 超過5個(gè)字節(jié)PRIORITY幀接收方響應(yīng)FRAME_SIZE_ERROR類型流錯(cuò)誤。
8. WINDOW_UPDATE
+-----------------------------------------------+ | 0x4 (24) | +---------------+---------------+---------------+ | 0x8 (8) | 0x0 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ |R| Window Size Increment (31) | +-+-------------------------------------------------------------+
流量控制幀,作用于單個(gè)流以及整個(gè)連接,但只能影響兩個(gè)端點(diǎn)之間傳輸?shù)腄ATA數(shù)據(jù)幀。但需注意,中介不轉(zhuǎn)發(fā)此幀。
字段列表: - Window Size Increment,31個(gè)比特位無符號(hào)自然數(shù),范圍為1-2^31-1(2,147,483,647)個(gè)字節(jié)數(shù),表明發(fā)送者可以發(fā)送的最大字節(jié)數(shù),以及接收者可以接收到的最大字節(jié)數(shù)。
注意事項(xiàng): - 目前流控只會(huì)影響到DATA數(shù)據(jù)幀 - 流標(biāo)識(shí)符為0,影響整個(gè)連接,非單個(gè)流 - 流標(biāo)識(shí)符不為空,具體流的標(biāo)識(shí)符,將只能夠影響到具體流 - WINDOW_UPDATE在某個(gè)攜帶有END_STREAM幀的后面被發(fā)送(當(dāng)前流處于關(guān)閉或遠(yuǎn)程關(guān)閉狀態(tài)),接收端可忽略,但不能作為錯(cuò)誤對(duì)待 - 發(fā)送者不能發(fā)送一個(gè)窗口值大于接收者已持有(接收端已經(jīng)擁有一個(gè)流控窗口值)可用空間大小的WINDOW_UPDATE幀 - 當(dāng)流控窗口所設(shè)置可用空間已耗盡時(shí),對(duì)端發(fā)送一個(gè)零負(fù)載帶有END_STREAM標(biāo)志位的DATA數(shù)據(jù)幀,這是允許的行為 - 流量控制不會(huì)計(jì)算幀頭所占用的9個(gè)字節(jié)空間 - 若窗口值溢出,針對(duì)單獨(dú)流,響應(yīng)RST_STREAM(錯(cuò)誤碼FLOW_CONTROL_ERROR)幀;針對(duì)整個(gè)連接的,響應(yīng)GOAWAY(錯(cuò)誤碼FLOW_CONTROL_ERROR)幀 - DATA數(shù)據(jù)幀的接收方在接收到數(shù)據(jù)幀之后,需要計(jì)算已消耗的流控窗口可用空間,同時(shí)要把最新可用窗口空間發(fā)送給對(duì)端 - DATA數(shù)據(jù)幀發(fā)送方接收到WINDOW_UPDATE幀之后,獲取最新可用窗口值 - 接收方異步更新發(fā)送方窗口值,避免流停頓/失速 - 默認(rèn)情況下流量控制窗口值為65535,除非接收到SETTINGS幀SETTINGS_INITIAL_WINDOW_SIZE參數(shù),或者WINDOWS_UPDATE幀攜帶的窗口值大小,否則不會(huì)改變 - SETTINGS_INITIAL_WINDOW_SIZE值的改變會(huì)導(dǎo)致窗口可用空間不明晰,易出問題,發(fā)送者必須停止受流控影響的DATA數(shù)據(jù)幀的發(fā)送直到接收到WINDOW_UPDATE幀獲得新的窗口值,才會(huì)繼續(xù)發(fā)送。eg:客戶端在連接建立的瞬間一口氣發(fā)送了60KB的數(shù)據(jù),但來自服務(wù)器SETTINGS設(shè)置幀的初始窗口值為16KB,客戶端只能夠等到WINDOW_UPDATE幀告知新的窗口值,然后繼續(xù)發(fā)送傳送剩下的44KB數(shù)據(jù) - SETTINGS幀無法修改針對(duì)整個(gè)連接的流量控制窗口值 - 任一端點(diǎn)在處理SETTINGS_INITIAL_WINDOW_SIZE值時(shí)一旦導(dǎo)致流控窗口值超出最大值,都需要作為一個(gè)FLOW_CONTROL_ERROR類型連接錯(cuò)誤對(duì)待
9. RST_STREAWM
優(yōu)先級(jí)幀,類型值為0x3,4個(gè)字節(jié)表示。表達(dá)了發(fā)送方對(duì)流優(yōu)先級(jí)權(quán)重的建議值,任何時(shí)間任何流都可以發(fā)送,包括空閑或關(guān)閉的流。
+-----------------------------------------------+ | 0x4 (24) | +---------------+---------------+---------------+ | 0x3 (8) | 0x0 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Error Code (32) | +---------------------------------------------------------------+
字段列表: - Error Code:錯(cuò)誤代碼,32位無符號(hào)的自然數(shù)表示流被關(guān)閉的錯(cuò)誤原因。
注意事項(xiàng): - 接收到RST_STREAM幀,需要關(guān)閉對(duì)應(yīng)流,因此流也要處于關(guān)閉狀態(tài)。 - 接收者不能夠在此流上發(fā)送任何幀。 - 發(fā)送端需要做好準(zhǔn)備接收接收端接收到RST_STREAM幀之前發(fā)送的幀,這個(gè)空隙的幀需要處理。 - 若流標(biāo)識(shí)符為0x0,接收方需要響應(yīng)PROTOCOL_ERROR類型連接錯(cuò)誤。 - 當(dāng)流處于空閑狀態(tài)idle狀態(tài)時(shí)是不能夠發(fā)送RST_STREAM幀,否則接收方會(huì)報(bào)以PROOTOCOL_ERROR類型連接錯(cuò)誤。
10. GOAWAY
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0x7 (8) | 0x0 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (32) | +=+=============================================================+ |R| Last-Stream-ID (31) | +-+-------------------------------------------------------------+ | Error Code (32) | +---------------------------------------------------------------+ | Additional Debug Data (*) | +---------------------------------------------------------------+
一端通知對(duì)端較為優(yōu)雅的方式停止創(chuàng)建流,同時(shí)還要完成之前已建立流的任務(wù)。
- 一旦發(fā)送,發(fā)送者將忽略接收到的流標(biāo)識(shí)符大于Last-Stream-ID任何幀
- 接收者不能夠在當(dāng)前流上創(chuàng)建新流,若創(chuàng)建新流則創(chuàng)建新的連接
- 可用于服務(wù)器的管理行為,比如服務(wù)器進(jìn)入維護(hù)階段,不再準(zhǔn)備接收新的連接
- 字段Last-Stream-ID為發(fā)送方取自最后一個(gè)正在處理或已經(jīng)處理流的標(biāo)識(shí)符
- 后續(xù)創(chuàng)建的流標(biāo)識(shí)符高于Last-Stream-ID數(shù)據(jù)幀都不會(huì)被處理
- 終端應(yīng)被鼓勵(lì)在關(guān)閉連接之前發(fā)送GOAWAY隱式方式告知對(duì)方某些流是否已經(jīng)被處理
- 終端可以選擇關(guān)閉連接,針對(duì)行為不當(dāng)?shù)慕K端不發(fā)送GOAWAY幀
- GOAWAY應(yīng)用于當(dāng)前連接,非具體流
- 沒有處理任何流的情況下,Last-Stream-ID值可為0,也是合法
- 流(標(biāo)識(shí)符小于或等于已有編號(hào)的標(biāo)識(shí)符)在連接關(guān)閉之前沒有被完全關(guān)閉,需要?jiǎng)?chuàng)建新的連接進(jìn)行重試
- 發(fā)送端在發(fā)送GOAWAY時(shí)還有一些流任務(wù)沒有完成,將保持連接為打開狀態(tài)直到任務(wù)完成
- 終端可以在自身環(huán)境發(fā)生改變時(shí)發(fā)送多個(gè)GOAWAY幀,但Last-Stream-ID不允許增長(zhǎng)
- Additional Debug Data沒有語義,僅用于聯(lián)機(jī)測(cè)試診斷目的。若攜帶登陸或持久化調(diào)試數(shù)據(jù),需要有安全保證避免未經(jīng)授權(quán)訪問。
四。幀的擴(kuò)展
HTTP/2協(xié)議的擴(kuò)展是允許存在的,在于提供額外服務(wù)。擴(kuò)展包括: - 新類型幀,需要遵守通用幀格式 - 新的設(shè)置參數(shù),用于設(shè)置新幀相關(guān)屬性 - 新的錯(cuò)誤代碼,約定幀可能觸發(fā)的錯(cuò)誤
當(dāng)定義一個(gè)新幀,需要注意 1. 規(guī)范建議新的擴(kuò)展需要經(jīng)過雙方協(xié)商后才能使用 1. 在SETTINGS幀添加新的參數(shù)項(xiàng),可在連接序言時(shí)發(fā)送給對(duì)端,或者適當(dāng)機(jī)會(huì)發(fā)送 1. 雙方協(xié)商成功,可以使用新的擴(kuò)展
已知ALTSVC、BLOCKED屬于擴(kuò)展幀。
1. ALTSVC
服務(wù)器提供給客戶端當(dāng)前可用的替代服務(wù),類似于CNAME,客戶端不支持可用選擇忽略
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0xa (8) | 0x0 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (32) | +=+=============================+===============================+ | Origin-Len (16) | Origin? (*) ... +-------------------------------+-------------------------------+ | Alt-Svc-Field-Value (*) ... +---------------------------------------------------------------+
字段列表: - Origin-Len: 16比特位整數(shù),說明了Origin字段字節(jié)數(shù) - Origin: ASCII字符串表示替代服務(wù) - Alt-Svc-Field-Value: 包含了Alt-Svc HTTP Header Field,長(zhǎng)度=Length (24) - Origin-Len (16)
需要注意: - 中介設(shè)備不能轉(zhuǎn)發(fā)給客戶端,原因就是中介自身替換處理,轉(zhuǎn)發(fā)正常的業(yè)務(wù)數(shù)據(jù)給客戶端就行
具體可參考:https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06
2. BLOCKED
一端告訴另一端因?yàn)槭艿搅髁靠刂频淖饔糜袛?shù)據(jù)但無法發(fā)送。
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | 0xb (8) | 0x0 (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier/0x0 (32) | +=+=============================================================+
- Stream Identifier若為0x0,則表示針對(duì)整個(gè)連接,否則針對(duì)具體流
- 在流量控制窗口生效之前不能發(fā)送BLOCKED
- 一旦遇到此項(xiàng)問題,說明我們的實(shí)現(xiàn)可能有缺陷,無法得到理想的傳輸速率
- 只能夠在WINDOW_UPDATE幀接收之前或SETTINGS_INITIAL_WINDOW_SIZE參數(shù)增加之前發(fā)送
五。小結(jié)
以上記錄了HTTP/2幀基本結(jié)構(gòu),10個(gè)文檔定義的正式幀,以及額外的兩個(gè)擴(kuò)展幀。
posted on 2015-03-20 09:24 nieyong 閱讀(11738) 評(píng)論(0) 編輯 收藏 所屬分類: HTTP