《HTTP权威指南》读书笔记
第1章 HTTP概述
1.1 HTTP因特网的多媒体信使
HTTP使用的是可靠的数据传输协议。
1.2 Web客户端和服务器
Web内容都是存储在Web服务器上的。Web服务器所使用的是HTTP协议,故常被称为HTTP服务器。HTTP服务器存储了因特网中的数据,如果HTTP客户端发送请求的话,它们会提供数据。HTTP客户端向服务端发送HTTP请求,服务器会在HTTP响应中回送所请求的数据。
1.3 资源
Web服务器是Web资源的宿主,Web资源是Web内容的源头。最简单的Web资源就是Web服务器文件系统中的静态文件。这些文件可以包含任意内容:文本文件,HTML文件,word文件,JPEG图片文件,AVI电影文件等
媒体类型
MIME(Multipurpose Internet Mail Extension,多用途因特网邮件扩展)
MIME类型是一种文本标记,表示一种主要的对象类型和一个特定的子类型
HTML:text/html
ASCII文本:text/plain
JPEG图片:image/jpeg
GIF图片:image.gif
QuickTime电影:video/quicktime
PowerPoint演示文件:application/vnd.ms-powerpoint
URI
统一资源标识符(Uniform Resource Identifier,URI)
URL
统一资源定位符(Uniform Resource Location,URL)
URL遵循一种标准格式:
- URL的第一部分被称为方案(schema),说明访问资源所使用的协议类型。这部分通常就是HTTP协议(http://)
- 第二部分给出了服务器的因特网网站(www.baidu.com)
- 其余部分指定了Web服务器上的某个资源(/path/to/file.html)
URN
统一资源名(Uniform Resource Name,URN)。URN作为特定内容的唯一名称使用的,与目前的资源所在地无关。使用这些与位置无关的URN,就可以将资源四处搬移,通过URN,还可以用同一个名字通过多种网络访问协议来访问资源。
1.4 事务
一个HTTP事务由一条(从客户端发往服务器的)请求命令和一个(服务器发回客户端的)响应结果组成。这种通信是通过名为HTTP报文(HTTP message)的格式化数据块进行的。
方法
HTTP支持几种不同的请求命令,这命令被称为HTTP方法。每条HTTP请求报文都包含一个方法。这个方法会告诉服务器要执行什么动作(获取一个Web页面、运行一个网关程序、删除一个文件等)
HTTP方法 | 描述 |
---|---|
GET | 从服务器向客户端发送命名资源 |
PUT | 将来自客户端的数据存储到一个命名的服务器资源中去 |
DELETE | 从服务器中删除命名资源 |
POST | 将客户端数据发送到一个服务器网关应用程序 |
HEAD | 仅发送命名资源响应中的HTTP首部 |
状态码
每条HTTP响应报文返回时都会携带一个状态码。状态码是一个三位数字的代码,告知客户端请求是否成功,或者是否需要采取其他动作。
HTTP状态码 | 描述 |
---|---|
200 | OK,文档正确返回 |
302 | Redirect(重定向),到其他地方获取资源 |
404 | Not Found(没找到),无法找到这个资源 |
401 | Unauthorized(未授权),需要输入用户名和密码 |
Web页面中可以包含多个对象
应用程序完成一向任务时通常会发布多个HTTP事务。浏览器会执行一个事务来获取描述页面布局的HTML“框架”,然后发布另外的HTTP事务来获取每个嵌入式图片、图像面板、Java小程序等。
1.5 报文
HTTP报文是有一行一行的简单字符串组成的。HTTP报文都是纯文本,不是二进制代码。
从Web客户端发往Web服务器的HTTP报文成为请求报文(request message)
从服务器发往客户端的报文成为响应报文(response message)
HTTP报文包括三部分
- 起始行:在请求报文中说明要做些什么,在响应报文中说明出现了说明情况
- 首部字段:每个首部字段都包含一个名字和一个值,为了便于解析,两者之间用冒号(:)来分割。
- 主体:可选主体,请求主体包括了要发送给Web服务器的数据;响应主体中装载了要返回给客户端的数据。起始行和首部都是文本形式且都是结构化的,而主体则不同
1.6 连接
TCP/IP
HTTP是个应用层协议,HTTP无需操心网络通信的具体细节;它吧联网的细节都交给了通用、可靠的因特网传输协议TCP/IP
TCP提供了:
- 无差错的数据传输
- 按序传输(数据总是会按照发送的顺序到达)
- 未分段的数据流(可以在任意时刻以任意尺寸将数据发送出去)
因特网自身就是基于TCP/IP的,TCP/IP是全世界的计算机和网络设备常用的层次化分组交换网络协议集。TCP/IP隐藏了各种网络和硬件的特点及弱点,使各种类型的计算机好网络都能够进行可靠的通信。
HTTP协议位于TCP的上层。HTTP使用TCP来传输其报文数据。
HTTP网络协议栈 | |
---|---|
HTTP | 应用层 |
TCP | 传输层 |
IP | 网络层 |
网络特有的链路接口 | 数据链路层 |
物理网络硬件 | 物理层 |
连接、IP地址及端口号
在HTTP客户端向服务器发送报文之前,需要网际协议(Internet Protocol,IP)地址和端口号在客户端和服务器之间建立一条TCP/IP连接
浏览器通过HTTP显示位于远端服务器中的某个简单HTML资源:
(1)浏览器从URL中解析出服务器的主机名
(2)浏览器将服务器的主机名转换成服务器的IP地址
(3)浏览器将端口从URL中解析出来
(4)浏览器建立一条与Web服务器的TCP连接
(5)浏览器向服务器发送一条HTTP请求报文
(6)服务器向浏览器回送一条HTTP响应报文
(7)关闭连接,浏览器显示文档
使用Telnet实例
Telnet程序可以将键盘连接到某个目标TCP端口,并将此TCP端口的输入回送到显示屏。Telnet常用于远程终端会话,但他几乎可以连接所有的TCP服务器,包括HTTP服务器。
可以通过Telnet程序直接与Web服务器进行对话。通过Telnet可以打开一条到某台机器上某个端口的TCP连接,然后直接向那个端口输入一些字符。Web服务器会将Telnet程序作为一个Web客户端来处理,所有回送给TCP连接的数据都会显示在屏幕上。
1.7协议版本
HTTP/0.9
HTTP/1.0
HTTP/1.0+
HTTP/1.1
HTTP-NG(HTTP/2.0)
1.8 Web的结构组件
- 代理:位于客户端和服务器之间HTTP中间实体
- 缓存:HTTP的仓库,使常用页面的副本可以保存在离客户端更近的地方
- 网关:连接其他应用程序的特殊Web服务器
- 隧道:对HTTP通信报文进行盲转发的特殊代理
- Agent代理:发起自动HTTP请求的半智能Web客户端
代理
代理位于客户端和服务器之间,接收所有客户端的HTTP请求,并将这些请求转发给服务器(可能会对请求进行修改之后转发)。出于安全考虑,通常会将代理作为转发所有Web流量的可信任中间节点使用。代理还可以对请求和响应进行过滤。
缓存
Web缓存(Web cache)或代理缓存(proxy cache)是一种特殊的HTTP代理服务器,可以将经过代理传送的常用文档复制保存起来。客户端从附近的缓存下载文档比远程web服务器下载快得多。
网关
网关(gateway)是一种特殊的服务器,作为其他服务器的中间实体使用。通常用于将HTTP流量转换成其他的协议。
隧道
隧道(tunnel)是建立起来之后,就会在两条连接之间对原始数据进行盲转发的HTTP应用程序。HTTP隧道通常用来在一条或多条HTTP连接上转发非HTTP数据,转发时不会窥探数据。
HTTP隧道的一种常见用途是通过HTTP连接承载加密的安全套接字层(SSL,Secure Scokets Layer)流量,这样SSL流量就可以穿过只允许Web流量通过的防火墙。HTTP/SSL隧道收到一条HTTP请求,要求建立一条到目的地址和端口的输出连接,然后在HTTP信道上通过隧道传输加密的SSL流量,这样就可以将其盲转发到目的服务器上去了。
Agent代理
用户Agent代理是代表用户发起HTTP请求的客户端程序。所有发布Web请求的应用程序都是HTTP Agent代理。
第2章 URL与资源
2.1 浏览因特网资源
URL是人们对HTTP和其他协议的常用访问的:一个人将浏览器只想一个URL,浏览器就会在幕后发送适当的协议报文来获取人们所期望的资源。
URI由两个主要的子集URL和URN构成,URL是通过描述资源的位置来标识资源的,而URN则是通过名字来识别资源的,与他们当前所处位置无关。
URL分为以下三部分:
- URL的第一部分(http)是URL方案(schema)。方案可以告知Web客户端怎样访问资源。
- URL的第二部分(www.baidu.com)指的是服务器位置,告知Web客户端资源位于何处
- URL的第三部分(path/to/index.html)是资源路径。路径说明了请求的是服务器上哪个特定的本地资源。
URL可以通过HTTP之外的其他协议来访问资源,它们可以指向因特网上的任意资源。
2.2 URL的语法
<schema>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
组件 | 描述 | 默认值 |
---|---|---|
方案 | 访问服务器以获取资源时要使用哪种协议 | 无 |
用户 | 某些方案访问资源时需要的用户名 | 匿名 |
密码 | 用户后面可能包含密码,中间由冒号(:)分隔 | |
主机 | 资源宿主服务器主机名或点分IP地址 | 无 |
端口 | 资源宿主服务器正在监听的端口号,很多方案都有默认端口号(HTTP的默认端口80) | 每个方案特有 |
路径 | 服务器上资源的本地名,由一个斜杆将其与前面的URL组件分隔开来。路径组件的语法是服务器和方案有关的 | 无 |
参数 | 某些方案会用这个组件来指定输入参数。参数为名/值对。URL中可以包含多个参数字段,它们相互之间以及与路径的其余部分之间用分号(:)分隔 | 无 |
查询 | 某些方案会用这个组件传递参数以激活应用程序。查询组件的内容没有通用格式。用字符?将其与URL的其余部分分隔开来。 | 无 |
片段 | 一小片或一部分资源的名字。引用对象时,不会讲frag字段传送给服务器,这个字段是在客户端内部使用的。通过字符#将其与URL的其余部分分隔分开来。 | 无 |
2.3 URL快捷方式
绝对和相对的URL
相对URL
要从相对URL中获取访问资源所需的全部信息就必须相对于另一个,被称为其基础的URL进行解析。
(1)基础URL
- 在资源中显示提供
- 封装资源的基础URL
- 没有基础的URL
(2)解析相对引用
自动扩展URL
主机名扩展,历史扩展
2.4 各种令人头疼的字符
字符 | 保留/受限 | |
---|---|---|
% | 保留作为编码字符的转义标志 | |
/ | 保留作为路径组件中分隔路径段的定界符 | |
. | 保留在路径组件中使用 | |
.. | 保留在路径组件中使用 | |
# | 保留作为分段定界符使用 | |
? | 保留作为查询字符串定界符使用 | |
; | 保留作为参数定界符使用 | |
: | 保留作为方案、用户/口令,以及主机/端口组件的定界符使用 | |
$,+ | 保留 | |
@&= | 在某些方案的上下文中有特殊的含义,保留 | |
{}\ | \^~[]` | 由于各种传输Agent代理,比如各种网关的不安全处理,使用受限 |
<>" | 不安全,这些字符在URL范围之外通常是有意义的,比如在文档中对URL自身进行丁姐,所以应该对其进行编码。 | |
0x00-0x1F,0x7F | 受限,这些十六进制范围内的字符都在US-ASCII字符集的不可打印区间内 | |
>0x7F | 受限,十六进制值在此范围内的字符都不在US-ASCII字符集的7二进制范围内 |
2.5 方案的世界
方案 | 描述 |
---|---|
http | http://:/?#默认端口80 |
https | https://:/?#默认端口443,使用了网景的SSL,为HTTP连接提供了端到端的加密机制 |
mailto | mailto: |
ftp | ftp://:@:/; |
rtsp,rtspu | rtsp://:@:/ rtspu://:@:/rtspu使用UDP协议来获取资源 |
file | file:/// |
news | news: news: |
telnet | telnet://:@:/ |
第3章 HTTP报文
3.1 报文流
报文流入源端服务器
报文向下游流动
3.2 报文的组成部分
起始行,首部,主体
报文的语法
请求报文的格式:
响应报文的格式:
方法(method),请求URL(request-URL),版本(version),状态码(status-code),原因短语(reason-phrase),首部(header),实体的主体部分(entity-body)
起始行
请求行,响应行,方法,状态码,原因短语,版本号
常用的HTTP方法
方法 | 描述 | 是否包含主体 |
---|---|---|
GET | 从服务器获取一份文档 | 否 |
HEAD | 只从服务器获取文档的首部 | 否 |
POST | 向服务器发送需要处理的数据 | 是 |
PUT | 将请求的主体部分存储在服务器上 | 是 |
TRACE | 对可能经过代理服务器传送到服务器上去的报文进行追踪 | 否 |
OPTIONS | 决定可以在服务器上执行哪些方法 | 否 |
DELETE | 从服务器上删除一份文档 | 否 |
状态码分类
整体范围 | 已定义范围 | 分类 |
---|---|---|
100~199 | 100~101 | 信息提示 |
200~299 | 200~206 | 成功 |
300~399 | 300~305 | 重定向 |
400~499 | 400~415 | 客户端错误 |
500~599 | 500~505 | 服务器错误 |
首部
(1)首部分类
- 通用首部:既可以出现在请求报文中,也可以出现在响应报文中
- 请求首部:提供更多有关请求的信息
- 响应首部:提供更多有关响应的信息
- 实体首部:描述主体的长度和内容,或者资源自身
- 扩展首部:规范中没有定义的新首部
首部实例 | 描述 |
---|---|
Date:Tue,3 Qct 1997 02:16:03 GMT | 服务器产生响应的日期 |
Content-length:15040 | 实体的主体部分包含了15040字节的数据 |
Content-type:image/gif | 实体的主体部分是GIF图片 |
Accept:image/gif,image/jpeg,text/html | 客户端可以接收GIF图片和JPEG图片以及HTML |
(2)首部延续行
实体的主体部分
3.3 方法
GET方法和HEAD方法都被认为是安全的
GET
通常用于请求服务器发送某个资源
HEAD
服务器再响应中只返回首部,不返回实体的主体部分,这允许客户端在未获取实际资源的情况下,对资源的首部进行检查。
使用HEAD,可以:
- 在不获取资源的情况下了解资源的情况
- 通过查看响应中的状态码,看看某个对象是否存在
- 通过查看首部,测试资源是否被修改了
服务器开发者必须确保返回的首部与GET请求所返回的首部完全相同。
PUT
PUT方法会向服务器写入文档。让服务器用请求的主体部分来创建一个由所请求的URL命名的新文档,如果URL已存在,就用这个主体替代它。PUT允许用户对内容进行修改。
POST用于向服务器发送数据,PUT用于向服务器中的资源中存储数据。
POST
POST方法起初是用来向服务器输入数据的,实际上,通常会用它来支持HTML的表单。表单中填好的数据通常会被送给服务器,然后由服务器将其发送到它要去的地方。
TRACE
TRACE方法运行客户端在最终将请求发送给服务器时,看看它变成了什么样子
TRACE请求会在目的服务器端发起一个环回诊断。行程最后一站的服务器会弹回一条TRACE响应,并在响应主体中携带它收到的原始请求报文,这样客户端就可以查看在所有中间HTTP应用程序组成的请求/响应链上,原始报文是否,以及如何被毁坏或修改过。
TRACE请求中不能带有实体的主体部分。TRACE响应的实体主体部分包含了响应服务器收到的请求的精确副本。
OPTIONS
OPTIONS方法请求Web服务器告知其支持的各种功能,可以询问服务器通常支持哪些方法,或者对某些特殊资源支持哪些方法。
DELETE
DELETE方法所做的事情就是请求服务器删除请求URL所指定的资源。客户端应用程序无法保证删除操作一定会被执行。HTTP规范运行服务器再不通知客户端的情况下撤销请求。
扩展方法
方法 | 描述 |
---|---|
LOCK | 允许用户锁定资源,比如可以在编辑某个资源的时候将其锁定,以防别同时对其进行修改 |
MKCOL | 运行用户创建资源 |
COPY | 便于在服务器上复制资源 |
MOVE | 在服务器上移动资源 |
3.4 状态码
100~199信息性状态码
状态码 | 原因短语 | 含义 |
---|---|---|
100 | Continue | 说明收到了请求的初始部分,请客户端继续。发送了这个状态码之后,服务器再收到请求之后必须进行响应。 |
101 | Switching Protocols | 说明服务器正在根据客户端的指定,将协议切换成Update首部所列的协议 |
200~299成功状态码
状态码 | 原因短语 | 含义 |
---|---|---|
200 | OK | 请求没问题,实体的主体部分包含所请求的资源 |
201 | Created | 用于创建服务器对象的请求(如PUT)响应的实体主体部分中应该包含各种引用了已创建的资源的URL,Location首部包含的则是最具体的引用。服务器必须在发送这个状态码之前创建好对象。 |
202 | Accepted | 请求已被接受,但服务器还未对其执行任何动作。不能保证服务器会完成这个请求,这只是一味着接受请求时,他看起来有效的。服务器应该在实体的主体部分包含对请求状态的描述,或许还应该有对请求完成时间的估计(或者包含一个指针,指向可以获取此信息的位置) |
203 | Non-Authoritative Information | 实体首部包含的信息不是来自于源端服务器,而是来自资源的一份副本。如果中间节点上有一份资源副本,但无法或者没有对它所发送的与资源有段的元信息(首部)进行验证,就会出现这种情况。 |
204 | No Content | 响应报文中包含若干首部和一个状态行,但没有实体的主体部分。主要用于在浏览器不转为显示新文档的情况下,对其进行更新。 |
205 | Reset Content | 另一个主要用于浏览器代码,负责告知浏览器清除当前页面中的所有HTML表单元素 |
206 | Partial Content | 成功执行了一个部分或Range(范围)请求。客户端可以通过一些特殊首部来获取部分或某个范围内的文档,这个状态码说明范围请求成功。206响应必须包含Content-Range、Date以及ETag或Content-Location首部 |
300~399重定向状态码
状态码 | 原因短语 | 含义 |
---|---|---|
300 | Multiple Choices | 客户端请求一个实际指向多个资源的URL时返回这个状态码,比如服务器上有某个HTML文档的英语或法语版本。返回这个代码时会带有一个选项列表,这样用户就可以选择希望使用的一项,有多个版本可用时,客户需要沟通解决。服务器可以在Location首部包含首选URL |
301 | Moved Permanently | 在请求的URL已被移除时使用,响应的Location首部中应该包含资源限制所处的URL |
302 | Found | 与301类似,客户端应该使用Location首部给出的URL来临时定位资源,将来的请求仍应使用老URL |
303 | See Other | 告知客户端应该用另一个URL来获取资源。新的URL位于响应报文的Location首部,其主要目的是允许POST请求的响应将客户端定向到某个资源上去 |
304 | Not Modified | 客户端可以通过所包含的请求首部,使其请求变成有条件的。若客户端发起一个条件GET请求,而最近资源未被修改的话,就可以用这个状态码来说明资源未被修改,带有这个状态码的响应不应该包含实体的主体部分。 |
305 | Use Proxy | 用来说明必须通过一个代理来访问资源。代理的位置由Location首部给出。客户端是相对某个特定资源来解析这条响应的,不能假定所有请求,甚至所有对持有所请求资源的服务器的请求都通过这个代理进行。如果客户端错误地让代理接入了某条请求,可能会引发破坏性的行为,而且造成安全漏洞。 |
306 | (未使用) | |
307 | Temporary Redirect | 与301相似,客户端应该使用Location首部给出的URL来临时定位资源,将来的请求仍应使用老URL |
400~499客户端错误状态码
状态码 | 原因短语 | 含义 |
---|---|---|
400 | Bad Request | 用于告知客户端它发送一个错误的请求 |
401 | Unauthorized | 与适当的首部一同返回,在这个首部中请求客户端在获取对资源的访问权之前,对自己进行认证。 |
402 | Payment Required | 未使用 |
403 | Forbidden | 用于说明请求被服务器拒绝了。如果服务器向说明为什么拒绝请求,可以包含实体的主体部分来对原因进行描述。这状态码通常在服务器不想说明拒绝原因的时候使用的。 |
404 | Not Found | 用于说明服务器无法找到所请求的URL。通常会包含一个实体,以便客户端应用程序显示给用户看。 |
405 | Method Not Allowed | 发起的请求中带有所请求的URL不支持的方法时,使用此状态码。应该在响应中包含Allow首部,以告知客户端对所请求的资源可以使用哪些方法。 |
406 | Not Acceptable | 客户端可以指定参数来说明他们愿意接收说明类型的实体。服务器没有雨客户端可接受的URL相匹配的资源时,使用此代码。通常,服务器会包含一些首部,以便客户端弄清楚为什么请求无法满足。 |
407 | Proxy Authentication Required | 与401相似,但用于要求对资源进行认证的代理服务器 |
408 | Request Timeout | 如果客户端完成请求所花的时间太长,服务器可以回送此状态码,并关闭连接。超时时长随服务器的不同有所不同,但通常对所有的合法请求来说,都是够长的。 |
409 | Conflict | 用于说明请求可能在资源上引发的一些冲突,服务器担心请求会引发冲突时,可以发送此状态码。响应中应该包含描述冲突的主体。 |
410 | Gone | 与404相似,只是服务器曾经拥有过此资源,主要用于Web站点的维护,这样的服务器管理者就可以在资源移除的情况下通知客户端 |
411 | Length Required | 服务器要求在请求报文中包含Content-Length首部时使用。 |
412 | Precondition Failed | 客户端发起了条件请求,且其中一个条件失败了的时候使用。 |
413 | Request Entity Too Large | 客户端发起了条件请求,且其中一个条件失败了的时候使用。客户端包含了Except首部时发起的就是条件请求。 |
414 | Request URI Too Long | 客户端发送的实体主体部分比服务器能够或者希望处理的要大时,使用此状态码 |
415 | Unsupported Media Type | 服务器无法理解或无法支持客户端所发实体的内容类型时,使用此状态码 |
416 | Requested Range Not Statisfiable | 请求报文所请求的是指定资源的某个范围,而且范围无效或无法满足时,使用此状态码 |
417 | Expectation Failed | 请求的Expect请求首部包含了一个期望,但服务器无法满足此期望时,使用此状态码。如果代理或其他中间应用程序有确切证据说明源端服务器会为某请求产生一个失败期望,就可以发送此响应状态码。 |
500~599服务器错误状态码
状态码 | 原因短语 | 含义 |
---|---|---|
500 | Internal Server Error | 服务器遇到一个妨碍他请求提供服务的错误时,使用此状态码 |
501 | Not Implemented | 客户端发起的请求超出服务器的能力范围(如使用服务器不支持的请求方法)时,使用此状态码 |
502 | Bad Gateway | 作为代理或者网关使用的服务器从请求响应链的下一条链路上收到了一条伪响应(如他无法连接到其父网关)时,使用此状态码 |
503 | Service Unavailable | 用来说明服务器现在无法为请求提供服务名,但将来可以。如果服务器知道什么时候资源会变为可用的,可以在响应中包含一个Retry-After首部。 |
504 | Gateway Timeout | 与408相似,只是这里的响应来自一个网关或代理,他们在等待另一服务器对其请求进行响应时超时了 |
505 | HTTP Version Not Support | f缶收到的请求是用来他无法或者不愿支持的协议版本时,使用此状态码,有些服务器应用程序会悬着不支持协议的早期版本。 |
3.5 首部
通用信息性首部
首部 | 描述 |
---|---|
Connection | 允许客户端和服务器指定与请求/响应连接有关的选项 |
Date | 提供日期和时间标志,说明报文是什么时间创建的 |
MIME-Version | 给出了发送端使用的MIME版本 |
Trailer | 如果报文采用了分块传输编码(chunked transfer encoding)方式,就可以用这个首部列出位于报文拖挂(trailer)部分的首部集合 |
Transfer-Encoding | 告知接受端围栏保证报文的可靠传输,对报文采用了什么编码方式 |
Update | 给发送端可能想要“升级”使用新版本或协议 |
Via | x显示了报文经过的中间节点(代理、网关) |
通用缓存首部
首部 | 描述 |
---|---|
Cache-Control | 用于随报文传送缓存z指示 |
Pragma | 另一种随报文传送指示的方式,但并不专用于缓存 |
请求信息性首部
首部 | 描述 |
---|---|
Client-IP | 提供了运行客户端的机器的IP地址 |
From | 提供了客户端用户端E-mail地址 |
Host | 给出了接收请求的服务器的主机名和端口号 |
Referer | 提供了包含当前请求URI的文档的URL |
UA-Color | 提供了与客户端显示器的显示颜色有关的信息 |
UA-CPU | 给出了客户端CPU的类型或制造商 |
UA-Disp | 给出了客户端显示器(屏幕)能力有关的信息 |
UA-OS | OS给出了运行在客户端机器上的操作系统名称及版本 |
UA-Pixels | 提供了客户端显示器的像素信息 |
User-Agent | 将发起请求的应用程序名称告知服务器 |
Accept首部
首部 | 描述 |
---|---|
Accept | 告诉服务器能够发送哪些媒体类型 |
Accept-Charset | 告诉服务器能够发送哪些字符集 |
Accept-Encoding | 告诉服务器能够发送哪些编码方式 |
Accept-Language | 告诉服务器能够发送哪些语言 |
TE | 告诉服务器可以使用哪些扩展传输编码 |
条件请求首部
首部 | 描述 |
---|---|
Expect | 允许客户端列出某请求所要求的服务器行为 |
If-Match | 如果实体标记与与文档当前的实体标记相匹配,就获取这份文档 |
If-Modified-Since | 除非在某个指定的日期之后资源被修改过,否则就限制这个请求 |
If-None-Match | 如果提供的实体标记与当前文档的实体标记不相符,就获取文档 |
If-Range | 允许对文档的某个范围进行条件请求 |
If-Unmodified-Since | 除非在某个指定日期之后资源没有被修改过,否则就限制这个请求 |
Range | 如果服务器支持范围请求,就请求资源的指定范围 |
安全请求首部
首部 | 描述 |
---|---|
Authorization | 包含客户端提供给服务器,以便其自身进行认证的数据 |
Cookie | 客户端用它向服务器传送一个令牌——它并不是真正的安全首部,但确定隐含了安全功能 |
Cookie2 | 用来说明请求段支持的cookie版本 |
代理请求首部
首部 | 描述 |
---|---|
Max-Forward | 在通往源端服务器的路径上,将请求转发给其他代理或网关的最大次数——与TRACE方法一同使用 |
Proxy-Authorization | 与Authorization首部相同,这个首部是在与代理进行认证时使用的 |
Proxy-Connection | 与Connection首部相同,但这个首部是在与代理建立连接时使用的。 |
响应信息性首部
首部 | 描述 |
---|---|
Age | (从最初创建开始)响应持续时间 |
Public | 服务器为其资源支持的请求方法列表 |
Retry-After | 如果资源不可用的话,在此日期或时间重试 |
Server | 服务器应用程序软件的名称和版本 |
Title | 对HTML文档来说,就是HTML文档的源端给出的标题 |
Warning | 比原因短语中更详细一些的警告报文 |
协议首部
首部 | 描述 |
---|---|
Accept-Range | 对此资源来说,服务器可接受的范围类型 |
Vary | 服务器查看的其他首部的列表,可能会使响应发生变化。这是一个首部列表,服务器会根据这些首部的内容挑选出最合适的资源版本发送给客户端。 |
安全响应首部
首部 | 描述 |
---|---|
Proxy-Authenticate | 来自代理的对客户端的质询列表 |
Set-Cookie | 不是真正的安全首部,但隐含有安全功能,可以在客户端设置一个令牌,以便服务器对客户端进行标识 |
Set-Cookie2 | 与Set-Cookie类似,RFC2965定义 |
WWW-Authenticate | 来自服务器的对客户端的质询列表 |
实体信息性首部
首部 | 描述 |
---|---|
Allow | 列出了可以对此实体执行的请求方法 |
Location | 告知客户端实体实际上位于何处,用于将接收端定向到资源的(可能是新的)位置(URL)上去 |
内容首部
首部 | 描述 |
---|---|
Content-Base | 解析主体中的相对URL时使用的基础URL |
Content-Encoding | 对主体执行的任意编码方式 |
Content-Language | 理解主体时最适宜使用的自然语言 |
Content-Length | 主体的长度或尺寸 |
Content-Location | 资源实际所处的位置 |
Content-MD5 | 主体的MD5校验和 |
Content-Range | 在整个资源中此实体表示的字节范围 |
Content-Type | 这个主体的对象类型 |
实体缓存首部
首部 | 描述 |
---|---|
ETag | 与此实体相关的实体标记 |
Expires | 实体不再有效,要从原始的源端再次获取此实体的日期和时间 |
Last-Modified | 这个实体最后一次呗修改的日期和时间 |
第4章 连接管理
4.1 TCP连接
TCP的可靠数据管道
TCP流是分段的、由IP分组传送
HTTP | HTTPS | ||
---|---|---|---|
HTTP | 应用层 | HTTP | 应用层 |
TCP | 传输层 | TSL or SSL | 安全层 |
IP | 网络层 | TCP | 传输层 |
网络接口 | 数据链路层 | IP | 网络层 |
网络接口 | 数据链路层 |
每个TCP段都是由IP分组承载,从一个IP地址发送到另一个IP地址的。每个IP分组都包括:
一个IP分组首部(通常为20字节)
一个TCP段首部(通常为20字节)
一个TCP数据块(0个或多个字节)
保持TCP连接的正确运行
TCP连接是通过4个值来识别的:
<源IP地址、源端口号、目的IP地址、目的端口号>
IP分组=>TCP段=>TCP数据流中的数据块
IP分组:
版本 | 首部长度(以字节为单位) | 服务类型(TOS) | 数据报总长(以字节为单位) |
---|---|---|---|
分组ID(16位的数字) | 标记 | 段偏移量 | 生存时间(TTL) |
高层协议 | 首部校验和 | 源IP地址 | 目的IP地址 |
TCP段:
源端口 | 目的端口 | TCP段序号 | 捎带的确认 |
---|---|---|---|
首部长度(以字为单位) | 保留 | URG | ACK |
PSH | RST | SYN | FIN |
窗口尺寸 | TCP校验和 | 紧急指针 |
TCP数据流中的数据块:
GET /index.html HTTP/1.1
Host:www.baidu.com
用TCP套接字编程
套接字API调用 | 描述 |
---|---|
s=socket() | 创建一个新的、未命名、未关联的套接字 |
bind(s,) | 向套接字赋一个本地端口和接口 |
connect(s,) | 创建一条连接本地套接字与远程主机及端口的连接 |
listen(s,...) | 标识一个本地套接字,使其可以合法接受连接 |
s2=accept(s) | 等待某人建立一条到本地端口的连接 |
n=read(s,buffer,n) | 尝试从套接字向缓冲区读取n个字节 |
n=write(s,buffer,n) | 尝试从缓冲区中向套接字写入n个字节 |
close(s) | 完全关闭TCP连接 |
shutdown(s,) | 只关闭TCP连接的输入或输出端 |
getsockopt(s,...) | 读取某个内部套接字配置选项的值 |
setsockopt(s,...) | 修改某个内部套接字配置选项的值 |
TCP客户端和服务器是如何通过TCP套接字接口进行通信的
客户端 | 服务器 |
---|---|
1.创建新的套接字(socket) 2.将套接字绑定到端口80上去(bind) 3.允许套接字进行连接(listen) 4.等待连接(accept) | |
1.获取IP地址口号 2.创建新的套接字(scoket) 3.连接到服务器IP:port上去(connect) | |
5. 通知应用程序有连接到来 6.开始读取请求(read) | |
4.连接成功 5.发送HTTP请求(write) 6.等待HTTP响应(read) | |
7.处理HTTP请求报文 8.回送HTTP响应(write) 9.关闭连接(close) | |
7.处理HTTP响应 8.关闭连接(close) |
4.2 对TCP性能的考虑
HTTP事务的时延
(1)客户端首先需要根据URI确定Web服务器的IP地址和端口号。如果最近没有对URI中的主机名进行访问,通过DNS解析系统将URI中的主机名转换成一个IP地址可能要话费数十秒的时间
(2)客户端会向服务器发送一条TCP连接请求,并等待服务器回送一个请求接受应答。每条新的TCP连接都会有连接建立时延。这个值通常最多只有一两秒,但如果有数百个HTTP事务的话,这个值会快速地叠加上去。
(3)一旦连接建立起来了,客户端就会通过新建立的TCP管道来发送HTTP请求。数据到达时,Web服务器会从TCP连接中读取请求报文,并对请求进行处理。因特网传输请求报文,以及服务器处理请求报文都需要时间。
(4)然后,Web服务器会回送HTTP响应,这也需要花费时间。
性能聚焦区域
- TCP连接建立握手
- TCP慢启动拥塞控制
- 数据聚集的Nagle算法
- 用于捎带确认的TCP延迟确认算法
- TIME_WAIT时延和端口耗尽
TCP连接的握手时延
(1)请求新的TCP连接时,客户端向服务器发送一个小的TCP分组(通常是40~60个字节)这个分组中设置了一个特殊的SYN标记,说明这是一个连接请求。
(2)如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP分组,这个分组中的SYN和ACK标记都被置位,说明连接请求已被接受。
(3)最后,客户端向服务器回送一条确认信息,通知它连接已成功建立。现代的TCP栈都允许客户端在这个确认分组中发送数据。
最后结果是,小的HTTP事务可能会在TCP建立上花费50%,或更多时间。
延迟确认
每个TCP段都有一个序列号和数据完整性校验和,每个段的接收者收到完好的段时,都会向发送者回送小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或损毁,并重发数据。
延迟确认算法会在一个特定的窗口世家你(通常是100~200毫秒)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段没有输出数据分组,就将确认信息放在单独的分组中传送。
HTTP具有双峰特征的请求-应答行为降低了捎带信息的可能。当希望有相反方向回传分组的时候,偏偏没有那么多。通常,延迟确认算法会引入相当大的时延。根据所使用操作系统的不同,可以调整或禁止延迟确认算法。
TCP慢启动
TCP数据传输的性能还取决于TCP连接的使用期。TCP连接会随着时间进行自我调谐,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为TCP慢启动,用于防止因特网的突然过载和拥塞。
TCP慢启动限制了一个TCP端点在任意时刻可以传输的分组数。每成功接收一个分组,发送端就有了发送另外两个分组的权限。如果某个HTTP事务有大量数据要发送,是不能一次将所有分组都发送出去,必须发送一个分组,等待确认。然后可以发送两个分组,每个分组都必须被确认,这样可以发送四个分组了。以此类推,这个方式被称为打开拥塞窗口
Nagle算法与TCP_NODELAY
Nagle算法师徒在发送一个分组之前,将大量TCP数据绑定在一起,以提高网络效率。Nagle算法鼓励发送全尺寸(LAN上最大尺寸的分组大约1500字节,在因特网上几百字节)的段。只有个当所有其他分组都被确认之后,Nagle算法才允许发送非全尺寸的分组。如果其他分组仍然在传输过程中,就将那部分数据缓存起来。只有当挂起分组被确认,或者缓存中积累了足够发送一个全尺寸分组的数据时,才会将缓存的数据发送出去。
Nagle算法会引发几种HTTP性能问题:
(1)小的HTTP报文可能无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生的延时。
(2)Nagle算法与延迟确认之间的交互存在问题——Nagle算法阻止数据的发送,知道有确认分组抵达为止,但确认分组自身会被延迟确认算法100~200毫秒。
HTTP应用程序常常会在自己的栈中设置参数TCP——NODELAY,禁用Nagle算法,提高性能。
TIME_WAIT积累与端口耗尽
当某个TCP端点关闭TCP连接时,会在内存中维护一个小的控制块,用来记录最近所关闭连接的IP地址和端口号。这类新型智慧维持一小段世家你,通常是所顾忌的最大分段使用期的两倍(2MSL,通常为2分钟)左右,以确保在这段时间内不会创建具有相同地址和端口号的新连接,实际上,这个算法可以防止在两分钟内创建、关闭并重新创建两个具有相同IP地址和端口号的连接。
在只有一个客户端和一台Web服务器的异常情况下,构建一条TCP连接的4个值:
其中3个都是固定的,只有源端口号可以随意改变。
客户端每次连接到服务器上去时,都会获得一个新的源端口,以实现连接的唯一性。但由于可用源端口的数量有限(如60000个),而且在2MSL秒(如120秒)内连接时无法重用的,连接率就被限制在了60000/120=500次/秒。如果在不进行优化,并且服务器的连接率不高于500次每秒,就可确保不会遇到TIME_WAIT端口胡奥就问题。
4.3 HTTP连接的处理
常被误解的Connection首部
Connection首部可以承载3种不同类型的标签:
- HTTP首部字段名,列出了只与此连接有关的首部
- 任意标签值,用于描述此连接的非标准选项
- 值close,说明操作完成之后需关闭这条持久连接
串行事务处理时延
每个事物都需要串行地建立一条新的连接,那么连接时延和慢启动启动时延就会叠加起来
并行连接
通过多条TCP连接发起并发的HTTP请求
持久连接
重用TCP连接,以及消除连接及关闭延时
管道化连接
通过共享的TCP连接发起并发的HTTP请求
复用连接
交替传送请求和响应报文
Keep-Alive操作
实现HTTP/1.0 keep-alive连接的客户端可以通过包含Connection:Keep-Alive首部请求将一条连接保持在打开状态。如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有Connection:Keep-Alive首部,客户端认为服务器不支持keep-alive,会在发回响应报文之后关闭连接。
Keep-Alive选项
- 参数timeout是在Keep-Alive响应首部发送的。它估计了服务器希望将连接保持在活跃的时间。
- 参数max是在Keep-Alive响应首部发送的。它估计了服务器还希望为多少个事务保持此连接的活跃状态。
- Keep-Alive首部还可支持任意未经处理的属性,这些属性主要用于诊断和调试。语法name[=value]
Keep-Alive连接的限制和规则
- 在HTTP/1.0中,keep-alive并不是默认使用的。客户端必须发送一个Connection:Keep-Alive请求首部来激活keep-alive连接。
- Connection:Keep-Alive首部必须随所有希望保持持久连接的报文一起发送。如果客户端没有发送Connection:Keep-Alive首部,服务器就会在那条请求之后关闭连接。
- 客户端探明响应中没有Connection:Keep-Alive响应首部,就可以知道服务器发出响应之后是否关闭连接了。
- 只有在无需检测到连接的关闭即可确认报文实体主体部分长度的情况下,才能将连接保持在打开状态——也说实体的主体部分必须有正确的Content-Length,有多部件媒体类型,或者用分块传输编码的方式进行了编码。在一条keep-alive信道中回送错误的Content-Length是很糟糕的事,事务处理的另一端的无法精确的检测出一条报文的结束和另一条报文的开始了。
- 代理和网关必须执行Connection首部的规则。代理或网关必须在将报文转发出去或其高速缓存之前,删除在Connection首部中命名的所有首部字段以及Connection首部自身。
- 不应该与无法确定是否支持Connection首部的代理服务器建立keep-alive连接,以防止出现下面要介绍的哑代理问题。
- 从技术上来讲,应该忽略所有来自HTTP/1.0设备的Connection首部字段,因为它们可能是由比较老的代理服务器误转发的。实际上,尽可能会有在老代理上挂起的危险,有些客户端和服务器还是会违反这条规则。
Keep-Alive和哑代理
盲中继:它们只是将字节从一个连接转发到另一个连接中去,不对Connection首部进行特殊的处理。
Keep-Alive无法与不支持Connection首部的代理进行互操作:
(1)客户端发送一个首部为Connection:Keep-Alive请求,哑代理直接转发到服务器。
(2)服务器认为对方请求保持连接保持活跃,因此处理完连接后不会将其关闭,返回含Connection:Keep-Alive的结果至哑代理。
(3)哑代理会忽略连接上所有新的请求,等待连接关闭,直接将返回结果转发到客户端。
(4)客户端由于代理不会对其进行处理,所以客户端在keep-alive连接上发送的第二条请求会被挂起。
插入Proxy-Connection
Proxy-Connection首部修正了单个盲中继带来的问题:
服务器不认识Proxy-Connection首部,将其忽略,没有建立keep-alive连接。哑代理转发了Proxy-Connection首部,服务器将其忽略。
代理识别出了ProxyConnection首部,统一与客户端进行keep-alive对话,可能还会决定于服务器建立一条keep-alive连接。充满的代理理解Proxy-Connection首部,并主动向服务器发送了一个Connection:Keep-Alive首部。
对有多层次代理 的情况,Proxy-Connection仍然无法解决问题:哑代理转发到聪明的代理,或聪明的代理转发到哑代理,哑代理无意识地向浏览器和崇明岛代理广播Keep-Alive
持久连接的限制和规则
- 发送了Connection:close请求首部之后,客户端就无法在那条连接上发送更多的请求了。
- 如果客户端不想在连接上发送其他请求了,就应该在最后一条请求中发送一个Connection:close请求首部。
- 只有当连接上所有的请求都有正确的、自定义长度时(实体主体部分的长度都和相应的Content-Length一致,或者是用分块传输编码方式编码的),连接才能持久保持。
- HTTP/1.1代理必须能够分别管理与客户端和服务器持久的连接,每个持久连接都只适用于一跳传输。
- 尽管服务器不应该师徒在传输报文的过程中关闭连接,而且在关闭连接之前至少应该响应一跳请求,但不管Connection首部取了什么值,HTTP/1.1设备都可以在任意时刻关闭连接
- HTTP/1.1应用程序必须能够从一部的关闭中恢复出来。只要不存在可能会累积起来的副作用,客户端都应该重试这条请求。
- 除非重复发起请求会产生副作用,否则如果在客户端收到整条响应之前连接关闭了,客户端就必须要重新发起请求。
- 一个用户客户端对任何服务器或代理最多只能维护两条持久连接,以防服务器过载,代理可能需要更多到服务器连接来支持并发用户的通信。多以,如果有N个用户师徒访问服务器,代理最多要维持2N条到任意服务器或父代理的连接。
4.6管道化连接
- 如果HTTP客户端无法确认连接是持久的,就不应该使用管道。
- 必须按照与请求相同的顺序回送HTTP响应。HTTP报文中没有序列号标签,因此如果收到的响应失序了,就没办法将其与请求匹配起来了。
- HTTP客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完成的管道化请求。如果客户端打开了一条持久连接,并立即发出了10条请求,服务器可能在只处理了,如5条请求之后关闭连接,剩下的5条请求会失败,客户端必须能够应对这些过早关闭连接的情况,重新发出这些请求。
- HTTP客户端不应该用管道化的方式发送回产生副作用的请求。
4.7 关闭连接的奥秘
任意解除连接
所有HTTP客户端、服务器或代理都可以在任意时刻关闭一条TCP传输连接。通常会在一条报文结束时关闭,但出错的时候,也可能在首部行的中间,或其他奇怪的地方关闭连接。HTTP应用程序可以在经过任意一段时间之后,关闭持久连接。
Content-Length及截尾操作
每条HTTP响应都应该有精确的Content-Length首部,用以描述响应主体的尺寸。客户端或代理收到一条随连接关闭而借宿的HTTP响应,且实际传输的实体长度与Content-Length并不匹配(或没有Content-Length)时,接收端就应该质疑长度的正确性。