HTTP2 详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTP2 详解相关的知识,希望对你有一定的参考价值。

参考技术A

维基百科关于 HTTP/2 的介绍,可以看下定义和发展历史:

Wiki

RFC 7540 定义了 HTTP/2 的协议规范和细节,本文的细节主要来自此文档,建议先看一遍本文,再回过头来照着协议大致过一遍 RFC,如果想深入某些细节再仔细翻看 RFC

RFC7540

1、 TCP 连接数限制

对于同一个域名,浏览器最多只能同时创建 6~8 个 TCP 连接 (不同浏览器不一样)。为了解决数量限制,出现了 域名分片 技术,其实就是资源分域,将资源放在不同域名下 (比如二级子域名下),这样就可以针对不同域名创建连接并请求,以一种讨巧的方式突破限制,但是滥用此技术也会造成很多问题,比如每个 TCP 连接本身需要经过 DNS 查询、三步握手、慢启动等,还占用额外的 CPU 和内存,对于服务器来说过多连接也容易造成网络拥挤、交通阻塞等,对于移动端来说问题更明显,可以参考这篇文章: Why Domain Sharding is Bad News for Mobile Performance and Users

在图中可以看到新建了六个 TCP 连接,每次新建连接 DNS 解析需要时间(几 ms 到几百 ms 不等)、TCP 慢启动也需要时间、TLS 握手又要时间,而且后续请求都要等待队列调度

2、 线头阻塞 (Head Of Line Blocking) 问题

每个 TCP 连接同时只能处理一个请求 - 响应,浏览器按 FIFO 原则处理请求,如果上一个响应没返回,后续请求 - 响应都会受阻。为了解决此问题,出现了 管线化 - pipelining 技术,但是管线化存在诸多问题,比如第一个响应慢还是会阻塞后续响应、服务器为了按序返回相应需要缓存多个响应占用更多资源、浏览器中途断连重试服务器可能得重新处理多个请求、还有必须客户端 - 代理 - 服务器都支持管线化

3、Header 内容多,而且每次请求 Header 不会变化太多,没有相应的压缩传输优化方案

4、为了尽可能减少请求数,需要做合并文件、雪碧图、资源内联等优化工作,但是这无疑造成了单个请求内容变大延迟变高的问题,且内嵌的资源不能有效地使用缓存机制

5、明文传输不安全

帧是数据传输的最小单位,以二进制传输代替原本的明文传输,原本的报文消息被划分为更小的数据帧:

h1 和 h2 的报文对比:

图中 h2 的报文是重组解析过后的,可以发现一些头字段发生了变化,而且所有头字段均小写

关于帧详见: How does it work ?- 帧

在一个 TCP 连接上,我们可以向对方不断发送帧,每帧的 stream identifier 的标明这一帧属于哪个流,然后在对方接收时,根据 stream identifier 拼接每个流的所有帧组成一整块数据。
把 HTTP/1.1 每个请求都当作一个流,那么多个请求变成多个流,请求响应数据分成多个帧,不同流中的帧交错地发送给对方,这就是 HTTP/2 中的多路复用。

流的概念实现了单连接上多请求 - 响应并行,解决了线头阻塞的问题,减少了 TCP 连接数量和 TCP 连接慢启动造成的问题

所以 http2 对于同一域名只需要创建一个连接,而不是像 http/1.1 那样创建 6~8 个连接:

关于流详见: How does it work ?- 流

浏览器发送一个请求,服务器主动向浏览器推送与这个请求相关的资源,这样浏览器就不用发起后续请求。

Server-Push 主要是针对资源内联做出的优化,相较于 http/1.1 资源内联的优势:

关于服务端推送详见: How does it work ?- Server-Push

使用 HPACK 算法来压缩首部内容

关于 HPACK 详见: How does it work ?- HPACK

对于 HTTP/1 来说,是通过设置 tcp segment 里的 reset flag 来通知对端关闭连接的。这种方式会直接断开连接,下次再发请求就必须重新建立连接。HTTP/2 引入 RST_STREAM 类型的 frame,可以在不断开连接的前提下取消某个 request 的 stream,表现更好。

HTTP/2 里的每个 stream 都可以设置依赖 (Dependency) 和权重,可以按依赖树分配优先级,解决了关键请求被阻塞的问题

每个 http2 流都拥有自己的公示的流量窗口,它可以限制另一端发送数据。对于每个流来说,两端都必须告诉对方自己还有足够的空间来处理新的数据,而在该窗口被扩大前,另一端只被允许发送这么多数据。

关于流量控制详见: How does it work ?- 流量控制

合并文件、内联资源、雪碧图、域名分片对于 HTTP/2 来说是不必要的,使用 h2 尽可能将资源细粒化,文件分解地尽可能散,不用担心请求数多

所有帧都是一个固定的 9 字节头部 (payload 之前) 跟一个指定长度的负载 (payload):

共分为十种类型的帧:

DATA 帧有如下标识 (flags):

例子:

HEADERS 帧有以下标识 (flags):

例子:

HTTP/2 里的首部字段也是一个键具有一个或多个值。这些首部字段用于 HTTP 请求和响应消息,也用于服务端推送操作。

首部列表 (Header List) 是零个或多个首部字段 (Header Field) 的集合。当通过连接传送时,首部列表通过压缩算法(即下文 HPACK) 序列化成首部块 (Header Block)。然后,序列化的首部块又被划分成一个或多个叫做首部块片段 (Header Block Fragment) 的字节序列,并通过 HEADERS、PUSH_PROMISE,或者 CONTINUATION 帧进行有效负载传送。

一个完整的首部块有两种可能

