Comment on page
HTTP 请求的完整过程
一次 HTTP 请求的整个过程包括:DNS 解析、建立 TCP 连接、客户端请求、服务端响应、断开 TCP 连接。 本文主要从以上几个方面来讲解一次完整的 HTTP 请求。
今天我们能够在网络中畅游,都得益于一位计算机科学家蒂姆·伯纳斯·李的构想。1991 年 8 月 6 日,蒂姆·伯纳斯·李在位于欧洲粒子物理研究所(CERN)的 NeXT 计算机上,正式公开运行世界上第一个Web网站,建立起基本的互联网基础概念和技术体系,由此开启了网络信息时代的序幕。
伯纳斯·李的提案包含了网络的基本概念并逐步建立了所有必要的工具:
- 提出 HTTP (Hypertext Transfer Protocol) 超文本传输协议,允许用户通过单击超链接访问资源。
- 提出使用HTML超文本标记语言(Hypertext Markup Language)作为创建网页的标准。
- 创建第一个 Web 浏览器,称为万维网浏览器,这也是一个 Web 编辑器。
HTTP 协议一共有五大特点:
- 支持客户/服务器模式。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由 Content-Type(Content-Type是HTTP包中用来表示内容类型的标识)加以标记。
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完,不会记录任何信息(Cookie 和 Session 孕育而生,后期再讲)。
DNS( Domain Name System) 是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于 TCP/IP 网络,它所提供的服务是用来将主机名和域名转换为 IP 地址的工作。
关于 DNS 的获取流程:
DNS 是应用层协议,事实上他是为其他应用层协议工作的,包括不限于 HTTP 和 SMTP 以及 FTP,用于将用户提供的主机名解析为 ip 地址。具体过程如下:
- 用户主机上运行着 DNS 的客户端,就是我们的 PC 机或者手机客户端运行着 DNS 客户端。
- 浏览器将接收到的 url 中抽取出域名字段,就是访问的主机名,比如
http://www.baidu.com/
,并将这个主机名传送给 DNS 应用的客户端。 - DNS 客户机端向 DNS 服务器端发送一份查询报文,报文中包含着要访问的主机名字段(中间包括一些列缓存查询以及分布式 DNS 集 群的工作)。
- 该 DNS 客户机最终会收到一份回答报文,其中包含有该主机名对应的 IP 地址。
- 一旦该浏览器收到来自 DNS 的 IP 地址,就可以向该IP地址定位的 HTTP 服务器发起 TCP 连接。
相信大家都知道 HTTP 是一个基于 TCP/IP 协议簇来传递数据。TCP/IP 协议在进行连接的时候都需要进行三次握手,所以 HTTP 在连接服务器的时候也需要进行三次握手。
TCP/IP 是互联网相关的各类协议簇的总称。也有另一种说法 TCP/IP 是 TCP 和 IP 两种协议。 TCP/IP 四层模型如下:

TCP 报文包 = TCP 头信息 + TCP 数据体,而在 TCP 头信息中包含了 6 种控制位(上图红色框中),这六种标志位就代表着 TCP 连接的状态:
- SYN:表示请求建立一个连接(同步序号)
- URG:紧急数据(urgent data)—这是一条紧急信息
- ACK:确认已收到(确认序号)
- PSH:尽可能快地将数据送往接收进程
- RST:表示要求对方重新建立连接
- FIN:表示通知对方本端已经完成数据发送,要关闭连接了
TCP 建立连接过程 --- 三次握手

过程说明:
- 客户端发送位码为 syn=1,随机产生 seq number=1234567 的数据包到服务器,服务器由SYN=1知道客户端要求建立联机(客户端:我要连接你)
- 服务器收到请求后要确认联机信息,向 A 发送ack number=(客户端的seq+1),syn=1,ack=1,随机产生seq=7654321的包(服务器:好的,你来连吧)
- 客户端收到后检查 ack number 是否正确,即第一次发送的 seq number+1 ,以及位码 ack 是否为1,若正确,客户端会再发送 ack number=(服务器的seq+1),ack=1,服务器收到后确认seq值与ack=1则连接建立成功。(客户端:好的,我来了)
注意注意注意,重要的问题说三次:为什么 http 建立连接需要三次握手,不是两次或四次?
个人理解:确认双方信道可以实现最低限度的全双工(可以合并第 2,3 步),三次是最少的安全次数,两次不安全,四次浪费资源。
如果觉得理解不对的,可以下方留言或者在 issue 里面去讨论。
三次握手之后,客户端和服务端的连接就已经建立好了,客户端就可以向服务器端发送 HTTP 请求。
TCP 报文包 = TCP 头信息 + TCP 数据体,TCP 头信息的结构如下:
TCP 数据体,也就是 HTTP 请求报文。结构如下:

GET① /settings/user_has_gravatar② HTTP/1.1③
Host: github.com
Connection: keep-alive
Accept: application/json
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Referer: https://github.com/settings/profile
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: _octo=GH1.1.503958834.1571276454; _ga=GA1.2.104159404.1571276456; _device_id=d0247b2a88a0126139fad221e62f2c91; user_session=Z3ZgN7swYstKY35aXI_GD_u4A3Jk-pZ-5bVBXRCBpPmrjfV9; __Host-user_session_same_site=Z3ZgN7swYstKY35aXI_GD_u4A3Jk-pZ-5bVBXRCBpPmrjfV9; logged_in=yes; dotcom_user=joyang1; has_recent_activity=1; tz=Asia%2FShanghai; _gat=1
④
username=tommyyang&userid=168168⑤
- ①是请求方法,HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE,最常的两种 GET 和 POST,如果是 RESTFUL 接口的话一般会用到 GET、POST、DELETE、PUT。
- ②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL
- ③是协议名称及版本号
- ④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息
- ⑤是报文体,GET 方法 username=tommyyang&userid=168168 通过请求 URL 传递参数,如“/settings/user_has_gravatar?username=tommyyang&userid=168168”的方式传递请求参数。
参数说明如下
: Host: 域名。 Connection: 连接状态。 User-Agent:客户端使用的操作系统和浏览器的名称和版本,有些网站会限制请求浏览器。 Referer:跳转到该网页的地址,表示此请求来自哪里,有些网站会限制请求来源。服务器在收到客户端请求,然后对请求处理完后需要响应并返回给客户端,而 HTTP 响应报文结构与请求结构体一致。
HTTP 响应报文结构与请求报文结构类似,包括:
- 报文首部。
- 空行(CR + LF),表示报文主体开始。
- 报文主体。
- 空行(CR + LF),表示报文主体结束。
结构如下:

| 类别 | 原因短语 |
1XX | Informational(信息状态码) | 接受的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
HTTP/1.1① 200 OK②
③
Server: GitHub.com
Date: Mon, 20 Jan 2020 11:17:40 GMT
Status: 304 Not Modified
Vary: X-PJAX
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: user_session=Z3ZgN7swYstKY35aXI_GD_u4A3Jk-pZ-5bVBXRCBpPmrjfV9; path=/; expires=Mon, 03 Feb 2020 11:17:40 -0000; secure; HttpOnly
Set-Cookie: __Host-user_session_same_site=Z3ZgN7swYstKY35aXI_GD_u4A3Jk-pZ-5bVBXRCBpPmrjfV9; path=/; expires=Mon, 03 Feb 2020 11:17:40 -0000; secure; HttpOnly; SameSite=Strict
Set-Cookie: has_recent_activity=1; path=/; expires=Mon, 20 Jan 2020 12:17:40 -0000
X-Request-Id: 1bc86002-496d-4d6a-a028-9fd129fac631
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
Expect-CT: max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"
Content-Security-Policy: default-src 'none'; base-uri 'self'; block-all-mixed-content; connect-src 'self' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src 'self'; media-src 'none'; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com
X-GitHub-Request-Id: BEF1:4193:BBF9FF:1933DED:5E258C54
Content-Type: application/json; charset=utf-8
ETag: W/"b086cd16a5d1e1190981cda623503729"
X-Frame-Options: deny
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Encoding: gzip
④
6f
{"has_gravatar":false}
0
- ① 报文协议及版本
- ② 状态码及状态描述
- ③ 响应头
- ④ 响应体
在服务器响应完毕后,一次会话就结束了,这时候连接会断开么?
是否断开,我们需要根据 HTTP 版本来确定:
- HTTP/1.0 版本的时候,客户端与服务器完成一个请求/响应之后,会将之前建立的 TCP 连接断开,下次请求的时候又要重新建立 TCP 连接,这也就是短连接。
- 在 HTTP1.0 发布仅半年后(1997年1月) ,HTTP/1.1 版本发布并带来一个新的功能:在客户端与服务器完成一次请求/响应之后,允许不断开 TCP 连接,这意味着下次请求就直接使用这个 TCP 连接而不需要重新握手建立新连接,这也被称为长连接。
tips:长连接是指一次TCP连接允许多次HTTP会话,HTTP永远都是一次请求/响应,会话结束,HTTP本身不存在长连接之说。
早在 1999 年 HTTP1.1 就推广普及,现在浏览器在请求时请求头中都会携带一个参数:Connection:keep-alive,这表示浏览器要求与服务器建立长连接, 而服务器也可以设置是否愿意建立长连接。
- 优点:当网站中有大量静态资源(图片、css、js等)被请求时就可以开启长连接,这些静态资源就可以通过一次 TCP 连接发送。
- 缺点:当客户端请求一次就不再请求时,服务器却一直开着长连接,资源被占用着,严重浪费资源。
在建立 TCP 连接时是三次握手,而断开 TCP 连接是四次挥手。
TCP 断开连接过程 --- 四次挥手结构图如下:

在 TCP 连接建立的时候,讲到了标志位:FIN 表示通知对方自身要断开连接了。
注意注意注意,重要的问题说三次:为什么断开连接是四次挥手呢?
个人理解:由于 TCP 要支持半关闭连接。在建立连接的时候是全双工的,A <=> B 双方都可以读写。 断开的时候需要支持半关闭,意味着 TCP 支持客户端和服务端双方独立关闭通道;因此会有两次独立的关闭写通道的请求。一次关闭请求(FIN),对应一个 ACK,也就有了四次挥手。
如果觉得理解不对的,可以下方留言或者在 issue 里面去讨论。
- 了解下 HTTP2.0。
- HTTP & RPC,了解下 HTTP 与 RPC 的区别,为什么要使用 RPC?
- HTTP & HTTPS,了解学习为什么现在应用更多的使用 HTTPS?
Last modified 3yr ago