http协议学习笔记

写在前面

  http协议是非常基础的东西,在大学里的计算机网络课程里面就有提到过这方面的内容,不过就略微提到了一下,之后我自己看了《http权威指南》中的一部分内容,写这篇文章的时候还没有完全看完,后来又在网上查找了一些资料,然后这里我就简单地做了一些总结,好方便理解并且加深自己的记忆。

http协议简要介绍

  超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是互联网上应用最为广泛的一种应用层的网络协议。

http事务

  一个 HTTP 事务由一条(从客户端发往服务器的)请求命令和一个(从服务器 发回客户端的)响应结果组成。这种通信是通过名为 HTTP 报文(HTTP message) 的格式化数据块进行的。


  http事务的一次工作流程一般可以分为四个步骤:
  1)首先客户机与服务器需要建立连接。HTTP 是个应用层协议,HTTP 无需操心网络通信的具体细节;它把网络传输的的具体过程都交给了通用、可靠的因特网传输协议 TCP/IP。
  2)建立连接后,客户机发送一个请求给服务器,请求方式要符合http报文的格式,里面一般包括请求的方法,资源,资源MIME类型,主机地址以及一些其他参数。
  3)服务器接到请求后,给予相应的响应信息。响应信息也是按照响应报文的格式来组织的。这条响应中一般包含了 HTTP 的版本号,成功状态码,一个描述性的原因短语,以及一块响应首部字段,在所有这些内容之后跟着包含了所请求文档的响应主体。
  4)客户端接收服务器所返回的信息,然后客户机与服务器断开连接。 如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端。

http连接和TCP/IP协议

  http网络协议栈(https是http的安全版本):

  http协议在发送请求报文之前,需要用网际协议(Internet Protocol,IP) 地址和端口号在客户端和服务器之间建立一条 TCP/IP 连接,在 TCP 中,你需要知道服务器的 IP 地址,以及与服务器上运行的特定软件相关的 TCP 端口号,而获取http服务器的IP地址和端口号就要通过URL了。而这个步骤可以概括为:
  1) 浏览器从URL中解析出服务器的主机名;
  2) 浏览器将服务器的主机名转换成服务器的 IP 地址(域名解析);
  3) 浏览器将端口号(如果有的话)从URL中解析出来;
  4) 浏览器建立一条与Web服务器的TCP连接;
  有关TCP连接的一些知识,在《http权威指南》一书的第四章有详细的介绍,这里我简单地做了一些记录笔记:HTTP 要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据流之后,会将数据流砍成被称作段的小数据块并将段封装在IP分组中通过因特网进行传输。所有这些工作都是由TCP/IP软件来处理的,这一过程对于HTTP程序员来说是透明的。每个TCP段都是由IP分组承载从一个IP地址发送到另一个IP地址的。每个IP分组中都包括:
  1) 一个IP分组首部(通常为 20 字节)
  2) 一个TCP段首部(通常为 20 字节)
  3) 一个 TCP 数据块(0 个或多个字节)
  IP首部包含了源和目的IP地址,长度和其他一些标记。TCP段的首部包含了TCP端口号,TCP控制标记,以及用于数据排序和完整性检查的一些数字值。
  TCP连接握手:
  TCP连接握手需要经过以下几个步骤:
  1)请求新的TCP连接时,客户端要向服务器发送一个小的TCP分组(通常是40~60个字节)。这个分组中设置了一个特殊的SYN标记,说明这是一个连接请求。
  2)如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个 TCP分组,这个分组中的SYN和ACK标记都被置位,说明连接请求已被接受。
  3)最后,客户端向服务器回送一条确认信息,通知它连接已成功建立。如下图:

http报文

  HTTP报文是在HTTP应用程序之间发送的格式化的数据块。这些数据块以一些文本形式的元信息(meta-information)开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。这些报文在客户端,服务器和代理之间流动。每条报文都包含一 条来自客户端的请求,或者一条来自服务器的响应。它们由三个部分组成:对报文进行描述的起始行(start line),包含属性的首部(header)块,以及可选的,包含数据的主体(body)部分。所有的HTTP报文都可以分为两类:请求报文(request message)和响应报文(response message)。请求报文会向Web服务器请求一个动作。响应报文会将请求的结果返回给客户端。请求和响应报文的基本报文结构相同。
  这是请求报文的格式:
  < method> < request-URL> < version>
  < headers>
  < entity-body>
  这是响应报文的格式(只有起始行的语法有所不同):
  < version> < status> < reason-phrase>
  < headers>
  < entity-body>
  例如:访问http://owlsn.github.io/blog ,在谷歌浏览器中查看请求报文头部:

  查看响应报文头部:

  其中请求报文主体部分没有参数或者其他内容,所以主体部分为空,而响应报文主体部分就是整个网页的html代码部分。
  以下是对各部分的描述:
  1) 方法(method)
  客户端希望服务器对资源执行的动作。是一个单独的词,比如 GET、HEAD 或 POST。
  2) 请求URL(request-URL)
  命名了所请求资源,或者 URL 路径组件的完整 URL。如果直接与服务器进行对 话,只要 URL 的路径组件是资源的绝对路径,通常就不会有什么问题——服务 器可以假定自己是URL的主机:端口。
  3) 版本(version)
  报文所使用的HTTP版本,其格式看起来是这样的:HTTP/< major>.< minor>