必须将首部块作为连续的帧序列传送,不能插入任何其他类型或其他流的帧。尾帧设置 END_HEADERS 标识代表首部块结束,这让首部块在逻辑上等价于一个单独的帧。接收端连接片段重组首部块,然后解压首部块重建首部列表。

https://httpwg.org/specs/rfc7540.html#SETTINGS

一个 SETTINGS 帧的 payload 由零个或多个参数组成,每个参数的形式如下:

在建立连接开始时双方都要发送 SETTINGS 帧以表明自己期许对方应做的配置,对方接收后同意配置参数便返回带有 ACK 标识的空 SETTINGS 帧表示确认,而且连接后任意时刻任意一方也都可能再发送 SETTINGS 帧调整,SETTINGS 帧中的参数会被最新接收到的参数覆盖

SETTINGS 帧作用于整个连接,而不是某个流,而且 SETTINGS 帧的 stream identifier 必须是 0x0,否则接收方会认为错误 (PROTOCOL_ERROR)。

SETTINGS 帧包含以下参数:

SETTINGS 帧有以下标识 (flags):

例子:

实际抓包会发现 HTTP2 请求创建连接发送 SETTINGS 帧初始化前还有一个 Magic 帧 (建立 HTTP/2 请求的前言)。

在 HTTP/2 中,要求两端都要发送一个连接前言,作为对所使用协议的最终确认,并确定 HTTP/2 连接的初始设置,客户端和服务端各自发送不同的连接前言。

客户端的前言内容 (对应上图中编号 23 的帧) 包含一个内容为 PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n 的序列加上一个可以为空的 SETTINGS 帧,在收到 101(Switching Protocols) 响应 (代表 upgrade 成功) 后发送,或者作为 TLS 连接的第一个传输的应用数据。如果在预先知道服务端支持 HTTP/2 的情况下启用 HTTP/2 连接,客户端连接前言在连接建立时发送。

服务端的前言 (对应上图中编号 26 的帧) 包含一个可以为空的 SETTINGS 帧,在建立 HTTP/2 连接后作为第一帧发送。详见 HTTP/2 Connection Preface

发送完前言后双方都得向对方发送带有 ACK 标识的 SETTINGS 帧表示确认,对应上图中编号 29 和 31 的帧。

请求站点的全部帧序列,帧后面的数字代表所属流的 id,最后以 GOAWAY 帧关闭连接:

GOAWAY 帧带有最大的那个流标识符 (比如图中第 29 帧是最大流),对于发送方来说会继续处理完不大于此数字的流,然后再真正关闭连接

流只是一个逻辑上的概念,代表 HTTP/2 连接中在客户端和服务器之间交换的独立双向帧序列,每个帧的 Stream Identifier 字段指明了它属于哪个流。

流有以下特性:

所有流以“空闲”状态开始。在这种状态下,没有任何帧的交换

其状态转换:

此状态下接收到 HEADERS 和 PRIORITY 以外的帧被视为 PROTOCOL_ERROR

状态图中 send PP 和 recv PP 是指连接的双方端点发送或接收了 PUSH_PROMISE,不是指某个空闲流发送或接收了 PUSH_PROMISE,是 PUSH_PROMISE 的出现促使一个预示的流从 idle 状态转为 reserved

PUSH_PROMISE 预示的流由 idle 状态进入此状态,代表准备进行 Server push

其状态转换:

reserved(local) 状态下的流不能发送 HEADERS、RST_STREAM、PRIORITY 以外的帧,接收到 RST_STREAM、PRIORITY、WINDOW_UPDATE 以外的帧被视为 PROTOCOL_ERROR

reserved(remote) 状态下的流不能发送 RST_STREAM、WINDOW_UPDATE、PRIORITY 以外的帧,接收到 HEADERS、RST_STREAM、PRIORITY 以外的帧被视为 PROTOCOL_ERROR

处于 open 状态的流可以被两个对端用来发送任何类型的帧

其状态转换:

流是双向的,半关闭表示这个流单向关闭了,local 代表本端到对端的方向关闭了,remote 代表对端到本端的方向关闭了

此状态下的流不能发送 WINDOW_UPDATE、PRIORITY、RST_STREAM 以外的帧

当此状态下的流收到带有 END_STREAM 标识的帧或者任一方发送 RST_STREAM 帧,会转为 closed 状态

此状态下的流收到的 PRIORITY 帧用以调整流的依赖关系顺序,可以看下文的流优先级

此状态下的流不会被对端用于发送帧,执行流量控制的端点不再有义务维护接收方的流控制窗口。

一个端点在此状态的流上接收到 WINDOW_UPDATE、PRIORITY、RST_STREAM 以外的帧,应该响应一个 STREAM_CLOSED 流错误

此状态下的流可以被端点用于发送任意类型的帧,且此状态下该端点仍会观察流级别的流控制的限制

当此状态下的流发送带有 END_STREAM 标识的帧或者任一方发送 RST_STREAM 帧,会转为 closed 状态

代表流已关闭

此状态下的流不能发送 PRIORITY 以外的帧,发送 PRIORITY 帧是调整那些依赖这个已关闭的流的流优先级,端点都应该处理 PRIORITY 帧,尽管如果该流从依赖关系树中移除了也可以忽略优先级帧

此状态下在收到带有 END_STREAM 标识的 DATA 或 HEADERS 帧后的一小段时间内 (period) 仍可能接收到 WINDOW_UPDATE 或 RST_STREAM 帧,因为在远程对端接收并处理 RST_STREAM 或带有 END_STREAM 标志的帧之前,它可能会发送这些类型的帧。但是端点必须忽略接收到的 WINDOW_UPDATE 或 RST_STREAM

