<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    John Jiang

    a cup of Java, cheers!
    https://github.com/johnshajiang/blog

       :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
      131 隨筆 :: 1 文章 :: 530 評(píng)論 :: 0 Trackbacks
    探索HTTP/2: HPACK協(xié)議簡述
    探索HTTP/2系列的第一篇文章已經(jīng)介紹了HTTP 2協(xié)議,本文則將簡述用于HTTP/2頭部壓縮的HPACK協(xié)議。(2016.10.01最后更新)

    1. 基本原理
        HPACK頭部壓縮的基本原理就是使用索引表和Huffman編碼。在壓縮(編碼)與解壓(解碼)過程,可將指定的頭部字段(包含字段名與字段值)存儲(chǔ)在索引表中。索引表中的每一個(gè)條目由索引(一個(gè)整數(shù)),字段名和字段值組成。對(duì)于存在索引表中的頭部字段,在編碼時(shí)可以僅使用索引作為該字段的代表,在解碼時(shí)通過該索引從表中查找出對(duì)應(yīng)的字段。對(duì)于其它的字符串,則可以使用Huffman編碼進(jìn)行壓縮。
    1.1 索引表
        索引表由靜態(tài)表與動(dòng)態(tài)表組成。靜態(tài)表由HPACK協(xié)議預(yù)定義的61個(gè)常用的頭部字段組成,其中大部分字段的值為空。靜態(tài)表是只讀的,其中的條目及其位置均不可更改。HPACK協(xié)議中的附錄A列出了全部的靜態(tài)表?xiàng)l目。動(dòng)態(tài)表也由一系列頭部字段組成,但其中的元素不固定,在實(shí)際操作中可以插入新的條目,也允許刪除已有的條目。
        HPACK協(xié)議要求靜態(tài)表與動(dòng)態(tài)表合并在同一個(gè)存儲(chǔ)空間中,其中靜態(tài)表置于前部,動(dòng)態(tài)表緊隨其后。那么在整個(gè)索引表空間中,動(dòng)態(tài)表的第一個(gè)條目的索引將是62。動(dòng)態(tài)表的維護(hù)原則是先進(jìn)先出(FIFO)。當(dāng)向動(dòng)態(tài)表中增加條目時(shí),將總是從第62位插入,原有的條目將全部向右移動(dòng)一個(gè)位置。當(dāng)從動(dòng)態(tài)表中刪除條目時(shí),將總是從最后一位進(jìn)行刪除。
        雖說,協(xié)議要求將靜態(tài)表與動(dòng)態(tài)表合并在一起,但這只是邏輯上的要求。只要?jiǎng)討B(tài)表的索引是從62開始,那么各個(gè)實(shí)現(xiàn)可以根據(jù)自己的喜好自由地使用存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)。比如,可以將靜態(tài)表單獨(dú)放在一個(gè)不可變的數(shù)組中,而動(dòng)態(tài)表由另一個(gè)鏈表進(jìn)行存儲(chǔ),這樣可能會(huì)便于插入和刪除條目。只不過,這個(gè)鏈表中元素的下標(biāo)與動(dòng)態(tài)表中條目的索引之間相差62。
        (動(dòng)態(tài))索引表中的條目允許重復(fù)。
    1.2 Huffman編碼
        Huffman編碼是一種用于無損數(shù)據(jù)壓縮的權(quán)路徑編碼算法。在使用該算法時(shí),需要一張所有被編碼字符的權(quán)重(出現(xiàn)頻率)代碼表。在對(duì)大量的HTTP頭部樣本進(jìn)行統(tǒng)計(jì)之后,得出了一份適用于HPACK的Huffman代碼表,由協(xié)議中的附錄B列出。

        必須注意的是,HPACK協(xié)議并不要求該協(xié)議的實(shí)現(xiàn)一定要使用索引表,即便某個(gè)字段已經(jīng)存在于索引表中了。而且也不要求一定要對(duì)字符串實(shí)施Huffman壓縮。也就是說,理論上,在編碼時(shí)可以不對(duì)頭部字段進(jìn)行任何形式的壓縮,而只需將所有的字符轉(zhuǎn)化成字節(jié)形式。

    2. 基本數(shù)據(jù)類型表示法
        HPACK協(xié)議使用的基本數(shù)據(jù)類型只有兩種:整數(shù);字符串。該協(xié)議使用整數(shù)去表示索引和字符串的長度。頭部字段名和值中出現(xiàn)的數(shù)字,只會(huì)被當(dāng)作字符串進(jìn)行處理。
    2.1 整數(shù)表示法
        HPACK在表示整數(shù)時(shí)并不是把它簡單的轉(zhuǎn)換成二進(jìn)制形式。因?yàn)镠PACK希望每一個(gè)整數(shù)的表示能夠從某個(gè)8比特位字節(jié)(octet,下文將其簡寫為"字節(jié)")中的任何一個(gè)比特位開始,但總是要在某個(gè)字節(jié)的最后一個(gè)比特位結(jié)束。比如表示127,讓它從字節(jié)的第一個(gè)比特位開始填充,肯定會(huì)在最后一個(gè)比特位結(jié)束,如下圖所示:
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
    +---+---+---+---+---+---+---+---+
    如果第一個(gè)比特位被其它值占用(用"?"代表),只能從第二個(gè)比特位開始填充呢?結(jié)果依然只需要一個(gè)字節(jié),如下所示:
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | ? | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
    +---+---+---+---+---+---+---+---+
    但如果是從第三個(gè)比特位開始填充呢?這時(shí)會(huì)發(fā)現(xiàn)一個(gè)字節(jié)已經(jīng)不夠了,必須要第二個(gè)字節(jié)。但能否表示成如下形式呢?
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | ? | ? | 1 | 1 | 1 | 1 | 1 | 1 |
    +---+---+---+-------------------+
    | 1 | ? | ? | ? | ? | ? | ? | ? |
    +---+---+---+---+---+---+---+---+
    這顯然不符合HPACK協(xié)議的要求,因?yàn)樗M軌蛟谀硞€(gè)字節(jié)的最后一個(gè)比特位結(jié)束這個(gè)表示。為達(dá)到這一目的,HPACK協(xié)議設(shè)計(jì)出了一種如下圖所示的表示法,
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | ? | ? | 1 | 1   1   1   1   1 |
    +---+---+---+-------------------+
    | 1 |    Value-(2^N-1) LSB      |
    +---+---------------------------+
                   ...
    +---+---------------------------+
    | 0 |    Value-(2^N-1) MSB      |
    +---+---------------------------+
    第一個(gè)字節(jié)中能夠被用來填充整數(shù)表示位的比特位數(shù)(上圖中的為6)被稱為prefix。下面是該表示法的Java語言實(shí)現(xiàn),
    public void encodeInteger(int value, int prefix) {
        
    if (value >> prefix <= 0) {
            printBinary(value);
        } 
    else {
            
    int number = (1 << prefix) - 1;
            printBinary(number);
            
    for (value -= number; value >= 128; value /= 128) {
                printBinary(value 
    % 128 + 128);
            }
            printBinary(value);
        }
    }

    private void printBinary(int value) {
        System.out.println(String.format(
    "%8s", Integer.toBinaryString(value)).replace(" ""0"));
    }
    根據(jù)上述算法可知,當(dāng)prefix為6時(shí),127的表示法如下圖所示:
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | ? | ? | 1 | 1 | 1 | 1 | 1 | 1 |
    +---+---+---+-------------------+
    | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
    +---+---+---+-------------------+
    2.2 字符串表示法
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | H |    String Length (7+)     |
    +---+---------------------------+
    |  String Data (Length octets)  |
    +-------------------------------+
    HPACK協(xié)議使用上圖展示的表示法,它由三部分組成:
    [1]Huffman標(biāo)志,表示該字符串是否為Huffman編碼,占用一個(gè)比特位。
    [2]字符串長度,一個(gè)使用2.1節(jié)所述方法表示的整數(shù),其中prefix為7。
    [3]字符串值。若Huffman標(biāo)志為0,該值就是原始字符串的字節(jié),否則該值是經(jīng)Huffman編碼過的數(shù)據(jù)。由于經(jīng)Huffman編碼過的數(shù)據(jù)并不總是能在一個(gè)字節(jié)的最后一個(gè)比特位處結(jié)束,所以可能會(huì)使用EOS(end-of-string)符號(hào)進(jìn)行填充。

    3. 頭部字段表示法
        有了第2節(jié)介紹的基本數(shù)據(jù)類型的表示法作為基礎(chǔ),現(xiàn)在就可以闡述頭部字段的表示法了。HPACK協(xié)議將字段表示法分成3種類型。在表示法開頭有一個(gè)或若干個(gè)比特位用于表示類型。
    3.1 已在索引表的頭部字段
        類型標(biāo)識(shí)占用1個(gè)比特位,值為1。索引使用prefix為7的整數(shù)表示法。在解碼時(shí),不會(huì)更新動(dòng)態(tài)表。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 1 |        Index (7+)         |
    +---+---------------------------+
    3.2 將置入索引表的頭部字段
        類型標(biāo)識(shí)占用2個(gè)比特位,值為01。在解碼時(shí),會(huì)向動(dòng)態(tài)表內(nèi)插入新條目。這種類型又被分成兩種情況:
    [1]頭部字段名已在索引表中,字段名索引使用prefix為6的整數(shù)表示法,而字段值使用字符串表示法。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 1 |      Index (6+)       |
    +---+---+-----------------------+
    | H |     Value Length (7+)     |
    +---+---------------------------+
    | Value String (Length octets)  |
    +-------------------------------+
    [2]頭部字段名不在索引表中,字段名和字段值均使用字符串表示法,而第一個(gè)字節(jié)的后6個(gè)比特位均使用0填充。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 1 |           0           |
    +---+---+-----------------------+
    | H |     Name Length (7+)      |
    +---+---------------------------+
    |  Name String (Length octets)  |
    +---+---------------------------+
    | H |     Value Length (7+)     |
    +---+---------------------------+
    | Value String (Length octets)  |
    +-------------------------------+
    3.2 暫不置入索引表的頭部字段
        類型標(biāo)識(shí)占用4個(gè)比特位,值為0000。在解碼時(shí),不向動(dòng)態(tài)表內(nèi)插入新條目。這種類型又被分成兩種情況:
    [1]頭部字段名已在索引表中,字段名索引使用prefix為4的整數(shù)表示法,而字段值使用字符串表示法。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 0 | 0 | 0 |  Index (4+)   |
    +---+---+-----------------------+
    | H |     Value Length (7+)     |
    +---+---------------------------+
    | Value String (Length octets)  |
    +-------------------------------+
    [2]頭部字段名不在索引表中,字段名和字段值均使用字符串表示法,而第一個(gè)字節(jié)的后4個(gè)比特位均使用0填充。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 0 | 0 | 0 |       0       |
    +---+---+-----------------------+
    | H |     Name Length (7+)      |
    +---+---------------------------+
    |  Name String (Length octets)  |
    +---+---------------------------+
    | H |     Value Length (7+)     |
    +---+---------------------------+
    | Value String (Length octets)  |
    +---+---------------------------+
    3.3 永不置入索引表的頭部字段
        類型標(biāo)識(shí)占用4個(gè)比特位,值為0001。在解碼時(shí),不向動(dòng)態(tài)表內(nèi)插入新條目。這種類型又被分成兩種情況:
    [1]頭部字段名已在索引表中,字段名索引使用prefix為4的整數(shù)表示法,而字段值使用字符串表示法。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 0 | 0 | 1 |  Index (4+)   |
    +---+---+-----------------------+
    | H |     Value Length (7+)     |
    +---+---------------------------+
    | Value String (Length octets)  |
    +-------------------------------+
    [2]頭部字段名不在索引表中,字段名和字段值均使用字符串表示法,而第一個(gè)字節(jié)的后4個(gè)比特位均使用0填充。   
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 0 | 0 | 1 |       0       |
    +---+---+-----------------------+
    | H |     Name Length (7+)      |
    +---+---------------------------+
    |  Name String (Length octets)  |
    +---+---------------------------+
    | H |     Value Length (7+)     |
    +---+---------------------------+
    | Value String (Length octets)  |
    +-------------------------------+
        可以發(fā)現(xiàn),3.2節(jié)與3.3節(jié)中的表示法除了類型標(biāo)識(shí)不同之外,其它的都完全相同。那么它們的區(qū)別是什么呢?類型0000表示的字段在經(jīng)過多次解碼與編碼時(shí),可能會(huì)被某個(gè)中介者置入索引表中。而類型0001表示法強(qiáng)調(diào)了該字段無論在任何時(shí)候都不可置入索引表。類型0001可用于表示包含有敏感信息,如密碼,的字段值,以避免對(duì)這些值進(jìn)行壓縮時(shí)產(chǎn)生的風(fēng)險(xiǎn)。

    4. 動(dòng)態(tài)表的管理
        動(dòng)態(tài)表中的條目被認(rèn)為是有尺寸的,其計(jì)算公式為:字段名的字節(jié)長度+字段值的字節(jié)長度+32。字段名/值的長度是指它們的原始字節(jié)的長度,而非經(jīng)過Huffman編碼后的字節(jié)的長度。
        動(dòng)態(tài)表的尺寸就是其中所有條目的尺寸之和。動(dòng)態(tài)表的最大尺寸是有限的,可以通過下面的整數(shù)表示法來通知協(xié)議的現(xiàn)實(shí)去改變動(dòng)態(tài)表的最大尺寸。
      0   1   2   3   4   5   6   7
    +---+---+---+---+---+---+---+---+
    | 0 | 0 | 1 |   Max size (5+)   |
    +---+---------------------------+
        當(dāng)插入新的條目或改變動(dòng)態(tài)表的最大尺寸時(shí),可能導(dǎo)致已有的一個(gè)或多個(gè)條目被逐出,甚至清空整個(gè)動(dòng)態(tài)表。將動(dòng)態(tài)表的最大尺寸設(shè)置為0是合法的,實(shí)際上,這是一種常用的清空動(dòng)態(tài)表的途徑。
    posted on 2016-09-24 20:29 John Jiang 閱讀(2560) 評(píng)論(0)  編輯  收藏 所屬分類: 原創(chuàng) 、HTTP/2 、探索HTTP/2
    主站蜘蛛池模板: 久久国产精品免费视频| jzzjzz免费观看大片免费| 99精品视频免费观看| 亚洲尤码不卡AV麻豆| 一级一片免费视频播放| 亚洲中久无码不卡永久在线观看| 国产成人亚洲午夜电影| 亚洲AV无码成H人在线观看| 成人在线免费视频| 在线a亚洲v天堂网2019无码| a级成人毛片免费视频高清| 精品亚洲综合在线第一区| 日本在线免费观看| 亚洲欧洲日产韩国在线| 欧美好看的免费电影在线观看| 亚洲人成片在线观看| 成人免费福利电影| 牛牛在线精品观看免费正| 国产成人麻豆亚洲综合无码精品| 国产免费一区二区三区在线观看| 亚洲AV无码久久精品成人| 日本三级2019在线观看免费| 亚洲欧美日韩中文字幕一区二区三区| 国产精品免费电影| 久久久久女教师免费一区| 色拍自拍亚洲综合图区| 成人午夜视频免费| 久久毛片免费看一区二区三区| 亚洲码国产精品高潮在线| 国产免费丝袜调教视频| 亚洲高清乱码午夜电影网| 亚洲日本韩国在线| 最近新韩国日本免费观看 | jizz免费观看| 91亚洲自偷手机在线观看| 免费无码黄网站在线观看| 中文在线日本免费永久18近| 亚洲日韩中文字幕| www.亚洲色图.com| 999在线视频精品免费播放观看| 色九月亚洲综合网|