本文将尽量用通俗易懂的方式来向读者讲述 HTTP 的知识。
另外,建议在学习 HTTP 知识的时候,利用 Chrome 开发者工具来做实践,这可以帮助你理解得更深刻。
(此图在网上找来的,侵删)
HTTP 概述
HTTP 超文本传输协议是位于 TCP/IP 体系结构中的应用层协议,它是万维网数据通信的基础。
当我们访问一个网站时,需要通过统一资源定位符(uniform resource locator,URL)来定位服务器并获取资源。
<协议>://<域名>:<端口>/<路径>
一个 URL 的一般形式通常如上所示(http://test.com/index.html
),现在最常用的协议就是 HTTP,HTTP 的默认端口是 80,通常可以省略。
HTTP/1.1
HTTP/1.1 是目前使用最广泛的版本,一般没有特别标明版本都是指 HTTP/1.1。
HTTP 连接建立过程
我们来看一下在浏览器输入 URL 后获取 HTML 页面的过程。
- 先通过域名系统(Domain Name System,DNS)查询将域名转换为 IP 地址。即将
test.com
转换为221.239.100.30
这一过程。 - 通过三次握手(稍后会讲)建立 TCP 连接。
- 发起 HTTP 请求。
- 目标服务器接收到 HTTP 请求并处理。
- 目标服务器往浏览器发回 HTTP 响应。
- 浏览器解析并渲染页面。
下图中的 RTT 为往返时延(Round-Trip Time: 往返时延。表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延)。
HTTP 连接拆除过程
所有 HTTP 客户端(浏览器)、服务器都可在任意时刻关闭 TCP 连接。通常会在一条报文结束时关闭连接,但出错的时候,也可能在首部行的中间或其他任意位置关闭连接。
TCP 三次握手和四次挥手
由于 HTTP 是基于 TCP 的,所以打算在这补充一下 TCP 连接建立和拆除的过程。
首先,我们需要了解一些 TCP 报文段的字段和标志位:
- 32 比特的序号字段和确认号字段,TCP 字节流每一个字节都按顺序编号。确认号是接收方期望从对方收到的下一字节的序号。
- ACK 标志位,用于指示确认字段中的值是有效的 ACK=1 有效,ACK=0 无效。
- SYN 标志位,用于连接建立,SYN 为 1 时,表明这是一个请求建立连接报文。
- FIN 标志位,用于连接拆除,FIN 为 1 时,表明发送方数据已发送完毕,并要求释放连接。
TCP 三次握手建立连接
TCP 标准规定,ACK 报文段可以携带数据,但不携带数据就不用消耗序号。
- 客户端发送一个不包含应用层数据的 TCP 报文段,首部的 SYN 置为 1,随机选择一个初始序号(一般为 0)放在 TCP 报文段的序号字段中。(SYN 为 1 的时候,不能携带数据,但要消耗掉一个序号)
- TCP 报文段到达服务器主机后,服务器提取报文段,并为该 TCP 连接分配缓存和变量。然后向客户端发送允许连接的 ACK 报文段(不包含应用层数据)。这个报文段的首部包含 4 个信息:ACK 置 为 1,SYN 置为 1;确认号字段置为客户端的序号 + 1;随机选择自己的初始序号(一般为 0)。
- 收到服务器的 TCP 响应报文段后,客户端也要为该 TCP 连接分配缓存和变量,并向服务器发送一个 ACK 报文段。这个报文段将服务器端的序号 + 1 放置在确认号字段中,用来对服务器允许连接的报文段进行响应,因为连接已经建立,所以 SYN 置为 0。最后一个阶段,报文段可以携带客户到服务器的数据。并且以后的每一个报文段,SYN 都置为 0。
下图是一个具体的示例:
(此截图是我使用 Wireshark 抓包工具截取的 TCP 报文段截图)。
TCP 四次挥手拆除连接
FIN 报文段即使不携带数据,也要消耗序号。
- 客户端发送一个 FIN 置为 1 的报文段。
- 服务器回送一个确认报文段。
- 服务器发送 FIN 置为 1 的报文段。
- 客户端回送一个确认报文段。
TCP 为什么是四次挥手,而不是三次?
- 当 A 给 B 发送 FIN 报文时,代表 A 不再发送报文,但仍可以接收报文。
- B 可能还有数据需要发送,因此先发送 ACK 报文,告知 A “我知道你想断开连接的请求了”。这样 A 便不会因为没有收到应答而继续发送断开连接的请求(即 FIN 报文)。
- B 在处理完数据后,就向 A 发送一个 FIN 报文,然后进入 LAST_ACK 阶段(超时等待)。
- A 向 B 发送 ACK 报文,双方都断开连接。
参考资料:
HTTP 报文格式
HTTP 报文由请求行、首部、实体主体组成,它们之间由 CRLF(回车换行符) 分隔开。
注意:实体包括首部(也称为实体首部)和实体主体,sp 即是空格 space。
请求行和首部是由 ASCII 文本组成的,实体主体是可选的,可以为空也可以是任意二进制数据。
请求报文和响应报文的格式基本相同。
请求报文格式:
<method> <request-URL> <version> <headers> <entity-body>
响应报文格式:
<version> <status> <reason-phrase> <headers> <entity-body>
一个请求或响应报文由以下字段组成:
- 请求方法,客户端希望服务器对资源执行的动作。
- 请求 URL,命名了所请求的资源。
- 协议版本,报文所使用的 HTTP 版本。
- 状态码,这三位数字描述了请求过程中所发生的情况。
- 原因短语,数字状态码的可读版本(例如上面的响应示例跟在 200 后面的 OK,一般按规范写最好)。
- 首部,可以有零或多个首部。
- 实体的主体部分,可以为空也可以包含任意二进制数据。
一个 HTTP 请求示例:
GET /2.app.js HTTP/1.1 Host: 118.190.217.8:3389 Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36 Accept: */* Referer: http://118.190.217.8:3389/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9
一个 HTTP 响应示例:
HTTP/1.1 200 OK X-Powered-By: Express Accept-Ranges: bytes Cache-Control: public, max-age=0 Last-Modified: Sat, 07 Mar 2020 03:52:30 GMT ETag: W/"253e-170b31f7de7" Content-Type: application/javascript; charset=UTF-8 Vary: Accept-Encoding Content-Encoding: gzip Date: Fri, 15 May 2020 05:38:05 GMT Connection: keep-alive Transfer-Encoding: chunked
方法
方法 | 描述 |
GET | 从服务器获取一份文档 |
HEAD | 只从服务器获取文档的头部 |
POST | 向服务器发送需要处理的数据 |
PUT | 将请求的数据部分存储在服务器上 |
TRACE | 对可能经过代理服务器传送到服务器上去的报文进行追踪 |
OPTIONS | 决定可以在服务器上执行哪些方法 |
DELETE | 从服务器上删除一份文档 |
GET 和 HEAD
其中 GET 和 HEAD 被称为安全方法,因为它们是幂等的(如果一个请求不管执行多少次,其结果都是一样的,这个请求就是幂等的),类似于 POST 就不是幂等的。
HEAD 方法和 GET 方法很类似,但服务器在响应中只返回首部。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查。使用 HEAD,可以:
- 在不获取资源的情况下了解资源的情况。
- 通过查看响应状态码,看看某个对象是否存在。
- 通过查看首部,了解测试资源是否被修改了。
服务器开发者必须确保返回的首部与 GET 请求所返回的首部完全相同。遵循 HTTP/1.1 规范,就必须实现 HEAD 方法。
PUT
与 GET 方法从服务器读取文档相反,PUT 方法会向服务器写入文档。PUT 方法的语义就是让服务器用请求的主体部分来创建一个由所请求的 URL 命名的新文档。 如果那个文档已存在,就覆盖它。
POST
POST 方法通常用来向服务器发送表单数据。
TRACE
客户端发起一个请求时,这个请求可能要穿过路由器、防火墙、代理、网关等。每个中间节点都可能会修改原始的 HTTP 请求,TRACE 方法允许客户端在最终发起请求时,看看它变成了什么样子。
TRACE 请求会在目的服务器端发起一个“环回”诊断。行程最后一站的服务器会弹回一条 TRACE 响应,并在响应主体中携带它收到的原始请求报文。 这样客户端就可以查看在所有中间 HTTP 应用程序组成的请求/响应链上,原始报文是否被毁坏或修改过。
TRACE 方法主要用于诊断,用于验证请求是否如愿穿过了请求/响应链。它也是一种工具,用来查看代理和其他应用程序对用户请求所产生的效果。 TRACE 请求中不能带有实体的主体部分。TRACE 响应的实体主体部分包含了响应服务器收到的请求的精确副本。
OPTIONS
OPTIONS 方法请求 Web 服务器告知其支持的各种功能。
DELETE
DELETE 方法就是让服务器删除请求 URL 所指定的资源。