如果一个流发送了 RST_STREAM 帧后转入此状态,而对端接收到 RST_STREAM 帧时可能已经发送了或者处在发送队列中,这些帧是不可撤销的,发送 RST_STREAM 帧的端点必须忽略这些帧。

一个端点可以限制 period 的长短,在 period 内接受的帧会忽略,超出 period 的帧被视为错误。

一个端点发送了 RST_STREAM 帧后接收到流控制帧(比如 DATA),仍会计入流量窗口,即使这些帧会被忽略,因为对端肯定是在接收到 RST_STREAM 帧前发送的流控制帧,对端会认为流控制已生效

一个端点可能会在发送了 RST_STREAM 帧后收到 PUSH_PROMISE 帧,即便预示的流已经被重置 (reset),PUSH_PROMISE 帧也能使预示流变成 reserved 状态。因此,需要 RST_STREAM 来关闭一个不想要的预示流。

下面看两个例子来理解流状态:

(1)、Server 在 Client 发起的一个流上发送 PUSH_PROMISE 帧,其 Promised Stream ID 指定一个预示流用于后续推送,send PP 后这个预示流在服务端从 idle 状态转为 reserve(local) 状态,客户端 recv PP 后这个流从 idle 状态转为 reserve(remote) 状态

(2)(3)、此时预示流处于保留状态,客户端如果选择拒绝接受推送,可以发送 RST 帧关闭这个流;服务端如果此时出问题了也可以发送 RST 帧取消推送。不管哪一方发送或接收到 RST,此状态都转为 closed

(4)、没有出现重置说明推送仍有效,则服务端开始推送,首先发送的肯定是响应的 HEADERS 首部块,此时流状态转为半关闭 half-closed(remote);客户端接收到 HEADERS 后流状态转为半关闭 half-closed(local)

(5)(6)、半关闭状态下的流应该还会继续推送诸如 DATA 帧、CONTINUATION 帧这样的数据帧,如果这个过程碰到任一方发起重置,则流会关闭进入 closed 状态

(7)、如果一切顺利,资源随着数据帧响应完毕,最后一帧会带上 END_STREAM 标识代表这个流结束了,此时流转为 closed 状态

(1)、客户端发起请求,首先发送一个 HEADERS 帧,其 Stream Identifier 创建一个新流,此流从 idle 状态转为 open 状态

(2)(3)、如果客户端取消请求可以发送 RST 帧,服务端出错也可以发送 RST 帧,不管哪一方接收或发送 RST,流关闭进入 closed 状态;

(4)、如果请求结束(END_STREAM),流转为半关闭状态。假如是 GET 请求,一般 HEADERS 帧就是最后一帧,send H 后流会立即进入半关闭状态。假如是 POST 请求,待数据传完,最后一帧带上 END_STREAM 标识,流转为半关闭

(5)(6)、客户端半关闭后服务端开始返回响应,此时任一方接收或发送 RST,流关闭;

(7)、如果一切顺利,等待响应结束(END_STREAM),流关闭

流 ID 是 31 位无符号整数,客户端发起的流必须是奇数,服务端发起的流必须是偶数,0x0 保留为连接控制消息不能用于建立新流。

HTTP/1.1 Upgrade to HTTP/2 时响应的流 ID 是 0x1,在升级完成之后,流 0x1 在客户端会转为 half-closed (local) 状态,因此这种情况下客户端不能用 0x1 初始化一个流

新建立的流的 ID 必须大于所有已使用过的数字,接收到一个错误大小的 ID 应该返回 PROTOCOL_ERROR 响应

使用一个新流时隐式地关闭了对端发起的 ID 小于当前流的且处于 idle 状态的流,比如一个流发送一个 HEADERS 帧打开了 ID 为 7 的流,但还从未向 ID 为 5 的流发送过帧,则流 0x5 会在 0x7 发送完或接收完第一帧后转为 closed 状态

一个连接内的流 ID 不能重用

客户端可以通过 HEADERS 帧的 PRIORITY 信息指定一个新建立流的优先级,其他期间也可以发送 PRIORITY 帧调整流优先级

设置优先级的目的是为了让端点表达它所期望对端在并发的多个流之间如何分配资源的行为。更重要的是,当发送容量有限时,可以使用优先级来选择用于发送帧的流。

流可以被标记为依赖其他流,所依赖的流完成后再处理当前流。每个依赖 (dependency) 后都跟着一个权重 (weight),这一数字是用来确定依赖于相同的流的可分配可用资源的相对比例

每个流都可以显示地依赖另一个流,包含依赖关系表示优先将资源分配给指定的流(上层节点)而不是依赖流

一个不依赖于其他流的流会指定 stream dependency 为 0x0 值,因为不存在的 0x0 流代表依赖树的根

一个依赖于其他流的流叫做 依赖流 ,被依赖的流是当前流的父级。如果被依赖的流不在当前依赖树中(比如状态为 idle 的流),被依赖的流会使用一个默认优先级

当依赖一个流时,该流会添加进父级的依赖关系中,共享相同父级的依赖流不会相对于彼此进行排序,比如 B 和 C 依赖 A,新添加一个依赖流 D,BCD 的顺序是不固定的:

独占标识 (exclusive) 允许插入一个新层级(新的依赖关系),独占标识导致该流成为父级的唯一依赖流,而其他依赖流变为其子级,比如同样插入一个新依赖流 E (带有 exclusive):

在依赖关系树中,只有当一个依赖流所依赖的所有流(父级最高为 0x0 的链)被关闭或者无法继续在上面执行,这个依赖流才应该被分配资源

所有依赖流都会分配一个 1~256 权重值

