彻底搞懂 HTTP 的请求过程

916 阅读15分钟

本篇我们探索从HTTP请求发送开始到收到网络响应回报,期间都经历了哪些具体过程。 由于是分析过程,从整个宏观流程来看,涉及的是主要流程,一些知识细节无法面面俱到,大家自行查漏补缺。

网络分层模型

A famous aphorism of Butler Lampson goes: "All problems in computer science can be solved by another level of indirection" from: en.wikipedia.org/wiki/Indire…

上面的这句名言流传甚广,不管是计算机体系结构,还是计算机网络;不管是操作系统,还是编译构建,我们也到处可见设计上的分层以及为解决问题而添加的间接层。

计算机网络体系也是一个抽象的层次网络分层模型,主要有两大重要的网络体系结构:

  • OSI 参考模型
  • TCP/IP 参考模型

OSI 参考模型

广泛用于教学讨论的模型,分为:应、表、会、传、网、数、物 七层。 image.png

图片来源:计算思维百科

TCP/IP 参考模型

实际广泛使用多年的模型,分为:

  • 应、传、网、数、物,5层(结合 OSI 优点,只是为介绍网络原理而设计的)
  • 应、传、网互、网接,4层(实际应用还是 TCP/IP 四层体系结构)

image.png

图片来源: zhuanlan.zhihu.com/p/31327310

分层对应关系

image.png

HTTP

