﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-John Jiang-随笔分类-HTTP/2</title><link>http://www.blogjava.net/jiangshachina/category/55119.html</link><description>a cup of Java, cheers!</description><language>zh-cn</language><lastBuildDate>Thu, 13 Oct 2016 20:45:10 GMT</lastBuildDate><pubDate>Thu, 13 Oct 2016 20:45:10 GMT</pubDate><ttl>60</ttl><item><title>探索HTTP/2: 流的状态(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 08 Oct 2016 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431871.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431871.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: 流的状态</span></strong></div>
<span style="font-size: 10pt;"><a href="http://www.blogjava.net/jiangshachina/category/55120.html">探索HTTP/2系列</a>的第四篇文章，解读了HTTP/2流的状态，以及状态之间的转化。(2016.10.09最后更新)</span><br />
<br />
<strong><span style="font-size: 12pt;">1. 概述</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; HTTP/2的流(Stream)是有状态的。当客户端或服务器端在使用某个流去发送或接收特定帧(Frame)或包含特定标签(Flag)的帧时，会引起流的状态的转化。<a href="https://tools.ietf.org/html/rfc7540">HTTP 2协议</a>定义的流状态，如下所示：</span><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send&nbsp;PP&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;PP<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">--------|&nbsp;&nbsp;idle&nbsp;&nbsp;|--------.<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;H&nbsp;/&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">------|&nbsp;reserved&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;H&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;reserved&nbsp;|------.<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(local)&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(remote)&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recv&nbsp;ES&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;ES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;send&nbsp;H&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">-------|&nbsp;&nbsp;open&nbsp;&nbsp;|-------.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;H&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;half&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;half&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;closed&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;R&nbsp;/&nbsp;&nbsp;|&nbsp;&nbsp;closed&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(remote)&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(local)&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+----------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;ES&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recv&nbsp;ES&nbsp;/&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;send&nbsp;R&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send&nbsp;R&nbsp;/&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+&nbsp;&nbsp;&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;send&nbsp;R&nbsp;/&nbsp;&nbsp;`-----------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;-----------'&nbsp;&nbsp;send&nbsp;R&nbsp;/&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">|&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;closed&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recv&nbsp;R&nbsp;&nbsp;&nbsp;|<br />
</span><span style="color: #000000; font-family: Courier;">`-----------------------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;----------------------'<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--------+<br />
<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;send:&nbsp;&nbsp;&nbsp;endpoint&nbsp;sends&nbsp;this&nbsp;frame<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;recv:&nbsp;&nbsp;&nbsp;endpoint&nbsp;receives&nbsp;this&nbsp;frame<br />
<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;H:&nbsp;&nbsp;HEADERS&nbsp;frame&nbsp;(with&nbsp;implied&nbsp;CONTINUATIONs)<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;PP:&nbsp;PUSH_PROMISE&nbsp;frame&nbsp;(with&nbsp;implied&nbsp;CONTINUATIONs)<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;ES:&nbsp;END_STREAM&nbsp;flag<br />
</span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;R:&nbsp;&nbsp;RST_STREAM&nbsp;frame</span></div>
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">总的说，HTTP/2为流的整个生命周期定义了7种状态：idle，reserved (local)，reserved (remote)，open，half closed (local)，half closed (remote)和closed。当一端发送或接收头部块(由一个HEADERS/PUSH_PROMISE帧和紧随它的零到多个CONTINUATION帧组成的集合)或RST_STREAM帧，或包含有END_STREAM标签的帧(HEADERS和DATA)之后，将改变流的状态。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 流的状态基于各端自己的视角。由于帧的传输会有网络延迟，在同一时刻，不同端认为的流的状态可能是不同的。比如，当发送端使用一个处于idle状态的流发送一个不包含END_STREAM标签的HEADERS帧之后会立即认为该流处于open状态，但此时接收端尚未得到该HEADERS帧，所以在那一时刻，接收端依然认为该流的状态是idle。</span><br />
<br />
<strong><span style="font-size: 12pt;">2. idle</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 所有的流在创建之初都处于idle状态。处于idle状态的流，只允许被用于发送HEADERS帧，但可以被用于接收HEADERS和PRIORITY帧。在一端使用该状态的流发送或接收HEADERS帧之后，该端会认为此流的状态转变为open。接收PRIORITY帧不会改变流的状态。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 一个idle状态的流可被另一个流通过发送/接收PUSH_PROMISE帧保留着，使其在将来被用于服务器端推送。被保留的流的状态则从idle变为reserved (local/remote)。</span><br />
<br />
<strong><span style="font-size: 12pt;">3. open</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 处于open状态的流可被用于发送任何类型的帧。使用该状态的流去发送/接收包含有END_STREAM标签的帧(HEADERS和DATA)之后，会使该流的状态变成half closed (local/remote)。使用open状态的流发送或接收RST_STREAM帧之后，则会使它的状态转变为closed。</span><br />
<br />
<strong><span style="font-size: 12pt;">4. half closed (local/remote)</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 状态half closed (local)与half closed (remote)中的local与remote的区别，完全是基于各端自己的视角。对于同一个流的两端，如果一端认为这个流的状态是half closed (local)，那么另一端只能认为这个流的状态是half closed (remote)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 处于half closed (local)状态的流只能被用于发送WINDOW_UPDATE，PRIORITY和RST_STREAM帧，但可以被用于接收任何类型的帧。相对应地，处于half closed (remote)状态的流只能被用于接收WINDOW_UPDATE，PRIORITY和RST_STREAM帧，但可以被用于发送任何类型的帧。</span><br />
<br />
<strong><span style="font-size: 12pt;">5. reserved (local/remote)</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 与half closed (local/remote)状态相似，reserved (local/remote)状态中的local与remote也是基于流两端各自的视角。更具体的是，服务器端发送PUSH_PROMISE将一个idle状态的流保留着以用于未来的推送，并视这个被保留的流的状态为reserved (local)，而客户端则视这个流的状态为reserved (remote)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 服务器端使用reserved (local)状态的流向客户端发送HEADERS帧。该HEADERS帧就是服务器端推送(Server Push)中被推送的响应的头部。当发送了HEADERS帧之后，服务器端将视该流的状态为half closed (remote)。</span><br />
<span style="font-size: 10pt;">相应地，客户端通过reserved (remote)状态的流接收到服务器端推送的响应的头部，然后会视该流的状态为half closed (local)。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 扩展一下，服务器端推送中被保留的流的状态在变为half closed(local/remote)之后才可能被用于接收/发送被推送的响应的体部，也就是DATA帧。</span><br />
<br />
<strong><span style="font-size: 12pt;">6. closed</span></strong><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 当一端使用一个流发送或接收到RST_STREAM帧，或通过状态为half closed (local/remote)的流接收/发送包含有END_STREAM标签的帧之后，都会视这个流的状态为closed。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; closed状态预示着流的终结，处于该状态的流将只能发送或接收PRIORITY帧。但有一个特例。即，如果通过使用half closed (local/remote)状态的流去接收或发送包含有END_STREAM标签的帧(HEADERS或DATA)，以使该流的状态变为closed，那么在此之后的较短时间内，仍然可以接收WINDOW_UPDATE或RST_STREAM帧。</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-10-08 21:17 <a href="http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索HTTP/2: HPACK协议简述(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Sat, 24 Sep 2016 12:29:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431837.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431837.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431837.html</trackback:ping><description><![CDATA[<div>
<div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: HPACK协议简述</span></strong></div><a href="http://www.blogjava.net/jiangshachina/category/55120.html">探索HTTP/2系列</a>的第一篇文章已经介绍了HTTP 2协议，本文则将简述用于HTTP/2头部压缩的<a href="https://tools.ietf.org/html/rfc7541">HPACK协议</a>。(2016.10.01最后更新)<br />
<br />
<strong><span style="font-size: 12pt;">1. 基本原理</span></strong><br />
&nbsp;&nbsp;&nbsp; HPACK头部压缩的基本原理就是使用索引表和<a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman编码</a>。在压缩(编码)与解压(解码)过程，可将指定的头部字段(包含字段名与字段值)存储在索引表中。索引表中的每一个条目由索引(一个整数)，字段名和字段值组成。对于存在索引表中的头部字段，在编码时可以仅使用索引作为该字段的代表，在解码时通过该索引从表中查找出对应的字段。对于其它的字符串，则可以使用Huffman编码进行压缩。<br />
<strong>1.1 索引表</strong><br />
&nbsp;&nbsp;&nbsp; 索引表由静态表与动态表组成。静态表由HPACK协议预定义的61个常用的头部字段组成，其中大部分字段的值为空。静态表是只读的，其中的条目及其位置均不可更改。HPACK协议中的<a href="https://tools.ietf.org/html/rfc7541#appendix-A">附录A</a>列出了全部的静态表条目。动态表也由一系列头部字段组成，但其中的元素不固定，在实际操作中可以插入新的条目，也允许删除已有的条目。<br />
&nbsp;&nbsp;&nbsp; HPACK协议要求静态表与动态表合并在同一个存储空间中，其中静态表置于前部，动态表紧随其后。那么在整个索引表空间中，动态表的第一个条目的索引将是62。动态表的维护原则是先进先出(FIFO)。当向动态表中增加条目时，将总是从第62位插入，原有的条目将全部向右移动一个位置。当从动态表中删除条目时，将总是从最后一位进行删除。<br />
&nbsp;&nbsp;&nbsp; 虽说，协议要求将静态表与动态表合并在一起，但这只是逻辑上的要求。只要动态表的索引是从62开始，那么各个实现可以根据自己的喜好自由地使用存储数据结构。比如，可以将静态表单独放在一个不可变的数组中，而动态表由另一个链表进行存储，这样可能会便于插入和删除条目。只不过，这个链表中元素的下标与动态表中条目的索引之间相差62。<br />
&nbsp;&nbsp;&nbsp; (动态)索引表中的条目允许重复。<br />
<strong>1.2 Huffman编码</strong><br />
&nbsp;&nbsp;&nbsp; Huffman编码是一种用于无损数据压缩的权路径编码算法。在使用该算法时，需要一张所有被编码字符的权重(出现频率)代码表。在对大量的HTTP头部样本进行统计之后，得出了一份适用于HPACK的Huffman代码表，由协议中的<a href="https://tools.ietf.org/html/rfc7541#appendix-B">附录B</a>列出。<br />
<br />
&nbsp;&nbsp;&nbsp; 必须注意的是，HPACK协议并不要求该协议的实现一定要使用索引表，即便某个字段已经存在于索引表中了。而且也不要求一定要对字符串实施Huffman压缩。也就是说，理论上，在编码时可以不对头部字段进行任何形式的压缩，而只需将所有的字符转化成字节形式。<br />
<br />
<strong><span style="font-size: 12pt;">2. 基本数据类型表示法</span></strong><br />
&nbsp;&nbsp;&nbsp; HPACK协议使用的基本数据类型只有两种：整数；字符串。该协议使用整数去表示索引和字符串的长度。头部字段名和值中出现的数字，只会被当作字符串进行处理。<br />
<strong>2.1 整数表示法</strong><br />
&nbsp;&nbsp;&nbsp; HPACK在表示整数时并不是把它简单的转换成二进制形式。因为HPACK希望每一个整数的表示能够从某个8比特位字节(octet，下文将其简写为"字节")中的任何一个比特位开始，但总是要在某个字节的最后一个比特位结束。比如表示127，让它从字节的第一个比特位开始填充，肯定会在最后一个比特位结束，如下图所示：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>如果第一个比特位被其它值占用(用"?"代表)，只能从第二个比特位开始填充呢？结果依然只需要一个字节，如下所示：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | 1 | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
但如果是从第三个比特位开始填充呢？这时会发现一个字节已经不够了，必须要第二个字节。但能否表示成如下形式呢？<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | ? | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span><br />
<span style="font-family: Courier;">| 1 | ? | ? | ? | ? | ? | ? | ? |</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span></div>
</div>
这显然不符合HPACK协议的要求，因为它希望能够在某个字节的最后一个比特位结束这个表示。为达到这一目的，HPACK协议设计出了一种如下图所示的表示法，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | ? | 1 | 1&nbsp;&nbsp; 1&nbsp;&nbsp; 1&nbsp;&nbsp; 1&nbsp;&nbsp; 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span><br />
<span style="font-family: Courier;">| 1 |&nbsp;&nbsp;&nbsp; Value-(2^N-1) LSB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| 0 |&nbsp;&nbsp;&nbsp; Value-(2^N-1) MSB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
第一个字节中能够被用来填充整数表示位的比特位数(上图中的为6)被称为prefix。下面是该表示法的Java语言实现，<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;encodeInteger(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;prefix)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(value&nbsp;</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;prefix&nbsp;</span><span style="color: #000000; ">&lt;=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;number&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;prefix)&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(number);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;(value&nbsp;</span><span style="color: #000000; ">-=</span><span style="color: #000000; ">&nbsp;number;&nbsp;value&nbsp;</span><span style="color: #000000; ">&gt;=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">;&nbsp;value&nbsp;</span><span style="color: #000000; ">/=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(value&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">128</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printBinary(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;printBinary(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(String.format(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%8s</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;Integer.toBinaryString(value)).replace(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">0</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />
}</span></div>
根据上述算法可知，当prefix为6时，127的表示法如下图所示：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| ? | ? | 1 | 1 | 1 | 1 | 1 | 1 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span><br />
<span style="font-family: Courier;">| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |</span><br />
<span style="font-family: Courier;">+---+---+---+-------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>2.2 字符串表示法</strong><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp; String Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; String Data (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
HPACK协议使用上图展示的表示法，它由三部分组成：<br />
[1]Huffman标志，表示该字符串是否为Huffman编码，占用一个比特位。<br />
[2]字符串长度，一个使用2.1节所述方法表示的整数，其中prefix为7。<br />
[3]字符串值。若Huffman标志为0，该值就是原始字符串的字节，否则该值是经Huffman编码过的数据。由于经Huffman编码过的数据并不总是能在一个字节的最后一个比特位处结束，所以可能会使用EOS(end-of-string)符号进行填充。<br />
<br />
<strong><span style="font-size: 12pt;">3. 头部字段表示法</span></strong><br />
&nbsp;&nbsp;&nbsp; 有了第2节介绍的基本数据类型的表示法作为基础，现在就可以阐述头部字段的表示法了。HPACK协议将字段表示法分成3种类型。在表示法开头有一个或若干个比特位用于表示类型。<br />
<strong>3.1 已在索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用1个比特位，值为1。索引使用prefix为7的整数表示法。在解码时，不会更新动态表。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Index (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>3.2 将置入索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用2个比特位，值为01。在解码时，会向动态表内插入新条目。这种类型又被分成两种情况：<br />
[1]头部字段名已在索引表中，字段名索引使用prefix为6的整数表示法，而字段值使用字符串表示法。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Index (6+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
[2]头部字段名不在索引表中，字段名和字段值均使用字符串表示法，而第一个字节的后6个比特位均使用0填充。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Name Length (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; Name String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>3.2 暂不置入索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用4个比特位，值为0000。在解码时，不向动态表内插入新条目。这种类型又被分成两种情况：<br />
[1]头部字段名已在索引表中，字段名索引使用prefix为4的整数表示法，而字段值使用字符串表示法。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 0 |&nbsp; Index (4+)&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
[2]头部字段名不在索引表中，字段名和字段值均使用字符串表示法，而第一个字节的后4个比特位均使用0填充。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 0 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Name Length (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; Name String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
<strong>3.3 永不置入索引表的头部字段</strong><br />
&nbsp;&nbsp;&nbsp; 类型标识占用4个比特位，值为0001。在解码时，不向动态表内插入新条目。这种类型又被分成两种情况：<br />
[1]头部字段名已在索引表中，字段名索引使用prefix为4的整数表示法，而字段值使用字符串表示法。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 1 |&nbsp; Index (4+)&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
[2]头部字段名不在索引表中，字段名和字段值均使用字符串表示法，而第一个字节的后4个比特位均使用0填充。&nbsp; &nbsp;<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 0 | 1 |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---+-----------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Name Length (7+)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">|&nbsp; Name String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| H |&nbsp;&nbsp;&nbsp;&nbsp; Value Length (7+)&nbsp;&nbsp;&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span><br />
<span style="font-family: Courier;">| Value String (Length octets)&nbsp; |</span><br />
<span style="font-family: Courier;">+-------------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
&nbsp;&nbsp;&nbsp; 可以发现，3.2节与3.3节中的表示法除了类型标识不同之外，其它的都完全相同。那么它们的区别是什么呢？类型0000表示的字段在经过多次解码与编码时，可能会被某个中介者置入索引表中。而类型0001表示法强调了该字段无论在任何时候都不可置入索引表。类型0001可用于表示包含有敏感信息，如密码，的字段值，以避免对这些值进行压缩时产生的风险。<br />
<br />
<strong><span style="font-size: 12pt;">4. 动态表的管理</span></strong><br />
&nbsp;&nbsp;&nbsp; 动态表中的条目被认为是有尺寸的，其计算公式为：字段名的字节长度+字段值的字节长度+32。字段名/值的长度是指它们的原始字节的长度，而非经过Huffman编码后的字节的长度。<br />
&nbsp;&nbsp;&nbsp; 动态表的尺寸就是其中所有条目的尺寸之和。动态表的最大尺寸是有限的，可以通过下面的整数表示法来通知协议的现实去改变动态表的最大尺寸。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;">&nbsp; 0&nbsp;&nbsp; 1&nbsp;&nbsp; 2&nbsp;&nbsp; 3&nbsp;&nbsp; 4&nbsp;&nbsp; 5&nbsp;&nbsp; 6&nbsp;&nbsp; 7</span><br />
<span style="font-family: Courier;">+---+---+---+---+---+---+---+---+</span><br />
<span style="font-family: Courier;">| 0 | 0 | 1 |&nbsp;&nbsp; Max size (5+)&nbsp;&nbsp; |</span><br />
<span style="font-family: Courier;">+---+---------------------------+</span></div>
<span style="color: #000000; font-family: Courier;"></span></div>
&nbsp;&nbsp;&nbsp; 当插入新的条目或改变动态表的最大尺寸时，可能导致已有的一个或多个条目被逐出，甚至清空整个动态表。将动态表的最大尺寸设置为0是合法的，实际上，这是一种常用的清空动态表的途径。</div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-09-24 20:29 <a href="http://www.blogjava.net/jiangshachina/archive/2016/09/24/431837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索HTTP/2: 初试HTTP/2(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Tue, 20 Sep 2016 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431814.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431814.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431814.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span><div><div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: 初试HTTP/2</span></strong></div><span style="font-size: 10pt;">目前支持HTTP/2的服务器端与客户端实现已有不少，<a href="http://www.blogjava.net/jiangshachina/category/55120.html">探索HTTP/2系列</a>的第二篇就分别以Jetty和curl作为服务器端和客户端，描述了HTTP/2测试环境的搭建过程。本文还将使用这个测试环境去展示Jetty在实现HTTP/2时的一个局限和一个Bug。(2016.09.22最后更新)</span><br /><br /><strong><span style="font-size: 12pt;">1. HTTP/2的实现</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 目前已经有众多的服务器端和客户端实现了对HTTP/2的支持。在服务器端，著名的Apache httpd从2.4.17版，Nginx从1.9.5版，开始支持HTTP/2。在客户端，主流的浏览器，如Chrome，FireFox和IE，的最新版均支持HTTP/2，但它们都只支持运行在TLS上的HTTP/2(即h2)。使用Java语言实现的，则有Jetty和Netty，它们都实现了服务器端和客户端。此处有一份HTTP/2实现的列表：<a href="https://github.com/http2/http2-spec/wiki/Implementations">https://github.com/http2/http2-spec/wiki/Implementations</a></span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 另外，还有一些工具支持对HTTP/2的分析与调试，如curl和WireShark。这里也有一份此类工具的列表：<a href="https://github.com/http2/http2-spec/wiki/Tools">https://github.com/http2/http2-spec/wiki/Tools</a></span><br /><br /><strong><span style="font-size: 12pt;">2. 服务器端</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 作为Java程序员，选用一款使用Java语言编写的开源HTTP/2服务器端实现似乎是很自然的结果。实际上，在日后的研究中，我们也需要查看服务器端的源代码。这对于深入地理解HTTP/2，并发现实现中可能的问题，具有现实意义。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 本文选择Jetty的最新版本9.3.11作为服务器端。Jetty是一个成熟的Servlet容器，这为开发Web应用程序提供了极大便利。而本文第1节中提到的Netty是一个传输层框架，它专注于网络程序。可以使用Netty去开发一个Servlet容器，但这显然不如直接使用Jetty方便。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 安装和配置Jetty是一件很容易的事情，具体过程如下所示。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 假设此时已经下载并解压好了Jetty 9.3.11的压缩文件，目录名为jetty-9.3.11。在其中创建一个test-base子目录，作为将要创建的Jetty Base的目录。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;cd&nbsp;jetty-</span><span style="color: #000000; font-family: Courier;">9.3.11</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">$&nbsp;mkdir&nbsp;test-base<br /></span><span style="color: #000000; font-family: Courier;">$&nbsp;cd&nbsp;test-base</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">在创建Base时，加入支持http，https，http2(h2)，http2c(h2c)和deploy的模块。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;java&nbsp;-jar&nbsp;../start.jar&nbsp;--add-to-startd</span><span style="color: #000000; font-family: Courier;">=</span><span style="color: #000000; font-family: Courier;">http</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">https</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">http2</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">http2c</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">deploy<br /><br /></span><span style="color: #000000; font-family: Courier;">ALERT:&nbsp;There&nbsp;are&nbsp;enabled&nbsp;module(s)&nbsp;with&nbsp;licenses.<br /></span><span style="color: #000000; font-family: Courier;">The&nbsp;following&nbsp;</span><span style="color: #000000; font-family: Courier;">1</span><span style="color: #000000; font-family: Courier;">&nbsp;module(s):<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;+&nbsp;contains&nbsp;software&nbsp;not&nbsp;provided&nbsp;by&nbsp;the&nbsp;Eclipse&nbsp;Foundation!<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;+&nbsp;contains&nbsp;software&nbsp;not&nbsp;covered&nbsp;by&nbsp;the&nbsp;Eclipse&nbsp;Public&nbsp;License!<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;+&nbsp;has&nbsp;not&nbsp;been&nbsp;audited&nbsp;for&nbsp;compliance&nbsp;with&nbsp;its&nbsp;license<br /><br /></span><span style="color: #000000; font-family: Courier;">&nbsp;Module:&nbsp;alpn<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;ALPN&nbsp;is&nbsp;a&nbsp;hosted&nbsp;at&nbsp;github&nbsp;under&nbsp;the&nbsp;GPL&nbsp;v2&nbsp;with&nbsp;ClassPath&nbsp;Exception.<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;ALPN&nbsp;replaces/modifies&nbsp;OpenJDK&nbsp;classes&nbsp;in&nbsp;the&nbsp;java.sun.security.ssl&nbsp;package.<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;http://github.com/jetty-project/jetty-alpn<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;+&nbsp;http://openjdk.java.net/legal/gplv2+ce.html<br /><br /></span><span style="color: #000000; font-family: Courier;">Proceed&nbsp;(y/N)?&nbsp;y<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;server&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;(transitively)&nbsp;in&nbsp;${jetty.base}\start.d\server.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;http&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\http.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;ssl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;(transitively)&nbsp;in&nbsp;${jetty.base}\start.d\ssl.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;alpn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;(transitively)&nbsp;in&nbsp;${jetty.base}\start.d\alpn.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;http2c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\http2c.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;https&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\https.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;deploy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\deploy.ini<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;http2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialised&nbsp;in&nbsp;${jetty.base}\start.d\http2.ini<br /></span><span style="color: #000000; font-family: Courier;">DOWNLOAD:&nbsp;http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/</span><span style="color: #000000; font-family: Courier;">8.1.5</span><span style="color: #000000; font-family: Courier;">.v20150921/alpn-boot-</span><span style="color: #000000; font-family: Courier;">8.1.5</span><span style="color: #000000; font-family: Courier;">.v20150921.jar&nbsp;to&nbsp;${jetty.base}\lib\alpn\alpn-boot-</span><span style="color: #000000; font-family: Courier;">8.1.5</span><span style="color: #000000; font-family: Courier;">.v20150921.jar<br /></span><span style="color: #000000; font-family: Courier;">DOWNLOAD:&nbsp;https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id</span><span style="color: #000000; font-family: Courier;">=</span><span style="color: #000000; font-family: Courier;">master&nbsp;to&nbsp;${jetty.base}\etc\keystore<br /></span><span style="color: #000000; font-family: Courier;">MKDIR:&nbsp;${jetty.base}\webapps<br /></span><span style="color: #000000; font-family: Courier;">INFO:&nbsp;Base&nbsp;directory&nbsp;was&nbsp;modified</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">注意，在上述过程中，会根据当前环境变量中使用的Java版本(此处为1.8.0_60)去下载一个对应的TLS-ALPN实现jar文件(此处为alpn-boot-8.1.5.v20150921.jar)，该jar会用于对h2的支持。当启动Jetty时，该jar会被Java的Bootstrap class loader加载到类路径中。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 创建一个最简单的Web应用，使它在根目录下包含一个文本文件index，内容为"HTTP/2 Test"。</span><span style="font-size: 10pt;">再包含一个简单的Servlet，代码如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-family: Courier;">package</span><span style="color: #000000; font-family: Courier;">&nbsp;test;<br /><br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;java.io.IOException;<br /><br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.ServletException;<br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.http.HttpServlet;<br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.http.HttpServletRequest;<br /></span><span style="color: #0000ff; font-family: Courier;">import</span><span style="color: #000000; font-family: Courier;">&nbsp;javax.servlet.http.HttpServletResponse;<br /><br /></span><span style="color: #0000ff; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">class</span><span style="color: #000000; font-family: Courier;">&nbsp;TestServlet&nbsp;</span><span style="color: #0000ff; font-family: Courier;">extends</span><span style="color: #000000; font-family: Courier;">&nbsp;HttpServlet&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">long</span><span style="color: #000000; font-family: Courier;">&nbsp;serialVersionUID&nbsp;</span><span style="color: #000000; font-family: Courier;">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">5222793251610509039L</span><span style="color: #000000; font-family: Courier;">;<br /><br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">void</span><span style="color: #000000; font-family: Courier;">&nbsp;doGet(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">throws</span><span style="color: #000000; font-family: Courier;">&nbsp;ServletException,&nbsp;IOException&nbsp;{<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.getWriter().println(</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">Test</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">);<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000ff; font-family: Courier;">void</span><span style="color: #000000; font-family: Courier;">&nbsp;doPost(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">throws</span><span style="color: #000000; font-family: Courier;">&nbsp;ServletException,&nbsp;IOException&nbsp;{<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doGet(request,&nbsp;response);<br /></span><span style="color: #000000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; font-family: Courier;">}</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">web.xml主要是定义了一个Servlet，具体内容如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-family: Courier;">&lt;?</span><span style="color: #ff00ff; font-family: Courier;">xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"</span><span style="color: #0000ff; font-family: Courier;">?&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">web-app&nbsp;</span><span style="color: #ff0000; font-family: Courier;">xmlns</span><span style="color: #0000ff; font-family: Courier;">="http://xmlns.jcp.org/xml/ns/javaee"</span><span style="color: #ff0000; font-family: Courier;">&nbsp;xmlns:xsi</span><span style="color: #0000ff; font-family: Courier;">="http://www.w3.org/2001/XMLSchema-instance"</span><span style="color: #FF0000; "><br /></span><span style="color: #ff0000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;xsi:schemaLocation</span><span style="color: #0000ff; font-family: Courier;">="http://xmlns.jcp.org/xml/ns/javaee&nbsp;http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"</span><span style="color: #FF0000; "><br /></span><span style="color: #ff0000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;metadata-complete</span><span style="color: #0000ff; font-family: Courier;">="false"</span><span style="color: #ff0000; font-family: Courier;">&nbsp;version</span><span style="color: #0000ff; font-family: Courier;">="3.1"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">welcome-file-list</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">welcome-file</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">index</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">welcome-file</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">welcome-file-list</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">test</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-class</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">test.TestServlet</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-class</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-mapping</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">test</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-name</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">url-pattern</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">/test/*</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">url-pattern</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">servlet-mapping</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">web-app</span><span style="color: #0000ff; font-family: Courier;">&gt;</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">该应用的部署路径为jetty-9.3.11/test-base/webapps/test.war。在该WAR文件所在的目录下，创建一个test.xml，其内容如下所示：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-family: Courier;">&lt;?</span><span style="color: #ff00ff; font-family: Courier;">xml&nbsp;version="1.0"&nbsp;&nbsp;encoding="ISO-8859-1"</span><span style="color: #0000ff; font-family: Courier;">?&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;!</span><span style="color: #ff00ff; font-family: Courier;">DOCTYPE&nbsp;Configure&nbsp;PUBLIC&nbsp;"-//Jetty//Configure//EN"&nbsp;"http://www.eclipse.org/jetty/configure_9_0.dtd"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">Configure&nbsp;</span><span style="color: #ff0000; font-family: Courier;">class</span><span style="color: #0000ff; font-family: Courier;">="org.eclipse.jetty.webapp.WebAppContext"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">Set&nbsp;</span><span style="color: #ff0000; font-family: Courier;">name</span><span style="color: #0000ff; font-family: Courier;">="contextPath"</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; font-family: Courier;">/</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">Set</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-family: Courier;">&lt;</span><span style="color: #800000; font-family: Courier;">Set&nbsp;</span><span style="color: #ff0000; font-family: Courier;">name</span><span style="color: #0000ff; font-family: Courier;">="war"</span><span style="color: #0000ff; font-family: Courier;">&gt;&lt;</span><span style="color: #800000; font-family: Courier;">SystemProperty&nbsp;</span><span style="color: #ff0000; font-family: Courier;">name</span><span style="color: #0000ff; font-family: Courier;">="jetty.base"</span><span style="color: #ff0000; font-family: Courier;">&nbsp;default</span><span style="color: #0000ff; font-family: Courier;">="."</span><span style="color: #0000ff; font-family: Courier;">/&gt;</span><span style="color: #000000; font-family: Courier;">/webapps/test.war</span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">Set</span><span style="color: #0000ff; font-family: Courier;">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-family: Courier;">&lt;/</span><span style="color: #800000; font-family: Courier;">Configure</span><span style="color: #0000ff; font-family: Courier;">&gt;</span></div><span style="font-size: 10pt;">启动Jetty服务器，使用默认的HTTP和HTTPS端口，分别为8080和8443。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;java&nbsp;-jar&nbsp;../start.jar<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:51.190:INFO:oejs.Server:main:&nbsp;jetty-9.3.11.v20160721<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:51.237:INFO:oejdp.ScanningAppProvider:main:&nbsp;Deployment&nbsp;monitor&nbsp;[file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/]&nbsp;at&nbsp;interval&nbsp;1<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.251:INFO:oejw.StandardDescriptorProcessor:main:&nbsp;NO&nbsp;JSP&nbsp;Support&nbsp;for&nbsp;/test.war,&nbsp;did&nbsp;not&nbsp;find&nbsp;org.eclipse.jetty.jsp.JettyJspServlet<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.313:INFO:oejsh.ContextHandler:main:&nbsp;Started&nbsp;o.e.j.w.WebAppContext@4520ebad{/test.war,file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/test.war/,AVAILABLE}{D:\http2\jetty\jetty-9.3.11\test-base\webapps\test.war}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.391:INFO:oejw.StandardDescriptorProcessor:main:&nbsp;NO&nbsp;JSP&nbsp;Support&nbsp;for&nbsp;/,&nbsp;did&nbsp;not&nbsp;find&nbsp;org.eclipse.jetty.jsp.JettyJspServlet<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.391:INFO:oejsh.ContextHandler:main:&nbsp;Started&nbsp;o.e.j.w.WebAppContext@711f39f9{/,file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/test.war/,AVAILABLE}{/test.war}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.532:INFO:oejs.AbstractConnector:main:&nbsp;Started&nbsp;ServerConnector@1b68ddbd{HTTP/1.1,[http/1.1,&nbsp;h2c,&nbsp;h2c-17,&nbsp;h2c-16,&nbsp;h2c-15,&nbsp;h2c-14]}{0.0.0.0:8080}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.735:INFO:oejus.SslContextFactory:main:&nbsp;x509=X509@e320068(jetty,h=[jetty.eclipse.org],w=[])&nbsp;for&nbsp;SslContextFactory@1f57539(file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore,file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore)<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:52.735:INFO:oejus.SslContextFactory:main:&nbsp;x509=X509@76f2b07d(mykey,h=[],w=[])&nbsp;for&nbsp;SslContextFactory@1f57539(file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore,file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore)<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:53.234:INFO:oejs.AbstractConnector:main:&nbsp;Started&nbsp;ServerConnector@4b168fa9{SSL,[ssl,&nbsp;alpn,&nbsp;h2,&nbsp;h2-17,&nbsp;h2-16,&nbsp;h2-15,&nbsp;h2-14,&nbsp;http/1.1]}{0.0.0.0:8443}<br /></span><span style="color: #000000; font-family: Courier;">2016-09-15&nbsp;21:15:53.249:INFO:oejs.Server:main:&nbsp;Started&nbsp;@3940ms</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">根据上述日志可知，Jetty启用了Web应用test.war，还启动了两个ServerConnector，一个支持h2c，另一个支持h2。值得注意的是，这两个ServerConnector还分别支持h2c-17, h2c-16, h2c-15, h2c-14和h2-17, h2-16, h2-15, h2-14。这是因为，HTTP/2在正式发布之前，先后发布了18个草案，其编号为00-17。所以，这里的h2c-XX和h2-XX指的就是第XX号草案。</span><br /><br /><strong><span style="font-size: 12pt;">3. 客户端</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 其实最方便的客户端就是浏览器了。只要使用的FireFox或Chrome版本不是太老，肯定都已经支持了HTTP/2，而且这一功能是默认打开的。也就是说，当使用FireFox去访问前面所部署的Web应用时，就是在使用HTTP/2，但你不会感觉到这种变化。使用FireFox提供的Developer Tools中的Network工具查看服务器端的响应，会发现HTTP版本为HTTP/2.0。但此处希望这个客户端能够提供更为丰富的与服务器端进行交互的功能，那么浏览器就并不合适了。<br />&nbsp;&nbsp;&nbsp; Jetty也实现了支持HTTP/2的客户端，但这个客户端是一个API，需要编写程序去访问HTTP/2服务器端。而且，目前该API的设计抽象层次较低，需要应用程序员对HTTP/2协议，比如各种帧，有较深入的了解。这对于初涉HTTP/2的开发者来说，显然很不合适。本文选择使用C语言编写的一个工具，其实也是HTTP/2的客户端实现之一，curl。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; curl在支持HTTP/2时，实际上是使用了nghttp2的C库，所以需要先安装nghttp2。另外，为了让curl支持h2，就必须要有TLS-ALPN的支持。那么，一般地还需要安装OpenSSL 1.0.2+。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 网络上关于在Linux下安装支持HTTP/2的curl的资源有很多，过程并不难，但有点儿繁，要安装的依赖比较多，本文就不赘述了。如果是使用Windows，笔者比较推荐通过Cygwin来安装和使用curl。在Windows中安装Cygwin非常简单，在Cygwin中执行各种命令时，感觉上就如同在使用Linux，尽管它并不是一个虚拟机。通过Cygwin安装curl，它会自动地安装所需的各种依赖程序和库。</span><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在笔者的机器上，通过查看curl的版本会出现如下信息：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">curl&nbsp;</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; font-family: Courier;">&nbsp;(x86_64-unknown-cygwin)&nbsp;libcurl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; font-family: Courier;">&nbsp;OpenSSL/</span><span style="color: #000000; font-family: Courier;">1.0</span><span style="color: #000000; font-family: Courier;">.2h&nbsp;zlib/</span><span style="color: #000000; font-family: Courier;">1.2.8</span><span style="color: #000000; font-family: Courier;">&nbsp;libidn/</span><span style="color: #000000; font-family: Courier;">1.29</span><span style="color: #000000; font-family: Courier;">&nbsp;libpsl/</span><span style="color: #000000; font-family: Courier;">0.14.0</span><span style="color: #000000; font-family: Courier;">&nbsp;(+libidn/</span><span style="color: #000000; font-family: Courier;">1.29</span><span style="color: #000000; font-family: Courier;">)&nbsp;libssh2/</span><span style="color: #000000; font-family: Courier;">1.7.0</span><span style="color: #000000; font-family: Courier;">&nbsp;nghttp2/</span><span style="color: #000000; font-family: Courier;">1.14.0</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">Protocols:&nbsp;dict&nbsp;file&nbsp;ftp&nbsp;ftps&nbsp;gopher&nbsp;http&nbsp;https&nbsp;imap&nbsp;imaps&nbsp;ldap&nbsp;ldaps&nbsp;pop3&nbsp;pop3s&nbsp;rtsp&nbsp;scp&nbsp;sftp&nbsp;smb&nbsp;smbs&nbsp;smtp&nbsp;smtps&nbsp;telnet&nbsp;tftp<br /></span><span style="color: #000000; font-family: Courier;">Features:&nbsp;Debug&nbsp;IDN&nbsp;IPv6&nbsp;Largefile&nbsp;GSS-API&nbsp;Kerberos&nbsp;SPNEGO&nbsp;NTLM&nbsp;NTLM_WB&nbsp;SSL&nbsp;libz&nbsp;TLS-SRP&nbsp;HTTP2&nbsp;UnixSockets&nbsp;Metalink&nbsp;PSL</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">由上可知，笔者使用的curl版本是7.50.2，nghttp2版本是1.14.0，而OpenSSL版本是1.0.2h。</span><br /><br /><strong><span style="font-size: 12pt;">4. 第一次尝试</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在第一次尝试中，只需要简单地访问第2节中部署的Web应用中的静态文本文件index，以感受下h2c，完整命令如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;curl&nbsp;-v&nbsp;--http2&nbsp;http://localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; font-family: Courier;">/index</span></div><span style="font-size: 10pt;">在输出中包含有如下的内容：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">...<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;GET&nbsp;/index&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Host:&nbsp;localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;User-Agent:&nbsp;curl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Accept:&nbsp;*/*<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Connection:&nbsp;Upgrade</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;HTTP2-Settings<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Upgrade:&nbsp;h2c<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;HTTP2-Settings:&nbsp;AAMAAABkAAQAAP__<br /></span><span style="color: #000000; font-family: Courier;">&gt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; font-family: Courier;">&nbsp;Switching&nbsp;Protocols<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Received&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Using&nbsp;HTTP2</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;server&nbsp;supports&nbsp;multi-use<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Connection&nbsp;state&nbsp;changed&nbsp;(HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;confirmed)<br />...<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">200</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;server:&nbsp;Jetty(</span><span style="color: #000000; font-family: Courier;">9.3.11</span><span style="color: #000000; font-family: Courier;">.v20160721)<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;last-modified:&nbsp;Wed</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">14</span><span style="color: #000000; font-family: Courier;">&nbsp;Sep&nbsp;</span><span style="color: #000000; font-family: Courier;">2016</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">12</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">52</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">32</span><span style="color: #000000; font-family: Courier;">&nbsp;GMT<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;content-length:&nbsp;</span><span style="color: #000000; font-family: Courier;">11</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;accept-ranges:&nbsp;bytes<br /></span><span style="color: #000000; font-family: Courier;">&lt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;Test</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">"&gt;"是客户端发送的请求，"&lt;"是服务器端发送的响应，而"*"是curl对当前过程的说明。</span><span style="font-size: 10pt;">结合本系列<a href="http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html">第一篇文章</a>中所简述的HTTP 2协议，可以有以下的基本理解。</span><br /><span style="font-size: 10pt;">[1]客户端发起了一个HTTP/1.1的请求，其中携带有Upgrade头部，要求服务器端升级到HTTP/2(h2c)。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">&gt;&nbsp;GET&nbsp;/index&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Host:&nbsp;localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;User-Agent:&nbsp;curl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Accept:&nbsp;*/*<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Connection:&nbsp;Upgrade</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;HTTP2-Settings<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Upgrade:&nbsp;h2c<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;HTTP2-Settings:&nbsp;AAMAAABkAAQAAP__<br /></span><span style="color: #000000; font-family: Courier;">&gt;</span></div><span style="font-size: 10pt;"></span><span style="font-size: 10pt;">[2]服务器端同意升级，返回响应"101 Switching Protocols"，然后客户端收到了101响应，HTTP/2连接进行确认。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; font-family: Courier;">&nbsp;Switching&nbsp;Protocols<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Received&nbsp;</span><span style="color: #000000; font-family: Courier;">101</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Using&nbsp;HTTP2</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;server&nbsp;supports&nbsp;multi-use<br /></span><span style="color: #000000; font-family: Courier;">*&nbsp;Connection&nbsp;state&nbsp;changed&nbsp;(HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;confirmed)</span></div><span style="font-size: 10pt;">[3]服务器端响应最终结果。状态行中出现的HTTP版本为HTTP/2，状态代码为200，且后面没有跟着"OK"。最后输出了index文件的内容"HTTP/2 Test"。</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">200</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;server:&nbsp;Jetty(</span><span style="color: #000000; font-family: Courier;">9.3.11</span><span style="color: #000000; font-family: Courier;">.v20160721)<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;last-modified:&nbsp;Wed</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">14</span><span style="color: #000000; font-family: Courier;">&nbsp;Sep&nbsp;</span><span style="color: #000000; font-family: Courier;">2016</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">12</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">52</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">32</span><span style="color: #000000; font-family: Courier;">&nbsp;GMT<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;content-length:&nbsp;</span><span style="color: #000000; font-family: Courier;">11</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;accept-ranges:&nbsp;bytes<br /></span><span style="color: #000000; font-family: Courier;">&lt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;Test</span></div><span style="font-size: 10pt;"></span><br /><strong><span style="font-size: 12pt;">5. 一个局限</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 这次，在发起的请求中包含体部，命令如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;curl&nbsp;-v&nbsp;--http2&nbsp;-d&nbsp;</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">body</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">&nbsp;http://localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; font-family: Courier;">/index</span></div><span style="font-size: 10pt;">在输出中包含有如下的内容：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">...<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;POST&nbsp;/index&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Host:&nbsp;localhost:</span><span style="color: #000000; font-family: Courier;">8080</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;User-Agent:&nbsp;curl/</span><span style="color: #000000; font-family: Courier;">7.50.2</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Accept:&nbsp;*/*<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Connection:&nbsp;Upgrade</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; font-family: Courier;">&nbsp;HTTP2-Settings<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Upgrade:&nbsp;h2c<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;HTTP2-Settings:&nbsp;AAMAAABkAAQAAP__<br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Content-Length:&nbsp;</span><span style="color: #000000; font-family: Courier;">4</span><span style="color: #000000; "><br /></span><span style="color: #000000; font-family: Courier;">&gt;&nbsp;Content-Type:&nbsp;application/x-www-form-urlencoded<br /></span><span style="color: #000000; font-family: Courier;">&gt;<br />...<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">1.1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">200</span><span style="color: #000000; font-family: Courier;">&nbsp;OK<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;Last-Modified:&nbsp;Wed</span><span style="color: #000000; font-family: Courier;">,</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">14</span><span style="color: #000000; font-family: Courier;">&nbsp;Sep&nbsp;</span><span style="color: #000000; font-family: Courier;">2016</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; font-family: Courier;">12</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">52</span><span style="color: #000000; font-family: Courier;">:</span><span style="color: #000000; font-family: Courier;">32</span><span style="color: #000000; font-family: Courier;">&nbsp;GMT<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;Accept-Ranges:&nbsp;bytes<br /></span><span style="color: #000000; font-family: Courier;">&lt;&nbsp;Content-Length:&nbsp;</span><span style="color: #000000; font-family: Courier;">11</span><span style="color: #000000; "><br />...<br /></span><span style="color: #000000; font-family: Courier;">HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;Test</span></div>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;"></span><span style="font-size: 10pt;">和第4节中的输出进行比较，会发现缺少了"101 Switching Protocols"那一段，而且最终响应状态行中出现的HTTP版本是HTTP/1.1。这就说明服务器端不同意升级，后面继续使用HTTP/1.1。刚刚部署的Jetty未做任何改变怎么会突然不支持HTTP/2了呢？或者这是curl的问题？其实，</span><span style="font-size: 10pt;">这是因为Jetty服务器端在实现h2c时不支持请求中包含体部。另外，Apache httpd也有同样的问题。如果是使用h2，则没有这个限制。这背后的原因超出了本文的范畴，不作表述。</span><br /><br /><strong><span style="font-size: 12pt;">6. 一个Bug</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; 在这次尝试中，测试一下两端对100-continue的支持。如果请求中使用了头部"Expect: 100-continue"，那么正常地该请求要有体部。但由于在第5节中介绍的问题，此时不能再使用h2c，而只能使用h2。另外，这次不访问静态文件，而是访问Servlet(此处为/test)。完整命令如下：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">$&nbsp;curl&nbsp;-vk&nbsp;--http2&nbsp;-H&nbsp;</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">Expect:&nbsp;100-continue</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">&nbsp;-d&nbsp;</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">body</span><span style="color: #000000; font-family: Courier;">"</span><span style="color: #000000; font-family: Courier;">&nbsp;https://localhost:</span><span style="color: #000000; font-family: Courier;">8443</span><span style="color: #000000; font-family: Courier;">/test</span></div><span style="font-size: 10pt;">在输出的最后出现了如下信息：</span><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; font-family: Courier;">curl:&nbsp;(</span><span style="color: #000000; font-family: Courier;">92</span><span style="color: #000000; font-family: Courier;">)&nbsp;HTTP/</span><span style="color: #000000; font-family: Courier;">2</span><span style="color: #000000; font-family: Courier;">&nbsp;stream&nbsp;</span><span style="color: #000000; font-family: Courier;">1</span><span style="color: #000000; font-family: Courier;">&nbsp;was&nbsp;not&nbsp;closed&nbsp;cleanly:&nbsp;CANCEL&nbsp;(err&nbsp;</span><span style="color: #000000; font-family: Courier;">8</span><span style="color: #000000; font-family: Courier;">)</span></div><span style="font-size: 10pt;">这其实是Jetty的一个<a href="https://github.com/eclipse/jetty.project/issues/902">Bug</a>，正在开发中的9.3.12已经修复了它。</span><br /><br /><strong><span style="font-size: 12pt;">7. 小结</span></strong><br /><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp; HTTP/2依然算是新潮的技术，对各家的实现，无论是服务器端，客户端，还是分析工具，都要持有一份怀疑态度。这些实现和工具都是程序，都有可能存在bug。而且协议对许多细节没有作出规定，各家都会发挥自己的想像力。比如，Apache httpd和Jetty在实现服务器端推送时，其方式就不尽相同。<br />&nbsp;&nbsp;&nbsp; 在开发自己的HTTP/2实现或应用的时候，需要同时使用已有的不同服务器端和客户端去部署多套测试环境进行对比分析。<br /></span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431814.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-09-20 16:42 <a href="http://www.blogjava.net/jiangshachina/archive/2016/09/20/431814.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探索HTTP/2: HTTP 2协议简述(原)</title><link>http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html</link><dc:creator>John Jiang</dc:creator><author>John Jiang</author><pubDate>Mon, 19 Sep 2016 03:36:00 GMT</pubDate><guid>http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html</guid><wfw:comment>http://www.blogjava.net/jiangshachina/comments/431811.html</wfw:comment><comments>http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jiangshachina/comments/commentRss/431811.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jiangshachina/services/trackbacks/431811.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt;"> </span>
<div>
<div align="center"><strong><span style="font-size: 14pt;">探索HTTP/2: HTTP/2协议简述</span></strong></div>
<span style="font-size: 10pt;">HTTP/2的协议包含着两个RFC：Hypertext Transfer Protocol Version 2 (<a href="https://tools.ietf.org/html/rfc7540">RFC7540</a>)，即HTTP/2；HPACK: Header Compression for HTTP/2 (<a href="https://tools.ietf.org/html/rfc7541">RFC7541</a>)，即HPACK。RFC7540描述了HTTP/2的语义，RFC7541则描述了用于HTTP/2的头部压缩的格式。本文只涉及HTTP/2协议，本系列的后续文章将会涉及HPACK协议。(2016.10.13最后更新)</span><br />
<br />
<strong><span style="font-size: 12pt;">1. HTTP/2要解决的问题</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/1.0只允许在一个TCP连接中出现一个请求。后来的HTTP/1.1虽然引入了请求流水线，以允许在一个连接中发送多个请求，但这只是部分地解决了请求并发的问题。服务器端在返回响应时，还是必须要按照它接收到的请求的顺序进行返回。如果排在前面的响应要消耗较长的时间，那依然会对后面的响应的造成阻塞，亦即线头阻塞(Head-of-line blocking)。所以，客户端必须要使用多条连接去发起多个的请求以实现并发，并进而减小延迟。更大的并发会增大服务器的负载，也会占用更大的网络带宽。另外，头部通常会包含有大量的信息，如cookie，而这也会增加网络传输的开销。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2允许在同一个TCP连接中交错地出现多个请求与响应，亦即多工(Multiplex)。同时，它使用了一个高效的编码方法对头部进行压缩。HTTP/2还允许对请求进行优先级排序，以便让更为重要的请求得以更快的完成，这会进一步提高性能。HTTP/2还改变了服务器端只能被动地向客户端返回响应的定式，允许服务器端主动地向客户端推送数据，这就可以减少客户端发起请求的数量。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">总之，HTTP/2主要是解决性能问题。</span><br />
<br />
<strong><span style="font-size: 12pt;">2. 发起HTTP/2</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2会使用与HTTP/1相同的URI scheme，即http和https。而且实现HTTP/2的服务器端也不会使用不同的端口去分别支持HTTP/1和HTTP/2。这样有利于平滑地从HTTP/1升级到HTTP/2。毕竟目前已部署的绝大部分网络服务都只支持HTTP/1，当未来它们升级到HTTP/2时，如果换用了不同URI scheme或端口，那么肯定会对客户端产生极大的影响。但是HTTP/2协议为运行在http和https上的HTTP/2分别定义了两个不同的标识符：h2c和h2。h2c中的"c"指的是cleartext，即明文。本文后面会使用h2c指代运行在http2上(直接使用TCP)的HTTP/2，而用h2指代运行在https上(使用TLS)的HTTP/2。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">那么，支持HTTP/2的客户端如何知道它所连接的服务器端是否也支持HTTP/2呢？</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">对于h2c，支持HTTP/2的客户端可以在发起的请求中使用HTTP/1.1的Upgrade头部去尝试要求服务器升级到HTTP/2。该请求的格式如下：</span><br />
<span style="font-size: 10pt;">GET / HTTP/1.1</span><br />
<span style="font-size: 10pt;">Host: server.example.com</span><br />
<span style="font-size: 10pt;">Connection: Upgrade, HTTP2-Settings</span><br />
<span style="font-size: 10pt;">Upgrade: h2c</span><br />
<span style="font-size: 10pt;">HTTP2-Settings: &lt;base64url encoding of HTTP/2 SETTINGS payload&gt;</span><br />
<span style="font-size: 10pt;">HTTP2-Settings是一个经由BASE64编码过的字符串，其原始内容是客户端将要发送的SETTINGS帧的载荷，即一些配置参数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">如果服务器端支持HTTP/2，它就响应"101 Switching Protocols"，表示可以进行升级。该响应的格式如下：</span><br />
<span style="font-size: 10pt;">HTTP/1.1 101 Switching Protocols</span><br />
<span style="font-size: 10pt;">Connection: Upgrade</span><br />
<span style="font-size: 10pt;">Upgrade: h2c</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">如果服务器端不支持HTTP/2，则会忽略Upgrade请求头部，后续依然使用HTTP/1.1。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">对于h2，会使用到协议Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension (<a href="https://tools.ietf.org/html/rfc7301">RFC7301</a>)，即TLS-ALPN。该协议允许客户端和服务器端就使用何种版本的HTTP进行协商。如果TLS-ALPN在现实中运行良好的话，也许某天还会使用该方法去协商使用别的协议。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">当客户端与服务器端都同意使用HTTP/2时，双方都需要各自发出一个连接序言(Connection Preface)以进行最后的确认。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">客户端在接收到服务器端的"101 Switching Protocols"响应(针对h2c)或TLS连接的第一个应用数据字节(针对h2)之后会立即发出连接序言。该序言的开头是"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"(其十六进制形式为"0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a")<sup>(1)</sup>，后面必须再跟一个SETTINGS帧，哪怕这个帧是空的。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">服务器端的连接序言则由一个SETTINGS帧构成，该帧必须是服务器端在HTTP/2连接中发送的第一个帧。这个SETTINGS帧可以为空，也可以包含一些希望客户端如何与自己进行通信的必要配置信息。</span><br />
<br />
<strong><span style="font-size: 12pt;">3. 帧(Frame)</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2消息使用二进制格式(实际编码时使用十六进制书写)，相比于文本格式，这样可以提高消息处理的效率。HTTP/2消息的最小单元为帧，它由头部与载荷(Payload)组成。每个帧的长度必须是一个或多个8比特位字节(octet，下文将其简写为"字节")。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">帧头部依次包含有如下的5个字段：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">长度(Length)：该字段占用24个比特位，代表帧载荷的长度。该长度是一个24位的无符号整数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">类型(Type)：该字段占用8个比特位，代表帧的类型。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">标志(Flags)：该字段占用8个比特位，代表帧所定义的一个或多个标志。并不是所有的帧都定义了标志。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">保留位(R)：该字段占用1个比特位，其语义尚未被定义。在读取帧时，该位需要被忽略；但在发送帧时，该位需要保持为0(0x0)。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">流标识符(Stream Identifier)：该字段占用31个比特位，代表该帧所在流的标识符。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">在头部之后，紧接着的就是载荷。载荷的结构与内容完全由帧的类型决定，它的长度也是不定的。<br />
<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2定义了如下10种不同类型的帧。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">DATA：用于携带一组长度不定的字节。一个或多个DATA可作为请求或响应的载荷。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HEADERS：用于开启一个流，并可携带一个头部块片断。头部块指由一个HEADERS/PUSH_PROMISE帧和紧随它的零到多个CONTINUATION帧组成的集合，因为只有它们才可能携带头部信息。这个集合可被分割成一个或一组字节，这样的字节被称为头部块片断。头部块中各个特定类型的帧必须紧紧相邻，不能出现其它类型的帧。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">PRIORITY：用于指定发送端建议的流优先级。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">RST_STREAM：用于立即终止流。当希望取消一个流或发生错误时，就可发送RST_STREAM帧。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">SETTINGS：用于携带可以影响两端之间通信方式的配置参数。SETTINGS帧定义了一个ACK标志，用于指示该帧所设置的参数是否已被接收端获知。当收到一个SETTINGS且其中的ACK标志为0时，接收端必须尽可能快的应用其中已被更新的参数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">PUSH_PROMISE：用于向接收端通知发送端将要创建的流。当接收端接收到该帧时，新的流尚未被发送端创建，但发送端承诺会创建该流。该帧用于实现HTTP/2的重要特性"服务器端推送(Server Push)"。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">PING：用于测量发送端与接收端之间的最小往返时间。这与使用众所周知的ping命令的目的相似，是为了测试某个空闲的连接是否还可用。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">GOAWAY：用于发起对连接的关闭，或触发严重的错误条件。该帧允许一端，在完成对之前已创建的流的处理的同时，优雅地停止接收新的流。一端在创建新的流，另一端在发送GOAWAY，这两者之间天然存在着竞争关系。为了就对这种情况，发送端在发送GOAWAY时会让它携带上(该发送端所知晓的)接收端最后创建的流的标识符，当该GOAWAY被发送之后，发送端将会忽视由接收端创建的任何一个标识符比该标识符大的流。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">WINDOW_UPDATE：用于流量控制。该帧的载荷由一个单比特保留位和一个31比特位的无符号整数组成。该整数向该帧的接收端指示了其向当前流量控制窗口所能增加传输量的值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">CONTINUATION：用于继续发送头部块片断。只要同一个流中前面的帧是HEADERS，PUSH_PROMISE或CONTINUATION，并且该帧没有设置END_HEADERS标志，那么可无限量地发送CONTINUATION帧。</span><br />
<span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp; 部分帧，DATA，HEADERS和PUSH_PROMISE，的载荷中可能包含填白(Padding)。填白在业务上没有实际的用处，它的出现是基于安全目的。比如，可以用它来扰乱实际数据的长度，以减轻特定的HTTP攻击。<br />
<br />
</span><span style="font-size: 10pt;">&nbsp;&nbsp;&nbsp;&nbsp; 发送端发送的帧的最大长度要尊重接收端设定的SETTINGS_MAX_FRAME_SIZE的值。但该值的范围要介于2^14至2^24-1个字节之间。</span><br />
<br />
<strong><span style="font-size: 12pt;">4. 流(Stream)</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">流是用于在客户端与服务器端之间进行帧传送的通道，同一个TCP连接中可以同时有多个流，如下图所示，<br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; font-family: Courier;"></span>
<div><span style="font-family: Courier;"></span>
<div><span style="font-family: Courier;"></span><span style="font-family: Courier;">
<div>
<div>
<div>&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connection&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474; ============================= &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; --------------------- &lt;-- Stream&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;&#9484;&#9472;&#9488;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&#9492;&#9472;&#9496; &lt;-- Frame&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; ---------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474; Client &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474; Server &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; ----------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9484;&#9472;&#9472;&#9488;&#9484;&#9472;&#9472;&#9472;&#9472;&#9488;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472;&#9496;&#9492;&#9472;&#9472;&#9472;&#9472;&#9496;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp; ----------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474; ============================= &#9474;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9474;<br />
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</div>
</div>
</div>
</span></div>
</div>
</div>
<span style="font-size: 10pt;">服务器端和客户端可以交错地向同一个连接中的不同流中传送帧。可以把一个流看作HTTP/1中的一个连接。客户端与服务器端在同一个流中的交互依然遵循发送请求-等待响应模式。两端都可以创建新的流，共享对方创建的流，也可以关闭对方创建的流。帧在流中的顺序是有意义的，接收端会以接收到的顺序去处理帧。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 每个流都有一个标识符，是一个31比特位的无符合整数。在同一个连接中，流标识符是唯一的。由客户端创建的流的标识符为奇数，由服务器创建的流的标识符为偶数。但标识符为0的流可看作连接，用于连接控制信息，创建新的流时不可使用该标识符。同一个连接中的任何一个流的标识符都不可重用，即便这个流已被关闭了。对于长时间没有中断的连接，可能会出现标识符不够用的情况，那时就必须强制创建一个新的连接。<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2协议为流的生命周期定义了7种状态<sup>(2)</sup>：idle，reserved(local)，reserved(remote)，open，half closed(local)，half closed(remote)和closed。当一端接收或发送头部块或(帧DATA和HEADERS的)标志RST_STREAM后可使流的状态发生转变。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">使用流来实现多工就会引起对TCP连接使用的竞争，这会造成流的阻塞。基于帧WINDOW_UPDATE的流量控制方案可以确保相同连接中的流相互之间不会产生破坏性干扰。流量控制可以作用于两个层面，即单个流或整个连接。只有帧DATA需要遵守流量控制，所有其它的帧所有消耗的空间均不会占用流量控制窗口。HTTP/2协议只是定义了WINDOW_UPDATE帧的结构和语义，协议的实现可以选择任何适用自己的流量控制算法。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">流可以有优先级。客户端在创建一个新的流时，可在HEADERS中指定优先级权重。在后续任何时间，通过PRIORITY可以改变流的优先级权重。在并发能力有限的情况下，高权重流的帧会被优先传送。权重的值必须介于1至256之间，默认权重为16。</span><span style="font-size: 10pt;">流与流之间还可以有依赖关系，这种关系会组成一棵依赖关系树。一个流能够指定自己成为另一个流的子流。这一过程，可以是非排他的，也可以是排他的。非排他性依赖，是指一个流在将自己变成另一个流的子流的过程中，允许另一个流还有别的子流，即允许有自己的兄弟流存在。排他性依赖，指在前述过程中，不允许另一个流还有别的子流。如果另一个流已经有子流了，那么该流会把所有潜在的兄弟流先变成自己的子流，然后再使自己成为另一个流的唯一子流。其实，排他性依赖的作用就是为了能够打破已有的关系树，在既成的父子节点中插入新的节点。否则，只能为已有节点添加子节点，那么关系树将不可能进行重构。所有的流在被创建时，默认成为标识符为0x0的流的子流。在"服务器端推送"中生成的"推送"流将自动地成为生成该推送流的流的子流，其默认权重也为16。</span><br />
<br />
<strong><span style="font-size: 12pt;">5. 消息交换</span></strong><br />
<strong><span style="font-size: 10pt;">5.1 请求/响应交换</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2沿袭了HTTP/1的语义，即所有的请求与响应语义均得到了保留，尽管传递这些语义的语法已经改变了。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">一个HTTP/2消息由如下几个部分组成：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[1]仅对于响应消息，可以包含一个携带有1xx响应头部的头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[2]一个头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[3]零到多个携带有体部(Body)消息的DATA帧。HTTP/1中使用的"分块(chunked)"体部将不适用于HTTP/2。因为一个体部可由多个DATA帧组成，所以HTTP/2的体部天然就是可分块的。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">[4]一个可能存在的包含着尾部消息的头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。<br />
<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">HTTP/2仍然沿用HTTP/1中的头部字段，但字段名称中的字母必须全部为小写。另外，还将HTTP/1消息开始行(请求中的<a href="https://tools.ietf.org/html/rfc2616#section-5.1">请求行</a>与响应中的<a href="https://tools.ietf.org/html/rfc2616#section-6.1">状态行</a>)中的消息，分解成了若干伪头部字段，此类字段均以冒号(:)开头。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">HTTP/1请求行格式为"method request-target HTTP-version"，对应的HTTP/2伪头部字段有:method=method和:path=request-target，但HTTP-version无对应字段，默认为HTTP/2。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">HTTP/1状态行格式为"HTTP-version status-code reason-phrase"，对应的HTTP/2伪头部字段有:status=status-code。但HTTP-version无对应字段，默认为HTTP/2；reason-phrase也无对应字段，因为可以通过状态代码查找到其对应的reason-phrase。HTTP/2协议是在尽量减少冗余消息。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2协议还为请求头部定义了另外两个伪字段：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">:scheme：URI中的scheme部分。它可以不仅仅是http或https，因为有时候可能会与非HTTP服务进行交互。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;
<span style="font-size: 10pt;">:authority：URI中的授权部分。即，scheme://user:password@host:port/path?query#fragment中的"user:password@host:port"。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2协议<a href="https://tools.ietf.org/html/rfc7540#section-8.1.3">8.1.3</a>节中给出一些简单示例，展示了如何将HTTP/1消息对应到HTTP/2消息。</span><br />
<strong><span style="font-size: 10pt;">5.2 服务器端推送</span></strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">HTTP/2的服务器端推送是传统的请求/响应模式的一种特殊形式。服务器端在收到客户端的请求(主请求)之后，为了主动向客户端推送更多的内容，会自动地生成若干新的请求(推送请求)。服务器向客户端发送的响应中，不仅包含对主请求的响应(主响应)，还包含对推送请求的响应(推送响应)。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">客户端可以通过发送包含有SETTINGS_MAX_CONCURRENT_STREAMS参数的SETTINGS帧去禁用服务器端推送，也可以通过发送RST_STREAM帧去取消已经发起的服务器端推送，但不能发送包含有END_STREAM标志的帧。<br />
<br />
(1)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"中的"PRI"与"SM"合起来就是"RRISM(棱镜)"。呵呵，HTTPbis工作组这是想表达什么意思呢 ;-)<br />
(2)本系列的<a href="http://www.blogjava.net/jiangshachina/archive/2016/10/08/431871.html">后续文章</a>解读了流的状态。<br />
</span></div><img src ="http://www.blogjava.net/jiangshachina/aggbug/431811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jiangshachina/" target="_blank">John Jiang</a> 2016-09-19 11:36 <a href="http://www.blogjava.net/jiangshachina/archive/2016/09/19/431811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>