相同父级的依赖流按权重比例分配资源,比如流 B 依赖于 A 且权重值为 4,流 C 依赖于 A 且权重值为 12,当 A 不再执行时,B 理论上能分配的资源只有 C 的三分之一

使用 PRIORITY 帧可以调整流优先级

PRIORITY 帧内容与 HEADERS 帧的优先级模块相同:

看下面这个例子: 第一个图是初始关系树,现在 A 要调整为依赖 D,根据第二点,现将 D 移至 x 之下,再把 A 调整为 D 的子树(图 3),如果 A 调整时带有独占标识根据第一点 F 也归为 A 子级(图 4)

当一个流从依赖树中移除,它的子级可以调整为依赖被关闭流的父级(应该就是连接上一层节点),新的依赖权重将根据关闭流的权重以及流自身的权重重新计算。

从依赖树中移除流会导致某些优先级信息丢失。资源在具有相同父级的流之间共享,这意味着如果这个集合中的某个流关闭或者阻塞,任何空闲容量将分配给最近的相邻流。然而,如果此集合的共有依赖(即父级节点)从树中移除,这些子流将与更上一层的流共享资源

一个例子: 流 A 和流 B 依赖相同父级节点,而流 C 和流 D 都依赖 A,在移除流 A 之前的一段时间内,A 和 D 都无法执行(可能任务阻塞了),则 C 会分配到 A 的所有资源;
如果 A 被移除出树了,A 的权重按比重新计算分配给 C 和 D,此时 D 仍旧阻塞,C 分配的资源相较之前变少了。对于同等的初始权重,C 获取到的可用资源是三分之一而不是二分之一(为什么是三分之一?文档中没有说明细节,权重如何重新分配也不太清楚,下面是按我的理解解释的)

X 的资源为 1,ABCD 初始权重均为 16,*号代表节点当前不可用,图一中 C 和 B 各占一半资源,而 A 移除后 CD 的权重重新分配变为 8,所以图二中 C 和 B 占比变为 1:2,R(C) 变为 1/3

可能向一个流创建依赖关系的优先级信息还在传输中,那个流就已经关闭了。如果一个依赖流的依赖指向没有相关优先级信息(即父节点无效),则这个依赖流会分配默认优先级,这可能会造成不理想的优先级,因为给流分配了不在预期的优先级。

为了避免上述问题,一个端点应该在流关闭后的一段时间内保留流的优先级调整状态信息,此状态保留时间越长,流被分配错误的或者默认的优先级可能性越低。

类似地,处于“空闲”状态的流可以被分配优先级或成为其他流的父节点。这允许在依赖关系树中创建分组节点,从而实现更灵活的优先级表达式。空闲流以默认优先级开始

流优先级状态信息的保留可能增加终端的负担,因此这种状态可以被限制。终端可能根据负荷来决定保留的额外的状态的数目;在高负荷下,可以丢弃额外的优先级状态来限制资源的任务。在极端情况下,终端甚至可以丢弃激活或者保留状态流的优先级信息。如果使用了固定的限制,终端应当至少保留跟 SETTINGS_MAX_CONCURRENT_STREAMS 设置一样大小的流状态

所有流都是初始为非独占地依赖于流 0x0。

Pushed 流初始依赖于相关的流(见 Server-Push)。

以上两种情况,流的权重都指定为 16。

PUSH_PROMISE 帧有以下标识 (flags):

结合上文关于 Server-Push 的流状态转换

PUSH_PROMISE 帧只能在对端(客户端)发起的且流状态为 open 或者 half-closed (remote) 的流上发送

PUSH_PROMISE 帧准备推送的响应总是和来自于客户端的请求相关联。服务端在该请求所在的流上发送 PUSH_PROMISE 帧。PUSH_PROMISE 帧包含一个 Promised Stream ID,该流标识符是从服务端可用的流标识符里选出来的。

如果服务端收到了一个对文档的请求,该文档包含内嵌的指向多个图片文件的链接,且服务端选择向客户端推送那些额外的图片,那么在发送包含图片链接的 DATA 帧之前发送 PUSH_PROMISE 帧可以确保客户端在发现内嵌的链接之前,能够知道有一个资源将要被推送过来。同样地,如果服务端准备推送被首部块引用的响应 (比如,在 Link 首部字段 里的),在发送首部块之前发送一个 PUSH_PROMISE 帧,可以确保客户端不再请求那些资源

一旦客户端收到了 PUSH_PROMISE 帧,并选择接收被推送的响应,客户端就不应该为准备推送的响应发起任何请求,直到预示的流被关闭以后。

不管出于什么原因,如果客户端决定不再从服务端接收准备推送的响应,或者如果服务端花费了太长时间准备发送被预示的响应,客户端可以发送一个 RST_STREAM 帧,该帧可以使用 CANCEL 或者 REFUSED_STEAM 码,并引用被推送的流标识符。

nginx 为例,从版本 1.13.9 开始正式支持 hppt2 serverpush 功能,

在相应 server 或 location 模块中加入 http2_push 字段加上相对路径的文件即可在请求该资源时推送相关资源,比如我的博客设置如下,访问首页时有四个文件会由服务器主动推送过去而不需要客户端请求:

通过浏览器控制台可以查看 Push 响应:

也可以用 nghttp 测试 push 响应 (* 号代表是服务端推送的):

上面 http2_push 的设置适合静态资源,服务端事先知道哪些文件是客户端需要的,然后选择性推送

假如是后台应用动态生成的文件(比如 json 文件),服务器事先不知道要推送什么,可以用 Link 响应头来做自动推送

在 server 模块中添加 http2_push_preload on;