HTTP 位于上方的网络分层模型中的应用层 ,下面是传输层的 TCP,我们发生的 HTTP 请求,为了传输方便,在传输层会把从 应用层 收到的数据(HTTP请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。

请求收发流程

发送与接收互为逆过程 image.png

图片来源: 图解HTTP ​

发送方逐层封装,接收方逐层解封。 image.png

图片来源: 图解HTTP

可见,发送端在层与层之间传输数据时,每经过一层时,必定会被打上一个该层所属的首部信息。 而在接收端,在层与层之间数据传输时,每经过一层时会把对应的首部消去

HTTP 请求过程

在发送一个 HTTP 请求之前,我们同 URL 来指明了具体发送到哪里,可之后是如何工作的呢?这里要说一下以下几个协议

  • DNS:应用层协议,负责域名解析,通过域名来获取目标主机的 IP
  • TCP:传输层协议,提供可靠的字节流服务,需要三次握手、四次挥手
  • IP:网际协议,把各种数据包传输给对方,需要有:IP地址和MAC 地址
  • ARP:网络层协议,是解析地址的协议,凭借 MAC 地址进行通信,根据通信方IP反查出对应的 MAC 地址

HTTP 报文

HTTP 报文具有固定的报文格式,无论是请求报文还是响应报文

请求报文示例

image.png

响应报文示例

image.png

详细的报文信息可参见:

应用层(DNS 解析)

正如让你记住你认识的所有人的电话号码一样,是一个很大的挑战,尤其对于时常换号的情况,则更加严峻。一种聪明的做法,如目前的手机通讯录,我们只需要知道他们的名字即可点击拨通他们电话与之联系,这是因为每一个名称对应着电话号码,手机则自动去进行拨打,通讯录则是一张映射表,大大减轻了人工的记忆负担。 ​

事实上,在 Internet 的早期,也采用了这种比较简单且原始的方式。那时有一个叫 hosts.txt 的文件,里面记录着所有的计算机名字和它们的 **IP** 地址。这种方式相对简单,然而对于一个百万台主机的网络来讲,文件会特别大,而且集中管理带来的负载和延迟及命名冲突等问题是很大的挑战。 ​

为了解决集中管理带来的种种问题,1983年开发了 DNS 域名系统。它本质上是一种层次的、基于域的命名方案,以分布式数据库系统加以实现。

什么是DNS

DNS (Domain Name System)域名系统,它既是一个由分层的DNS服务器实现的分布式数据库,同时又是一个使得主机能够查询分布式数据库的应用层协议,它的主要用途是将主机名映射成 IP 地址。

DNS 层级结构

为了处理扩展性问题,DNS 使用了大量的 DNS 服务器,并且是有明确的层级关系。 部分DNS 服务器层级示意:

image.png

DNS 解析过程

DNS 协议运行在 UDP 之上,端口为 53。 通过 UDP 协议,DNS 消息通过 UDP 数据包发送,格式简单,只有查询和响应。 每个查询报文都包含 16 位的标识符,这个标识符被复制到响应包中,以便域名服务器收到答案与相应的查询匹配,而不至于混淆多个查询的结果。 ​

事实上,一次域名解析可以涉及递归和迭代两种机制。

递归查询

简而言之,就是委托本地DNS服务器查询,只关注结果。 举例,老板发微信问我,哪里夜店妹子多,这我哪知道,我又没去过,我就问经常去夜店的二狗,二狗又问了他的基友大飞,大飞告诉二狗,二狗再转告我,最后我装作很内行的回复了老板。 而对于老板而言,这个查询就是递归查询

迭代查询

简而言之,就是 DNS 客户端每个过程都亲力亲为进行查询。 举例,老板问,小毛推荐下最近的足疗店吧?我满脸严肃的拒绝说,不知道,你可以问下公关部孙总。老板去找孙总,又被指引到公关人员小张,从小张嘴里问到了答案。 这个过程是老板一步一步地查找答案,就是迭代查询image.png 图片来源:《计算机网络:自顶向下方法》

DNS 缓存

为了减少不必要的请求,加快响应性能,减少网络时延,在 DNS 系统中广泛使用了缓存技术。 就是在 DNS 服务器中缓存了主机名和IP 地址的映射对,下次查询可以直接命中返回。

DNS 资源记录

无论是只有一台主机的域,还是顶级域,每个域都有一组与它相关联的资源记录,这些记录组成了DNS数据库。 DNS 的基本功能就是将域名映射到资源记录。 一条资源记录的用一个五元组来表示,通常会被编码成二进制形式来提高效率。 每条记录均会占一行,示例如下:

Domain_name	Time_to_live	Class	Type	Value
  域名		生存期           类别      类型    值

对于上面的五元组,其中域名和生存期类别等都比较好理解,重点说一下类型。 类型是用来指出这是什么类型的记录,DNS 记录有许多的类型,常见列举如下:

类型含义
A主机的 IPV4地址32位整数
AAAA主机的 IPV6地址128位整数
CNAME规范名域名
NS域名服务器本域的服务器名
TXT文本说明的ASCII 文本
MX邮件交换优先级,愿意接受邮件的域

注意:每台 Internet 主机至少要有一个 IP 地址,以便跟其他机器进行通信,某些主机可能有两个或多个网络接口,它们就有两个或多个A或AAAA资源记录。因此,查询单个域名时,可能获得多个地址。 DNS 资源记录示例

google.com.     1165    IN  TXT		"v=spf1 include:_spf.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all"
google.com.     53965   IN  SOA 	ns1.google.com. dns-admin.google.com. 2014112500 7200 1800 1209600 300
google.com.     231 		IN  A   	173.194.115.73
google.com.     231 		IN  A   	173.194.115.78
google.com.     231 		IN  A   	173.194.115.64
google.com.     231 		IN  A   	173.194.115.65
google.com.     231 		IN  A   	173.194.115.66
google.com.     231 		IN  A   	173.194.115.67
google.com.     231 		IN  A   	173.194.115.68
google.com.     231 		IN  A   	173.194.115.69
google.com.     231 		IN  A   	173.194.115.70
google.com.     231 		IN  A   	173.194.115.71
google.com.     231 		IN  A   	173.194.115.72
google.com.     128 		IN  AAAA	2607:f8b0:4000:809::1001
google.com.     40766   IN  NS  	ns3.google.com.
google.com.     40766   IN  NS  	ns4.google.com.
google.com.     40766   IN  NS  	ns1.google.com.
google.com.     40766   IN  NS  	ns2.google.com.

NSLOOKUP

nslookup 是一款可以查询任何指定 DNS 服务器的 DNS 记录。 命令语法:

nslookup [-option1] [-option2] host-to-find dns-server

常用命令如下:

查询域名对应 IP地址

$ nslookup baidu.com
Server:		192.168.1.1
Address:	192.168.1.1#53

Non-authoritative answer:				# 非权威应答
Name:	baidu.com
Address: 220.181.38.148
Name:	baidu.com
Address: 39.156.69.79

# 可见和 ping 的地址一样
$ ping baidu.com
PING baidu.com (220.181.38.148): 56 data bytes
64 bytes from 220.181.38.148: icmp_seq=0 ttl=50 time=9.037 ms
64 bytes from 220.181.38.148: icmp_seq=1 ttl=50 time=8.393 ms

查看域名的 DNS 主机名

使用 -type=NS 来查找 DNS 服务器

$ nslookup -type=NS qq.com
Server:		192.168.1.1
Address:	192.168.1.1#53

Non-authoritative answer:					# 非权威应答
qq.com	nameserver = ns2.qq.com.
qq.com	nameserver = ns1.qq.com.
qq.com	nameserver = ns3.qq.com.
qq.com	nameserver = ns4.qq.com.

Authoritative answers can be found from:

说明:非权威应答表示着这个响应来自某个服务器的缓存,而不是来自权威 DNS 服务器。

通过指定 DNS 服务器来解析域名

通过前面获得的 ns2.qq.com 解析 baidu.com

$ nslookup baidu.com ns2.qq.com
Server:		ns2.qq.com
Address:	123.151.66.78#53

** server can't find baidu.com: REFUSED

看来,qq 的 DNS 是自家使用的,没有其他的数据记录,因此拒绝了。 当然我们可以使用公共的 DNS 进行解析域名 通过 dns.alidns.com 解析 qq.com

$ nslookup qq.com  dns.alidns.com

Server:		dns.alidns.com
Address:	223.5.5.5#53

Non-authoritative answer:
Name:	qq.com
Address: 58.247.214.47
Name:	qq.com
Address: 58.250.137.36

这下成功解析到了相应的 IP 地址。

dig 域名 输出过程

通过 dig 命令,可以看到整个详细的查询过程 dig juejin.cn

$ dig juejin.cn

# 工具信息段
; <<>> DiG 9.10.6 <<>> juejin.cn
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5896
;; flags: qr rd ra; QUERY: 1, ANSWER: 17, AUTHORITY: 0, ADDITIONAL: 0

# 查询内容端
;; QUESTION SECTION:
;juejin.cn.			IN	A

# DNS服务器应答段
;; ANSWER SECTION:
juejin.cn.		193	IN	CNAME	juejin.cn.w.cdngslb.com.
juejin.cn.w.cdngslb.com. 1	IN	A	218.61.192.113
juejin.cn.w.cdngslb.com. 1	IN	A	218.61.192.109
juejin.cn.w.cdngslb.com. 1	IN	A	218.61.192.110
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.238
juejin.cn.w.cdngslb.com. 1	IN	A	116.95.26.243
juejin.cn.w.cdngslb.com. 1	IN	A	116.95.26.242
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.240
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.241
juejin.cn.w.cdngslb.com. 1	IN	A	218.61.192.114
juejin.cn.w.cdngslb.com. 1	IN	A	1.28.145.231
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.244
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.248
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.242
juejin.cn.w.cdngslb.com. 1	IN	A	124.132.135.242
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.239
juejin.cn.w.cdngslb.com. 1	IN	A	60.19.67.243

# 传输信息段
;; Query time: 49 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Sat Jun 26 12:42:34 CST 2021
;; MSG SIZE  rcvd: 320

DNS 抓包跟踪

接下来,我们通过抓包来跟踪下 DNS 的解析过程

递归查询

juejin.cn 为例,输入 dig juejin.cn 见上方响应信息,此处不再粘贴。WireShark 抓包图示

image.png

可见我本地103DNS server 114.114.114.114 发送了一条 DNS 查询消息

image.png

之后受到了 DNS server 114.114.114.114 的响应

image.png

迭代查询

baidu.com 为例,输入 dig +trace baidu.cn 可以实现迭代查询。 过程:

  1. 从不权威DNS府二期查到全球 13台根服务器
  2. 从其中的1台根服务器查到了.com 顶级域名服务器
  3. 从其中的1台顶级域名服务器中查到了百度的域名服务器
  4. 从百度的域名服务器查到了 baidu.com ip 地址

控制台输出:

$ dig +trace baidu.com

; <<>> DiG 9.10.6 <<>> +trace baidu.com		
;; global options: +cmd												# 从不权威DNS府二期查到全球 13台根服务器
.			954	IN	NS	b.root-servers.net.
.			954	IN	NS	i.root-servers.net.
.			954	IN	NS	l.root-servers.net.
.			954	IN	NS	g.root-servers.net.
.			954	IN	NS	c.root-servers.net.
.			954	IN	NS	e.root-servers.net.
.			954	IN	NS	m.root-servers.net.
.			954	IN	NS	j.root-servers.net.
.			954	IN	NS	a.root-servers.net.
.			954	IN	NS	k.root-servers.net.
.			954	IN	NS	f.root-servers.net.
.			954	IN	NS	h.root-servers.net.
.			954	IN	NS	d.root-servers.net.
;; Received 239 bytes from 114.114.114.114#53(114.114.114.114) in 11 ms

com.			172800	IN	NS	a.gtld-servers.net.
com.			172800	IN	NS	b.gtld-servers.net.
com.			172800	IN	NS	c.gtld-servers.net.	# 从其中的1台根服务器查到了.com 顶级域名服务器
com.			172800	IN	NS	d.gtld-servers.net.
com.			172800	IN	NS	e.gtld-servers.net.
com.			172800	IN	NS	f.gtld-servers.net.
com.			172800	IN	NS	g.gtld-servers.net.
com.			172800	IN	NS	h.gtld-servers.net.
com.			172800	IN	NS	i.gtld-servers.net.
com.			172800	IN	NS	j.gtld-servers.net.
com.			172800	IN	NS	k.gtld-servers.net.
com.			172800	IN	NS	l.gtld-servers.net.
com.			172800	IN	NS	m.gtld-servers.net.
com.			86400	IN	DS	30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
com.			86400	IN	RRSIG	DS 8 1 86400 20210708200000 20210625190000 14631 . E4vNdg++JOGz+5Q0BcqMUAr4nJBE9dZ2j0S/4khgXUqCsJ5Wdorhccyn zjdcbRmkkCxasBFWgDqcKT00K18E9ErXgcgHVZkcy0eFbSHOFWLwbWU1 xEFBv8kjz+NxLd3bugv8zzEcDY5/4BE0TM/gPsIXz6FSjZSkZJJfrlMJ l1QQvur7cREIGYqMhFDs3IlEFXtrD35UVWgiVqwFKSsXxMnhqJauf/iF OjnUq8EEqtJxpuMtjLXWEkPzZPEfjCo7tLAzKfjp4DkbxK17B0e64foz u6oRbFL4yaeDkL+RRdbuKAIhq9AzwkR165xXtp8EdUTo8Vi2Br4uaAc5 thhuGA==
;; Received 1169 bytes from 198.97.190.53#53(h.root-servers.net) in 123 ms

baidu.com.		172800	IN	NS	ns2.baidu.com.
baidu.com.		172800	IN	NS	ns3.baidu.com.	# 从顶级域名服务器中查到了百度的域名服务器
baidu.com.		172800	IN	NS	ns4.baidu.com.
baidu.com.		172800	IN	NS	ns1.baidu.com.
baidu.com.		172800	IN	NS	ns7.baidu.com.
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A  NS SOA RRSIG DNSKEY NSEC3PARAM
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20210703042346 20210626031346 54714 com. fpRZKBFjStpVmT0pXcXOuti8qzhE0DfHJhriwBJn6Uj/rYfpI788/Jj6 K55Qs394YruLilWiMbHWRu7ubsSTwgqBD76CVuyqsq/jVUZePqBeU/5r nN68gFAjpfBZnG5UkxH9CzTHmercrzWD4rAp+5ZcpxkjjcT6x1VxbnYO brhgCDWKEipgGtByalV7NfRjyTuJrsrO8j1loeBOJOXnSQ==
HPVUSBDNI26UDNIV6R0SV14GC3KGR4JP.com. 86400 IN NSEC3 1 1 0 - HPVV8SARM2LDLRBTVC5EP1CUB1EF7LOP  NS DS RRSIG
HPVUSBDNI26UDNIV6R0SV14GC3KGR4JP.com. 86400 IN RRSIG NSEC3 8 2 86400 20210703045003 20210626034003 54714 com. hG2hp+pbEak9kYn4UqCWs6f1fssX2v7DCKYKXQvzdA8Ruye7RJIoJWzT ae3gfdzoKt4uYvoRy2Mho2r9SvCerJuCbun9YQuY51SY9KfJVEfAL/7X 1BZ+WcL++zvwFUJZ/p/BCUrT6qvRFfgQvKAocwndfDvmSeM7O866u9Mv sCUP1JSYddWQBmZq50NZsRPKnWFaN0uYv/G+dOUVmE4rqw==
;; Received 757 bytes from 192.54.112.30#53(h.gtld-servers.net) in 192 ms

baidu.com.		600	IN	A	220.181.38.148
baidu.com.		600	IN	A	39.156.69.79
baidu.com.		86400	IN	NS	ns2.baidu.com.
baidu.com.		86400	IN	NS	dns.baidu.com.
baidu.com.		86400	IN	NS	ns3.baidu.com.   # 从百度的域名服务器查到了 baidu.com ip 地址
baidu.com.		86400	IN	NS	ns7.baidu.com.
baidu.com.		86400	IN	NS	ns4.baidu.com.
;; Received 240 bytes from 220.181.33.31#53(ns2.baidu.com) in 9 ms

WireShark 抓包图示,数据包细节和上面迭代比较一致,此处就贴一个全图吧 image.png

DNS 劫持

DNS 劫持定义

DNS 劫持是指通过某种技术手段,篡改正确域名和IP地址之间的映射关系,使得域名映射到错误的IP 地址,因此 DNS 劫持被认为是一种 DNS 重定向攻击。 通常,DNS 劫持,会被用作域名欺诈,将其解析到一个钓鱼网站 IP 上面,或者访问网站时候显示额外的广告信息,窃取用户数据与个人信息等。

DNS 劫持分类

通常根据其链路可以分为以下几类:

  • 本地 DNS劫持:指发生在客户端的 DNS劫持
    • 病毒或模板程序,篡改 DNS配置、DNS服务地址、DNS缓存等
    • 篡改路由器、网络代理设备的 DNS 配置及域名解析结果
  • DNS 解析路径劫持
    • DNS 请求转发重定向到其他DNS服务器
    • DNS 请求复制,然后先于正常应答返回DNS 劫持的结果
    • DNS 请求代答,代替DNS服务器进行回答
  • 篡改权威 DNS 服务器
    • 黑入域名管理账户,篡改域名NS授权记录
    • 黑入管局管理账户,篡改域名NS授权记录

DNS 劫持对策

业内常见的对策是 HTTPDNS ,越来越多的厂商采用这种服务以一定程度上缓解 DNS 劫持的影响。 域名防劫持:

使用HTTP(HTTPS)协议进行域名解析,域名解析请求直接发送至HTTPDNS服务器,绕过运营商Local DNS,避免域名劫持问题。

特殊服务:

由于运营商策略的多样性,其 Local DNS 的解析结果可能不是最近、最优的节点,HTTPDNS 能直接获取客户端 IP ,基于客户端 IP 获得最精准的解析结果,让客户端就近接入业务节点

HttpDNS 的工作原理图示 image.png 图片来源:阿里云 HTTPDNS image.png 图片来源:网易云盾 DNS劫持原理?如何处理DNS劫持?

传输层(TCP)

前面已经提到过,应用层发生的 HTTP 请求,为了传输方便,在传输层会把从 应用层 收到的数据(HTTP请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。 通过上述 DNS 解析 环节已经解析出了目标主机的 IP 地址,这里就可以建立连接通信了。 建连接通信的过程如下:

image.png

图片来源: 图解HTTP

TCP 是有连接型协议

为什么通信前需要返回确认3次才建连、四次挥手之后才断开呢?这是因为TCP是在不可靠网络上建立的有连接型可靠传输。 image.png

图片来源: 图解TCP/IP

TCP头部格式

image.png

图片来源: 图解TCP/IP

网络层(IP、ARP)

前面我们提到了,会通过 DNS 解析去找到目标主机的 IP 地址,然后通过它进行 TCP 链接的建立。通过上面 TCP 封包的图示可以看到,网络层关注 IP地址, 网络层,增加作为通信目的的 MAC 地址后转发给链路层。 但是在数据链路层则不使用 IP 地址,在以太网的情况下只使用 MAC 地址传输数据包。 发送数据包使用的地址不足以实现发送到目标端,还需要 MAC 地址才能找到真正的主机信息,保存这种信息的就是 路由控制表

ARP

如何查找到 Mac 地址

只要确定了 IP 地址,就可以向这个目标地址发送 IP 数据报了,但是底层数据链路层,进行实际通信时却有必要了解每个 IP 地址所对应的 MAC 地址。 ARP 就是这么一种解决地址问题的协议,它可以通过目标IP 地址,以定位下一个应该接收数据分包的网络设备对应的 MAC 地址。

ARP 工作机制

image.png

从一个 IP 地址发送 ARP 请求包以发现其 MAC 地址,目标地址将自己的 MAC 地址填入其中的 ARP 响应包中返回给 IP 地址,然后实现链路内的 IP 通信。 每台主机都有一张ARP 缓存信息表,它记录着主机的 IP地址和MAC地址的对应关系。 ​

数据链路层

当有了 MAC 地址和 IP 之后,就可以向目标主机通信了。

数据链路层的封装

应用层的数据,会在传输层进行包装,附加上 TCP 包头部等信息,往下传递,在网络层会再次附加IP包头部,进入数据链路层同样会附加MAC地址等的以太网包头部信息。 经过数据链路层的包示意图如下: image.png

图片来源: 图解TCP/IP

HTTP 请求抓包

接下来使用 WireShark 抓包分析下一次 HTTP 网络请求 我们以访问:[https://www.rfc-editor.org/info/rfc2616](https://www.rfc-editor.org/info/rfc2616) 为例

DNS 解析

通过 DNS 请求 114.114.114.114 域名服务,查询 www.rfc-editor.orgip image.png 在回包中可以看到,查询到了IP 地址为:4.31.198.49 image.png

TCP 三次握手

前一步获取到了IP 地址为: 4.31.198.49 ,然后进行 TCP 的握手建连 image.png

HTML GET请求

TCP 建连成功之后,进行 HTTP 请求 image.png image.png

参考链接