其中主要版本号(major)和次要版本号(minor)都是整数。
  4) 状态码(status-code)
  这三位数字描述了请求过程中所发生的情况。每个状态码的第一位数字都用于描 述状态的一般类别(“成功”、“出错”等)。
  5) 原因短语(reason-phrase)
  数字状态码的可读版本,包含行终止序列之前的所有文本。原因短语只对人类有意义,因此,比如说,尽管响应行 HTTP/1.0 200 NOT OK 和 HTTP/1.0 200 OK 中原因短语的含义不同,但同样都会被当作成功指示处理。
  6) 首部(header)
  可以有零个或多个首部,每个首部都包含一个名字,后面跟着一个冒号(:),然后是一个可选的空格,接着是一个值,最后是一个 CRLF。首部是由一个空行(CRLF)结束的,表示了首部列表的结束和实体主体部分的开始。有些HTTP版本,比如HTTP/1.1,要求有效的请求或响应报文中必须包含特定的首部。
  7) 实体的主体部分(entity-body)
  实体的主体部分包含一个由任意数据组成的数据块。并不是所有的报文都包含实体的主体部分,有时,报文只是以一个 CRLF 结束。
  在请求和响应报文中都可以用首部来提供信息,有些首部是某种报文专用的,有些 首部则更通用一些。可以将首部分为五个主要的类型:通用首部,请求首部,响应首部,实体首部,扩展首部。具体的一些首部的含义可以查阅相关的技术手册。

web缓存

  Web缓存是可以自动保存常见文档副本的HTTP设备。当Web请求抵达缓存时,如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这个文档。使用缓存有下列优点:
  1) 缓存减少了冗余的数据传输,节省了你的网络费用。
  2) 缓存缓解了网络瓶颈的问题。不需要更多的带宽就能够更快地加载页面。
  3) 缓存降低了对原始服务器的要求。服务器可以更快地响应,避免过载的出现。
  4) 缓存降低了距离时延,因为从较远的地方加载页面会更慢一些。

http缓存处理步骤

  现代的商业化代理缓存相当地复杂。这些缓存构建得非常高效,可以支持 HTTP 和 其他一些技术的各种高级特性。但除了一些微妙的细节之外,Web 缓存的基本工作 原理大多很简单。对一条 HTTP GET报文的基本缓存处理过程包括7个步骤。
  1) 接收——缓存从网络中读取抵达的请求报文。
  2) 解析——缓存对报文进行解析,提取出 URL 和各种首部。
  3) 查询——缓存查看是否有本地副本可用,如果没有,就获取一份副本(并将其保 存在本地)。
  4) 新鲜度检测——缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是 否有任何更新。
  5) 创建响应——缓存会用新的首部和已缓存的主体来构建一条响应报文。
  6) 发送——缓存通过网络将响应发回给客户端。
  7) 日志——缓存可选地创建一个日志文件条目来描述这个事务。
  HTTP有一些简单的机制可以在不要求服务器记住有哪些缓存拥有其文档副本的情况下,保持已缓存数据与服务器数据之间充分一致。HTTP 将这些简单的机制称为文档过期(document expiration)和服务器再验证(server revalidation),
通过特殊的HTTP Cache-Control首部和Expires首部,HTTP让原始服务器向每个文档附加了一个“过期日期”。
  

文档过期(document expiration)和服务器再验证(server revalidation)

  HTTP 有一些简单的机制可以在不要求服务器记住有哪些缓存拥有其文档副本的情况下,保持已缓存数据与服务器数据之间充分一致。HTTP 将这些简单的机制称为文档过期(document expiration)和服务器再验证(server revalidation)。

新鲜度和缓存控制首部

  服务器应当告知客户端能够将内容缓存多长时间,在这个时间之内就是新鲜的,文档还未过期。 服务器可以用这两个首部之一来提供这种信息:Expires(过期)和 Cache-Control(缓存控制)。
  下表列出了一些有关Cache-Control首部的一些参数:

首部 类型 描述
no-cache 请求 在重新向服务器验证之前,不要返回文档的缓存副本
no-store 请求 不要返回文档的缓存副本。不要保存服务器的响应
max-age 请求 缓存中的文档不能超过指定的使用期
max-stale 请求 文档允许过期(根据服务器提供的过期信息计算),但不能超过 指令中指定的过期值
min-fresh 请求 文档的使用期不能小于这个指定的时间与它的当前存活时间之 和。换句话说,响应必须至少在指定的这段时间之内保持新鲜
no-transform 请求 文档在发送之前不允许被转换
only-if-cached 请求 只有当文档在缓存中才发送,不要联系原始服务器
public 响应 响应可以被任何服务器缓存
private 响应 响应可以被缓存,但只能被单个客户端访问
no-cache 响应 如果该指令伴随一个首部列表的话,那么内容可以被缓存并提 供给客户端,但必须先删除所列出的首部。如果没有指定首部, 缓存中的副本在没有重新向服务器验证之前不能提供给客户端
no-store 响应 响应不允许被缓存
no-transform 响应 响应在提供给客户端之前不能做任何形式的修改
must-revalidate 响应 响应在提供给客户端之前必须重新向服务器验证
proxy-revalidate 响应 共享的缓存在提供给客户端之前必须重新向原始服务器验证。 私有的缓存可以忽略这条指令
max-age 响应 指定文档可以被缓存的时间以及新鲜度的最长时间
s-max-age 响应 指定文档作为共享缓存时的最长使用时间(如果有 max-age 指 令的话,以本指令为准)。私有的缓存可以忽略本指令
此外还有Etag首部和Expires首部,Expires首部指定的是实际的过期日期而不是秒数。

有条件的请求和验证码

  当文档在客户端“过期”之后(也就是说,客户端不再认为该副本有效),客户端必须从服务器请求一份新的副本。不过,如果该文档在服务器上并未发生改变,客户 端也就不需要再接收一次了——继续使用缓存的副本即可。这种特殊的请求,称为有条件的请求(conditional request),要求客户端使用验证(validator)来告知服务器它当前拥有的版本号,并仅当它的当前副本不再有效时才要求发送新的副本。
  每个有条件的请求都通过特定的验证码来发挥作用。验证码是文档实例的一个特殊 属性,用它来测试条件是否为真。从概念上说,你可以把验证码看作文件的序列号、 版本号,或者最后发生改变的日期时间。有条件的首部 If-Modified-Since 测试的是文档实例最后被修改的日期时间,因此 我们说最后被修改的日期时间就是验证码。有条件的首部 If-None-Match 测试的 是文档的 ETag 值,它是与实体相关联的一个特殊的关键字,或者说是版本识别标 记。Last-Modified 和 ETag 是 HTTP 使用的两种主要验证码

请求类型 | 验证码 | 描述
—- | —-
If-Modified-Since | Last-Modified | 如果在前一条响应的Last-Modified 首部中说明的时间之后,资源的版本发生变化,就发送其副本
If-Unmodified-Since | Last-Modified | 仅在前一条响应的Last-Modified首部中说明的时间之后,资源的版本没有变化,才发送其副本
If-Match | ETag | 如果实体的标记与前一次响应首部中的ETag相同,就发送该资源的副本
If-None-Match | ETag | 如果实体的标记与前一次响应首部中的ETag不同,就发送该资源的副本
  HTTP把验证码分为两类:弱验证码(weak validators)和强验证码(strong validators)。弱验证码不一定能唯一标识资源的一个实例,而强验证码必须如此。弱验证码的一个例子是对象的大小字节数。有可能资源的内容改变了,而大小还保持不变,因此假想的字节计数验证码与改变是弱相关的。而资源内容的加密校验和(比如 MD5)就是强验证码,当文档改变时它总是会改变
  最后修改时间被当作弱验证码,因为尽管它说明了资源最后被修改的时间,但它的 描述精度最大就是1秒。因为资源在1秒内可以改变很多次,而且服务器每秒可以处理数千个请求,最后修改日期时间并不总能反应变化情况。ETag首部被当作强验 证码,因为每当资源内容改变时,服务器都可以在ETag首部放置不同的值。版本号和摘要校验和也是很好的ETag首部候选,但它们不能带有任意的文本。ETag首部很灵活,它可以带上任意的文本值(以标记的形式),这样就可以用来设计出各种 各样的客户端和服务器验证策略。服务器可以在标记前面加上“W/”前缀来广播一个“弱”实体标记。对于弱实体标记来说,只有当关联的实体在语义上发生了重大改变时,标记才会变化。而强实体标记则不管关联的实体发生了什么性质的变化,标记都一定会改变。

参考资料

  《http权威指南》