然后设置响应头 (add_header) 或者后台程序生成数据文件返回时带上响应头 Link 标签,比如

nginx 会根据 Link 响应头主动推送这些资源

更多nginx 官方介绍见 Introducing HTTP/2 Server Push with NGINX 1.13.9

看了这篇文章 HTTP/2 中的 Server Push 讨论 ,发现 Server-Push 有个潜在的问题

Server-Push 满足条件时便会发起推送,可是客户端已经有缓存了想发送 RST 拒收,而服务器在收到 RST 之前已经推送资源了,虽然这部分推送无效但是肯定会占用带宽

比如我上面博客关于 http2_push 的配置,我每次打开首页服务器都会推送那四个文件,而实际上浏览器知道自己有缓存使用的也是本地缓存,也就是说本地缓存未失效的期间内,服务器的 Server-Push 只是起到了占用带宽的作用

当然实际上对我的小站点来说影响并不大,但是如果网站需要大量推送的话,需要考虑并测试 Server-Push 是否会影响用户的后续访问

另外服

HTTP协议详解

HTTP协议详解

摘自 HTTP协议详解

2016-12-01

 目录

一、概念
二、简史
三、特点
四、工作流程
五、使用Wireshark抓TCP、http包
六、头域
  6.1、请求信息
  6.2、请求方法
    GET和POST的区别
  6.3、响应消息
  6.4、响应头域 

  6.5、HTTP常见的请求头
  6.6、HTTP常见的响应头
七、解决HTTP无状态的问题
  7.1、通过Cookies保存状态信息
  7.2、通过Session保存状态信息
    Cookie和Session有以下明显的不同点
    Token ,Cookie和Session的区别--学习笔记
  7.3、通过表单变量保持状态
  7.4、通过QueryString保持状态
八、使用telnet进行http测试
九、URL详解
十、缓存的实现原理
  10.1、缓存的优点
  10.2、客户端缓存生效的常见流程
  10.3、Web缓存机制
十一、HTTP应用
  11.1、断点续传的实现原理
  11.2、多线程下载的原理
  11.3、http代理
  11.4、虚拟主机
十二、HTTP认证方式
  12.1、基本认证 basic authentication(HTTP1.0提出的认证方法)
  12.2、摘要认证 digest authentication(HTTP1.1提出的基本认证的替代方法)
十三、HTTPS传输协议原理
  13.1、两种基本的加解密算法类型
  13.2、HTTPS通信过程
  13.3、HTTPS通信的优点
十四、http的状态响应码

 

简析TCP的三次握手与四次分手

听大神说:https和http有何区别?

HTTPS的工作原理

HTTPS加密原理

一、概念


 返回

协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。

HTTP协议,即超文本传输协议(Hypertext transfer protocol)。是一种详细规定了浏览器和万维网(WWW = World Wide Web)服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。

HTTP协议是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。

HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。

在Internet中所有的传输都是通过TCP/IP进行的。HTTP协议作为TCP/IP模型中应用层的协议也不例外。HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。如下图所示:

HTTP默认的端口号为80,HTTPS的端口号为443。

浏览网页是HTTP的主要应用,但是这并不代表HTTP就只能应用于网页的浏览。HTTP是一种协议,只要通信的双方都遵守这个协议,HTTP就能有用武之地。比如咱们常用的QQ,迅雷这些软件,都会使用HTTP协议(还包括其他的协议)。

二、简史


 返回 

它的发展是万维网协会(World Wide Web Consortium)和Internet工作小组IETF(Internet Engineering Task Force)合作的结果,(他们)最终发布了一系列的RFC,RFC 1945定义了HTTP/1.0版本。其中最著名的就是RFC 2616。RFC 2616定义了今天普遍使用的一个版本——HTTP 1.1。

三、特点


 

 返回

HTTP协议永远都是客户端发起请求,服务器回送响应。这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端。

HTTP协议的主要特点可概括如下:

  1. 支持客户/服务器模式。支持基本认证和安全认证。
  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  3. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  4. HTTP 0.9和1.0使用非持续连接:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。HTTP 1.1使用持续连接:不必为每个web对象创建一个新的连接,一个连接可以传送多个对象,采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。

无状态协议:

协议的状态是指下一次传输可以“记住”这次传输信息的能力。 http是不会为了下一次连接而维护这次连接所传输的信息,为了保证服务器内存。 比如客户获得一张网页之后关闭浏览器,然后再一次启动浏览器,再登陆该网站,但是服务器并不知道客户关闭了一次浏览器。 由于Web服务器要面对很多浏览器的并发访问,为了提高Web服务器对并发访问的处理能力,在设计HTTP协议时规定Web服务器发送HTTP应答报文和文档时,不保存发出请求的Web浏览器进程的任何状态信息。这有可能出现一个浏览器在短短几秒之内两次访问同一对象时,服务器进程不会因为已经给它发过应答报文而不接受第二期服务请求。由于Web服务器不保存发送请求的Web浏览器进程的任何信息,因此HTTP协议属于无状态协议(Stateless Protocol)。

HTTP协议是无状态的和Connection: keep-alive的区别:

无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。从另一方面讲,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。 HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)。 从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。 Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。

四、工作流程


 返回

一次HTTP操作称为一个事务,其工作过程可分为四步:

  1. 首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。
  2. 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
  3. 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
  4. 客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。

对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

 

HTTP是基于传输层的TCP协议,而TCP是一个端到端的面向连接的协议。所谓的端到端可以理解为进程到进程之间的通信。所以HTTP在开始传输之前,首先需要建立TCP连接,而TCP连接的过程需要所谓的“三次握手”。下图所示TCP连接的三次握手。 在TCP三次握手之后,建立了TCP连接,此时HTTP就可以进行传输了。一个重要的概念是面向连接,既HTTP在传输完成之间并不断开TCP连接。在HTTP1.1中(通过Connection头设置)这是默认行为。

 

 

五、使用Wireshark抓TCP、http包


 返回

打开Wireshark,选择工具栏上的"Capture"->"Options"

 

点击"Capture Filter",此处选择的是"HTTP TCP port(80)",选择后点击上图的"Start"开始抓包。 然后在浏览器中打开http://image.baidu.com/,抓包结果如下图所示:

在上图中,可清晰的看到客户端浏览器(ip为192.168.1.6)与服务器(115.239.210.36)的交互过程:

  1. No1:浏览器(192.168.1.6)向服务器(115.239.210.36)发出连接请求。此为TCP三次握手第一步,此时从图中可以看出,为SYN,seq:X (x=0);
  2. No2:服务器(115.239.210.36)回应了浏览器(192.168.1.6)的请求,并要求确认,此时为:SYN,ACK,此时seq:y(y为0),ACK:x+1(为1)。此为三次握手的第二步;
  3. No3:浏览器(192.168.1.6)回应了服务器(115.239.210.36)的确认,连接成功。为:ACK,此时seq:x+1(为1),ACK:y+1(为1)。此为三次握手的第三步;
  4. No4:浏览器(192.168.1.6)发出一个页面HTTP请求;
  5. No5:服务器(115.239.210.36)确认;
  6. No6:服务器(115.239.210.36)发送数据;
  7. No8:客户端浏览器(192.168.1.6)确认;
  8. No81:客户端(192.168.1.6)发出一个图片HTTP请求;
  9. No202:服务器(115.239.210.36)发送状态响应码200 OK。

 

六、头域


 返回 

每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。

6.1、请求信息:

发出的请求信息格式如下:

  • 请求行,例如GET /images/logo.gif HTTP/1.1,表示从/images目录下请求logo.gif这个文件。
  • (请求)头,例如Accept-Language: en
  • 空行
  • 可选的消息体 请求行和标题必须以<CR><LF>作为结尾(也就是,回车然后换行)。空行内必须只有<CR><LF>而无其他空格。在HTTP/1.1协议中,所有的请求头,除post外,都是可选的。

 

三个部分分别是:请求行、消息报头、请求正文。

6.2、请求方法

HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式:

  • OPTIONS - 返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送\'*\'的请求来测试服务器的功能性。
  • HEAD- 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新。
  • GET - 向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在web app.中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
  • POST - 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
  • PUT - 向指定资源位置上传其最新内容。
  • DELETE - 请求服务器删除Request-URI所标识的资源。
  • TRACE- 回显服务器收到的请求,主要用于测试或诊断。
  • CONNECT - HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
  • PATCH - 用来将局部修改应用于某一资源,添加于规范RFC5789。 方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed);当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。 HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法。

GET和POST的区别

  • GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditPosts.aspx?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的Body中。
  • GET提交的数据大小有限制,最多只能有1024字节(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制。
  • GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值。
  • GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码。

6.3、响应消息

客户端向服务器发送一个请求,服务器以一个状态行作为响应,响应的内容包括:消息协议的版本、成功或者错误编码、服务器信息、实体元信息以及必要的实体内容。根据响应类别的类别,服务器响应里可以含实体内容,但不是所有的响应都有实体内容。

响应头第一行也称为状态行,格式如下(下图中红线标出的那行):

HTTP-Version 空格 Status-Code 空格 Reason-Phrase CRLF

其中,

  • HTTP- Version表示HTTP版本,例如为HTTP/1.1。
  • Status- Code是结果代码,用三个数字表示。
  • Reason-Phrase是个简单的文本描述,解释Status-Code的具体原因。

Status-Code用于机器自动识别,Reason-Phrase用于人工理解。Status-Code的第一个数字代表响应类别,可能取5个不同的值。后两个数字没有分类作用。Status-Code的第一个数字代表响应的类别,后续两位描述在该类响应下发生的具体状况,具体请参见:HTTP状态码 。

 

响应消息的结构: 

三个部分分别是:状态行、消息报头、响应正文。

无论你何时浏览一个网页,你的电脑都会通过一个使用HTTP协议的服务器来获取所请求的数据。在你请求的网页显示在浏览器之前,支配网页的网站服务器会返回一个包含有状态码的HTTP头文件。这个状态码提供了有关所请求网页的相关条件信息。如果一切正常,一个标准网页会收到一条诸如200的状态码。当然我们的目的不是去研究200响应码,而是去探讨那些代表出现错误信息的服务器头文件响应码,例如表示“未找到指定网页”的404码。

6.4、响应头域

服务器需要传递许多附加信息,这些信息不能全放在状态行里。因此,需要另行定义响应头域,用来描述这些附加信息。响应头域主要描述服务器的信息和Request-URI的信息。

 

6.5、HTTP常见的请求头

(在HTTP/1.1 协议中,所有的请求头,除Host外,都是可选的)

  • If-Modified-Since:把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就会返回200和新的文件内容。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中。 例如:If-Modified-Since: Thu, 09 Feb 2012 09:07:57 GMT

 

  • If-None-Match:If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 当用户再次请求该资源时,将在HTTP Request 中加入If-None-Match信息(ETag的值)。如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。否则将返回200状态和新的资源和Etag.  使用这样的机制将提高网站的性能。例如: If-None-Match: "03f2b33c0bfcc1:0"。

 

  • Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝;在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一样。Pargma只有一个用法, 例如: Pragma: no-cache 注意: 在HTTP/1.0版本中,只实现了Pragema:no-cache, 没有实现Cache-Control
  • Cache-Control:指定请求和响应遵循的缓存机制。缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程)。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage。
    •   Cache-Control:Public 可以被任何缓存所缓存
    •   Cache-Control:Private 内容只缓存到私有缓存中
    •   Cache-Control:no-cache 所有内容都不会被缓存
    •   Cache-Control:no-store 用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
    •   Cache-Control:max-age 指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
    •   Cache-Control:min-fresh 指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
    •   Cache-Control:max-stale 指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
  • Accept:浏览器端可以接受的MIME类型。例如:Accept: text/html 代表浏览器可以接受服务器回发的类型为 text/html 也就是我们常说的html文档,如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(non acceptable)。通配符 * 代表任意类型,例如 Accept: */* 代表浏览器可以处理所有类型,(一般浏览器发给服务器都是发这个)。
  • Accept-Encoding:浏览器申明自己可接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate);Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。例如: Accept-Encoding: gzip, deflate。如果请求消息中没有设置这个域,服务器假定客户端对各种内容编码都可以接受。
  • Accept-Language:浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等;例如:Accept-Language: en-us。如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。
  • Accept-Charset:浏览器可接受的字符集。如果在请求消息中没有设置这个域,缺省表示任何字符集都可以接受。
  • User-Agent:告诉HTTP服务器,客户端使用的操作系统和浏览器的名称和版本。 例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)。
  • Content-Type:例如:Content-Type: application/x-www-form-urlencoded。
  • Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。提供了Request的上下文信息的服务器,告诉服务器我是从哪个链接过来的,比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。 例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT
  • Connection: 例如:Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。HTTP 1.1默认进行持久连接。利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。 Connection: close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭,当客户端再次发送Request,需要重新建立TCP连接。
  • Host:(发送请求时,该头域是必需的)主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。 例如: 我们在浏览器中输入:http://www.guet.edu.cn/index.html,浏览器发送的请求消息中,就会包含Host请求头域:Host:http://www.guet.edu.cn,此处使用缺省端口号80,若指定了端口号,则变成:Host:指定端口号。
  • Cookie:最重要的请求头之一, 将cookie的值发送给HTTP服务器。
  • Content-Length:表示请求消息正文的长度。例如:Content-Length: 38。
  • Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。
  • UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
  • From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
  • Range:可以请求实体的一个或者多个子范围。例如, 表示头500个字节:bytes=0-499 表示第二个500字节:bytes=500-999 表示最后500个字节:bytes=-500 表示500字节以后的范围:bytes=500- 第一个和最后一个字节:bytes=0-0,-1 同时指定几个范围:bytes=500-600,601-999 但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200(OK)。

 6.6、HTTP常见的响应头

  • Allow:服务器支持哪些请求方法(如GET、POST等)。
  • Date:表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦
  • Expires:指明应该在什么时候认为文档已经过期,从而不再缓存它,重新从服务器获取,会更新缓存。过期之前使用本地缓存。HTTP1.1的客户端和缓存会将非法的日期格式(包括0)看作已经过期。eg:为了让浏览器不要缓存页面,我们也可以将Expires实体报头域,设置为0。 例如: Expires: Tue, 08 Feb 2022 11:35:14 GMT
  • P3P:用于跨域设置Cookie, 这样可以解决iframe跨域访问cookie的问题 例如: P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR
  • Set-Cookie:非常重要的header, 用于把cookie发送到客户端浏览器,每一个写入cookie都会生成一个Set-Cookie。 例如: Set-Cookie: sc=4c31523a; path=/; domain=.acookie.taobao.com
  • ETag:和If-None-Match 配合使用。
  • Last-Modified:用于指示资源的最后修改日期和时间。Last-Modified也可用setDateHeader方法来设置。
  • Content-Type:WEB服务器告诉浏览器自己响应的对象的类型和字符集。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。可在web.xml文件中配置扩展名和MIME类型的对应关系。 例如:Content-Type: text/html;charset=utf-8    Content-Type:text/html;charset=GB2312    Content-Type: image/jpeg
  • 媒体类型的格式为:大类/小类,比如text/html。 IANA(The Internet Assigned Numbers Authority,互联网数字分配机构)定义了8个大类的媒体类型,分别是: application— (比如: application/vnd.ms-excel.) audio (比如: audio/mpeg.) image (比如: image/png.) message (比如,:message/http.) model(比如:model/vrml.) multipart (比如:multipart/form-data.) text(比如:text/html.) video(比如:video/quicktime.)
  • Content-Range:用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-length。 例如,传送头500个字节次字段的形式:Content-Range:bytes0-499/1234如果一个http消息包含此节(例如,对范围请求的响 应或对一系列范围的重叠请求),Content-Range表示传送的范围。
  • Content-Length:指明实体正文的长度,以字节方式存储的十进制数字来表示。在数据下行的过程中,Content-Length的方式要预先在服务器中缓存所有数据,然后所有数据再一股脑儿地发给客户端。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。 例如: Content-Length: 19847
  • Content-Encoding:WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。 例如:Content-Encoding:gzip
  • Content-Language:WEB服务器告诉浏览器自己响应的对象所用的自然语言。例如: Content-Language:da。没有设置该域则认为实体内容将提供给所有的语言阅读。
  • Server:指明HTTP服务器用来处理请求的软件信息。例如:Server: Microsoft-IIS/7.5、Server:Apache-Coyote/1.1。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。
  • X-AspNet-Version:如果网站是用ASP.NET开发的,这个header用来表示ASP.NET的版本。 例如: X-AspNet-Version: 4.0.30319
  • X-Powered-By:表示网站是用什么技术开发的。 例如: X-Powered-By: ASP.NET
  • Connection: 例如:Connection: keep-alive  当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。 Connection: close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭,当客户端再次发送Request,需要重新建立TCP连接。
  • Location:用于重定向一个新的位置,包含新的URL地址。表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。Location响应报头域常用在更换域名的时候。
  • Refresh:表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
  • WWW-Authenticate:该响应报头域必须被包含在401(未授权的)响应消息中,客户端收到401响应消息时候,并发送Authorization报头域请求服务器对其进行验证时,服务端响应报头就包含该报头域。 eg:WWW-Authenticate:Basic realm="Basic Auth Test!"  //可以看出服务器对请求资源采用的是基本验证机制。

 

七、解决HTTP无状态的问题


 返回

7.1、通过Cookies保存状态信息

通过Cookies,服务器就可以清楚的知道请求2和请求1来自同一个客户端。

 

7.2、通过Session保存状态信息

Session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。 当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。

Session的实现方式:

1、使用Cookie来实现 服务器给每个Session分配一个唯一的JSESSIONID,并通过Cookie发送给客户端。 当客户端发起新的请求的时候,将在Cookie头中携带这个JSESSIONID。这样服务器能够找到这个客户端对应的Session。

 

2、使用URL回写来实现 URL回写是指服务器在发送给浏览器页面的所有链接中都携带JSESSIONID的参数,这样客户端点击任何一个链接都会把JSESSIONID带会服务器。如果直接在浏览器输入服务端资源的url来请求该资源,那么Session是匹配不到的。 Tomcat对Session的实现,是一开始同时使用Cookie和URL回写机制,如果发现客户端支持Cookie,就继续使用Cookie,停止使用URL回写。如果发现Cookie被禁用,就一直使用URL回写。jsp开发处理到Session的时候,对页面中的链接记得使用response.encodeURL() 。

Cookie和Session有以下明显的不同点:

  • Cookie将状态保存在客户端,Session将状态保存在服务器端;
  • Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。Cookie最早在RFC2109中实现,后续RFC2965做了增强。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies。Session并没有在HTTP的协议中定义;
  • Session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器;
  • 就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的SESSION机制更安全些。因为它不会任意读取客户存储的信息。

Token ,Cookie和Session的区别--学习笔记

7.3、通过表单变量保持状态

除了Cookies之外,还可以使用表单变量来保持状态,比如Asp.net就通过一个叫ViewState的Input=“hidden”的框来保持状态,比如: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0OTM4MTAwNGRkXUfhlDv1Cs7/qhBlyZROCzlvf5U=" /> 这个原理和Cookies大同小异,只是每次请求和响应所附带的信息变成了表单变量。

7.4、通过QueryString保持状态

QueryString通过将信息保存在所请求地址的末尾来向服务器传送信息,通常和表单结合使用,一个典型的QueryString比如:www.xxx.com/xxx.aspx?var1=value&var2=value2

八、使用telnet进行http测试


 返回

在Windows下,可使用命令窗口进行http简单测试。

  1. 输入cmd进入命令窗口,在命令行键入如下命令后按回车: telnet www.baidu.com 80
  2. 而后在窗口中按下"Ctrl+]"后按回车可让返回结果回显。
  3. 接着开始发请求消息,例如发送如下请求消息请求baidu的首页消息,使用的HTTP协议为HTTP/1.1: GET /index.html HTTP/1.1 注意:copy如上的消息到命令窗口后需要按两个回车换行才能得到响应的消息,第一个回车换行是在命令后键入回车换行,是HTTP协议要求的。第二个是确认输入,发送请求。

可看到,当采用HTTP/1.1时,连接不是在请求结束后就断开的。若采用HTTP1.0,在命令窗口键入: GET /index.html HTTP/1.0 此时可以看到请求结束之后马上断开。 读者还可以尝试在使用GET或POST等时,带上头域信息,例如键入如下信息: GET /index.html HTTP/1.1 connection: close Host: www.baidu.com

九、URL详解


 返回

URL(Uniform Resource Locator) 地址用于描述一个网络上的资源, 基本格式如下

  • schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]   scheme          指定低层使用的协议(例如:http, https, ftp)   
  • host HTTP服务器的IP地址或者域名   
  • port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/   
  • path 访问资源的路径   
  • url-params
  • query-string 发送给http服务器的数据   
  • anchor- 锚

URL 的一个例子: 

http://www.mywebsite.com/sj/test;id=8079?name=sviergn&x=true#stuff 
  •Schema: http 
  •host: www.mywebsite.com 
  •path: /sj/test 
  •URL params: id=8079 
  •Query String: name=sviergn&x=true 
  •Anchor: stuff

十、缓存的实现原理


 返回

WEB缓存(cache)位于Web服务器和客户端之间。 缓存会根据请求保存输出内容的副本,例如html页面,图片,文件,当下一个请求来到的时候:如果是相同的URL,缓存直接使用副本响应访问请求,而不是向源服务器再次发送请求。 HTTP协议定义了相关的消息头来使WEB缓存尽可能好的工作。

10.1、缓存的优点

减少相应延迟:因为请求从缓存服务器(离客户端更近)而不是源服务器被相应,这个过程耗时更少,让web服务器看上去相应更快。 减少网络带宽消耗:当副本被重用时会减低客户端的带宽消耗;客户可以节省带宽费用,控制带宽的需求的增长并更易于管理。

10.2、客户端缓存生效的常见流程

HTTP2.0 原理详解

HTTP2 详解

HTTP详解

HTTP详解

HTTP详解

HTTP 协议详解