DNS协议与DNS系统
DNS系统的由来
如果说ARP协议是用来将IP地址转换为MAC地址,那么DNS协议则是用来将域名转换为IP地址(也可以将IP地址转换为相应的域名地址)。
我们都知道,TCP/IP中使用的是IP地址和端口号来确定网络上某一台主机上的某一个程序,不免有人有疑问,为什么不用域名来直接进行通信呢?
1. 因为IP地址是固定长度的,IPv4是32位,IPv6是128位,而域名是变长的,不便于计算机处理。
2. IP地址对于用户来说不方便记忆,但域名便于用户使用,例如www.baidu.com这是百度的域名。
总结一点就是IP地址是面向主机的,而域名则是面向用户的。
hosts文件
域名和IP的对应关系保存在一个叫hosts文件中。
最初,通过互联网信息中心来管理这个文件,如果有一个新的计算机想接入网络,或者某个计算IP变更都需要到信息中心申请变更hosts文件。其他计算机也需要定期更新,才能上网。
但是这样太麻烦了,就出现了DNS系统。
DNS系统
- 一个组织的系统管理机构, 维护系统内的每个主机的IP和主机名的对应关系
- 如果新计算机接入网络,将这个信息注册到数据库中
- 用户输入域名的时候,会自动查询DNS服务器,由DNS服务器检索数据库,得到对应的IP地址
域名详解
世界域名的最高管理机构,是一个叫做 ICANN (Internet Corporation for Assigned Names and Numbers)的组织,总部在美国加州。「ICANN 负责管理全世界域名系统的运作」。
域名其实是具有一定的层次结构的,从上到下依次为:「根域名」、「顶级域名」(top level domain,TLD)、「二级域名」、(三级域名)
① 顶级域名
先来讲讲「顶级域名」(TLD),即最高层级的域名。简单说,就是网址的最后一个部分。比如,网址www.baidu.com 的顶级域名就是 .com。ICANN 的一项主要工作,就是规定哪些字符串可以当作顶级域名。截至 2015 年 7 月,顶级域名共有 1058 个,它们大致可以分成两类:
- 一类是「通用顶级域名」(gTLD),比如.com、.net、.edu、.org、.xxx等等,共有 700 多个。 另一类是*「国家顶级域名」(ccTLD),代表不同的国家和地区,比如.cn(中国)、.io(英属印度洋领地)、.cc( 科科斯群岛)、.tv(图瓦卢)等,共有 300 多个。
当然,ICANN 自己不会去管理这些顶级域名,因为根本管不过来。想想看,顶级域名有1000多个,每个顶级域名下面都有许多批发商,如果每个都要管,就太麻烦了。ICANN 的政策是,每个顶级域名都找一个「托管商」,该域名的所有事项都由托管商负责。ICANN 只与托管商联系,这样管理起来就容易多了。举例来说,.cn 国家顶级域名的托管商就是中国互联网络信息中心(CNNIC),它决定了 .cn 域名的各种政策。
② 二级域名
而「二级域名」(Second Level Domain,SLD) 在通用顶级域名或国家顶级域名之下具有不同的意义:
- 通用顶级域名下的二级域名:一般是指域名注册人选择使用的网上名称,如 yahoo.com(商业组织通常使用自己的商标、商号或其他商业标志作为自己的网上名称,如 baidu.com)* 国家顶级域名下的二级域名:一般是指类似于通用顶级域名的表示注册人类别和功能的标志。例如,在 .com.cn 域名结构中,.com 此时是置于国家顶级域名 .cn 下的二级域名,表示中国的商业性组织,以此类推。
「三级域名」是形如 www.baidu.com 的域名,可以当做是二级域名的子域名,特征为域名包含两个 .。对于域名所有者/使用者而言,三级域名都是二级域名的附属物而无需单独费用。「三级域名甚至不能称为域名,一般称之为域名下的 “二级目录”」。
③ 根域名
❓ 那么「根域名」在哪里呢?在层次结构中根域名不是最顶级的吗?域名中怎么没有看见它?
由于 ICANN 管理着所有的顶级域名,所以它是最高一级的域名节点,被称为根域名(root domain)。在有些场合,www.xxx.com 被写成 www.xxx.com.,即最后还会多出一个点。这个点就是根域名。
理论上,「所有域名的查询都必须先查询根域名」,因为只有根域名才能告诉你,某个顶级域名由哪台服务器管理。事实上也确实如此,「ICANN 维护着一张列表(根域名列表),里面记载着顶级域名和对应的托管商」。
比如,我要访问abc.xyz,也必须先去询问根域名列表,它会告诉我 .xyz 域名由 CentralNic 公司托管。根域名列表还记载,.google由谷歌公司托管,.apple由苹果公司托管等等。
由于根域名列表很少变化,大多数 DNS 服务商都会提供它的缓存,所以根域名的查询事实上不是那么频繁。
域名服务器详解
域名服务器是指管理域名的主机和相应的软件,它可以管理所在分层的域的相关信息。一个域名服务器所负责管里的分层叫作 「区 (ZONE)」。域名的每层都设有一个域名服务器:
- 根域名服务器 顶级域名服务器 权限域名服务器
下面这幅图就很直观了:
除了上面三种 DNS 服务器,还有一种不在 DNS 层次结构之中,但是很重要的 DNS 服务器,即「本地域名服务器」。下面我们分别讲解这四种服务器都是用来干什么的
① 根域名服务器
上面我们提到,ICANN 维护着一张根域名列表,里面记载着顶级域名和对应的托管商,其实根域名列表的正式名称是 「DNS 根区」(DNS root zone),保存 DNS 根区文件的服务器,就叫做 「DNS 根域名服务器」(root name server)。根域名服务器「保存所有的顶级域名服务器的地址」
由于早期的 DNS 查询结果是一个 512 字节的 UDP 数据包。这个包最多可以容纳 13 个服务器的地址,因此就规定全世界有 13 个根域名服务器,编号从 a.root-servers.net 一直到 m.root-servers.net。其中 10 台设置在美国,另外各有一台设置于荷兰、瑞典和日本。
前面我们说过,理论上「所有域名的查询都必须先查询根域名」,所以一般来说所有的域名服务器都会注册一份根域名服务器的 IP 地址的缓存,用于在必要的时候向其发送请求。
② 顶级域名服务器
按照根域名服务器管理顶级域名的逻辑,顶级域名服务器显然就是用来「管理注册在该顶级域名下的所有二级域名」的,「记录这些二级域名的 IP 地址」。
③ 权限(权威)域名服务器
按照上面的逻辑,权限域名服务器应该是管理注册在二级域名下的所有三/四级域名的,但其实不是这样,如果一个二级域名或者一个三/四级域名对应一个域名服务器,则域名服务器数量会很多,我们需要使用「划分区」的办法来解决这个问题。那么权限域名服务器就是负责管理一个“「区」”的域名服务器。
❓ 什么是区?怎样划分区呢?
区和域其实是不同的,区可以有多种不同的划分方法。以百度为例,我们假设有 fanyi.baidu.com、ai.baidu.com、tieba.baidu.com 这三个三级域名。我们可以这样分区,fanyi.baidu.com 和 tieba.baidu.com 放在 baidu.com 权限域名服务器,ai.baidu.com 放在 ai.baidu.com 权限域名服务器中。并且 baidu.com 权限域名服务器和 ai.baidu.com 权限域名服务器是「同等地位」的,而具体怎么分区是百度公司根据域名多少、访问多少等情况去自己规定的。
④ 本地域名服务器
除了上面三种 DNS 服务器,还有一种不在 DNS 层次结构之中,但是很重要的 DNS 服务器,就是「本地域名服务器」(也被称为「权威域名服务器」)。本地域名服务器是电脑解析时的「默认」域名服务器,即电脑中设置的首选 DNS 服务器和备选 DNS 服务器。常见的有电信、联通、谷歌、阿里等的本地 DNS 服务。
如果不设置的话,会是多少呢 —-ISP 一般设置了默认缺省的DNS地址
每个因特网服务提供者或一所大学,甚至一所大学中的各个系,都可以拥有一个本地域名服务器。「当一台主机发出 DNS 查询请求时,这个查询请求报文就发送给该主机的本地域名服务器」。「本地域名服务器管理本地域名的解析和映射,并且能够向上级域名服务器进行查询」。
DNS 查询方式
递归查询
递归查询:本机向本地域名服务器发出一次查询请求,就静待最终的结果。如果本地域名服务器无法解析,自己会以DNS客户机的身份向其它域名服务器查询,直到得到最终的IP地址告诉本机
迭代查询
迭代查询:本地域名服务器向根域名服务器查询,根域名服务器告诉它下一步到哪里去查询,然后它再去查,每次它都是以客户机的身份去各个服务器查询。
所谓迭代就是,如果请求的接收者不知道所请求的内容,那么「接收者将扮演请求者」,发出有关请求,直到获得所需要的内容,然后将内容返回给最初的请求者。
通俗点来说,在递归查询中,如果 A 请求 B,那么 B 作为请求的接收者一定要给 A 想要的答案;而迭代查询则是指,如果接收者 B 没有请求者 A 所需要的准确内容,接收者 B 将告诉请求者 A,如何去获得这个内容,但是自己并不去发出请求。
一般来说,「域名服务器之间的查询使用迭代查询方式,以免根域名服务器的压力过大」。
域名缓存
上面讲解的是域名服务器之间的 DNS 查询请求过程,但实际上,每个时刻都有无数网民要上网,那每次都去访问本地域名服务器去获取 IP 地址显然是不实际的。解决方法就是「使用缓存保存域名和 IP 地址的映射」。
计算机中 DNS 记录在本地有两种缓存方式:浏览器缓存和操作系统缓存。
1)「浏览器缓存」:浏览器在获取网站域名的实际 IP 地址后会对其进行缓存,减少网络请求的损耗。每种浏览器都有一个固定的 DNS 缓存时间,如 Chrome 的过期时间是 1 分钟,在这个期限内不会重新请求 DNS
2)「操作系统缓存」:操作系统的缓存其实是用户自己配置的 hosts 文件。比如 Windows10 下的 hosts 文件存放在 C:\Windows\System32\drivers\etc\hosts
完整域名解析过程
将上面所说的域名服务器之间的 DNS 查询请求过程和域名缓存结合起来,就是一个完整的 DNS 协议进行域名解析的过程。这里我们以正向解析为例(域名解析成 IP 地址):
1)首先搜索「浏览器的 DNS 缓存」,缓存中维护一张域名与 IP 地址的对应表;
2)若没有命中,则继续搜索「操作系统的 DNS 缓存」;
3)若仍然没有命中,则操作系统将域名发送至「本地域名服务器」,本地域名服务器查询自己的 DNS 缓存,查找成功则返回结果(注意:主机和本地域名服务器之间的查询方式是「递归查询」);
4)若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行查询,通过以下方式进行「迭代查询」(注意:本地域名服务器和其他域名服务器之间的查询方式是迭代查询,防止根域名服务器压力过大):
- 首先本地域名服务器向「根域名服务器」发起请求,根域名服务器是最高层次的,它并不会直接指明这个域名对应的 IP 地址,而是返回顶级域名服务器的地址,也就是说给本地域名服务器指明一条道路,让他去这里寻找答案 本地域名服务器拿到这个「顶级域名服务器」的地址后,就向其发起请求,获取「权限域名服务器」的地址 本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址
4)本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来
5)操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来
6)至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起来
DNS报文格式及抓包解析
报文结构
DNS的报文结构如下,其中黄色为基础部分(头部),绿色为问题部分(正文),蓝色为资源记录部分(响应)。资源记录部分只在响应包中出现。
上图中显示了 DNS 的报文格式。其中,事务 ID、标志、问题计数、回答资源记录数、权威名称服务器计数、附加资源记录数这 6 个字段是DNS的报文首部,共 12 个字节。
以对www.tmall.com的请求与查询为例 请求事务ID 0x580f
头部
事务ID Trasaction ID
16bit,DNS 报文的 ID 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 DNS 应答报文是对哪个请求进行响应的
标志 Flags
16bit,DNS 报文中的标志字段
请求中的标志
响应中的标志
QR(Response)(1bit) | 查询/响应标志,0为查询,1为响应 | Response: Message is a request Response: Message is a response |
---|---|---|
opcode(4bit)操作码 | 4个比特位用来设置查询的种类,应答的时候会带相同值, 0 标准查询 (QUERY) 1 反向查询 (IQUERY) 2 服务器状态查询 (STATUS) 3 无 4 为通知 (Notify) 5 为更新 (Update) 6-15 保留值,暂时未使用 |
Opcode: Standard query (0) |
AA(1bit) | 表示授权应答 Authoritative Answer 应答的时候有意义,指出给出应答的服务器是查询域名的授权解析服务器。注意因为别名的存在,应答可能存在多个主域名,这个AA位对应请求名,或者应答中的第一个主域名。 1标识域名服务器是授权服务器。 0 为应答服务器不是该域名的权威解析服务器 1 为应答服务器是该域名的权威解析服务器 |
Authoritative: Server is not an authority for domain |
TC(1bit) | 表示截断 TrunCation 用来指出报文比允许的长度还要长,导致被截断。仅当DNS报文使用UDP服务时使用。因为UDP数据包有长度限制,当长度过长时会被截断。 1标识DNS报文长度超过512字节,并被截断。 0 为报文未截断 1 为报文过长被截断 (只返回了前 512 个字节) |
Truncated: Message is not truncated |
RD(1bit) | 表示期望递归 Recursion Desired 这个比特位被请求设置,应答的时候使用的相同的值返回。如果设置了RD,就建议域名服务器进行递归解析,递归查询的支持是可选的。 1标识执行递归查询,即DNS服务器无法解析某一个主机名,则它向其它DNS服务器继续查询,如此反复,直到它解析到目标主机名并返回给客户端。 0标识迭代查询,即DNS服务器无法解析目标主机时,它将查询到的其它DNS服务器的IP地址返回给客户端,供客户端进行参考。 0 为不期望进行递归查询 1 为期望进行递归查询 (从域名服务器进行递归查询) |
Recursion desired: Don’t do query recursively |
RA(1bit) | 表示支持递归 Recursion Available 这个比特位在应答中设置或取消,用来代表服务器是否支持递归查询。允许递归标识。此字段在应答字段中使用,1标识DNS服务器支持递归查询。 0 为应答服务器不支持递归查询 1 为应答服务器支持递归查询 |
Recursion available: Server can’t do recursive queries |
Z(3 / 1bit) AD、CD需要查证 | 保留字段,暂时未使用。在所有的请求和应答报文中必须置为0。 | Z: reserved (0) |
AD(1bit) | 应答认证:应答/权限部分未由服务器进行认证 |
0 为应答服务器未验证了该查询相关的 DNSSEC 数字签名
1 为应答服务器已经验证了该查询相关的 DNSSEC 数字签名 | Answer authenticated: Answer/authority portion was not authenticated by the server |
| CD(1bit) | 未经认证的数据:不可接受
0 为服务器已经进行了相关 DNSSEC 数字签名的验证
1 为服务器并未进行相关 DNSSEC 数字签名的验证 | Non-authenticated data: Unacceptable |
| rcode(4bit) | 应答码(Response code) - 这4个比特位在应答报文中设置,代表的含义如下:
0 正常,没有差错。
1 报文格式错误(Format error) - NS服务器不能解析请求的报文。
2 服务器失败(Server failure) - 因为服务器的原因导致没办法处理这个请求。
3 名字差错(Name Error) - 只有对授权域名解析服务器有意义,指出解析的域名不存在(请求中的地址并不存在)。
4 没有实现(Not Implemented) - NS域名服务器不支持查询类型。
5 拒绝(Refused) - 服务器由于设置的策略拒绝给出应答。比如,服务器不希望对某些请求者给出应答,或者服务器不希望进行某些操作(比如区域传送zone transfer)。
6 为域名出现了但是他不该出现
7 为集合 RR 存在但是他不该存在
8 为集合 RR 不存在但是他应该存在
9 为服务器并不是这个区域的权威服务器
10 为该名称并不包含在区域中
11-15 保留值,暂时未使用。
16 为错误的 OPT 版本或者 TSIG 签名无效
17 为无法识别的密钥
18 为签名不在时间范围内
19 为错误的 TKEY 模式
20 为重复的密钥名称
21 为该算法不支持
22 为错误的截断
23 - 3840 保留
3841 - 4095 私人使用
4096 - 65534 保留
65535 RFC 6195 | Reply code: No error (0) |
问题计数 Questions
回答资源记录数 Anwser RSS
权威名称服务器计数 Authority RSS
附加资源记录数 Additional RSS
问题部分
问题部分指的是报文格式中查询问题区域(Queries)部分。该部分是用来显示 DNS 查询请求的问题,通常只有一个问题。该部分包含正在进行的查询信息,包含查询名(被查询主机名字)、查询类型、查询类。
查询名 QNAME
要查找的名字,是一个或多个标识符的序列,长度不固定,且不使用填充字节,一般该字段表示的就是需要查询的域名(如果是反向查询,则为IP,反向查询即由IP地址反查域名)
查询类型 QTYPE
每个问题有一个查询类型。2个字节表示查询类型,取值可以为任何可用的类型值,以及通配码来表示所有的资源记录
| 类型 | 助记符 | 说明 |
| —- | —- | —- |
| 1 | A | 由域名获得IPv4地址 |
| 2 | NS(nameserver) | 查询授权的域名服务器 |
| 5 | CNAME | 查询规范名称(别名) |
| 6 | SOA | 开始授权 |
| 11 | WKS | 熟知服务 |
| 12 | PTR | 把IP地址转换成域名(指针记录,反向查询) |
| 13 | HINFO | 主机信息 |
| 15 | MX | 邮件交换记录 |
| 16 | TXT | text strings TXT 记录是为您所在网域之外的来源提供文本信息的一种 DNS 记录,可用于多种用途。该记录的值可以是人工可读文本,也可以是机器可读文本。(常用夹带其他数据比如C&C和文件,用于建立隧道) |
| 28 | *AAAA | 由域名获得IPv6地址 |
| 252 | AXFR | 对区域转换的请求,传送整个区的请求 |
| 255 | ANY | 对所有记录的请求 |
查询类 CLASS
IN (1) 指互联网地址。
CS 2 the CSNET class (Obsolete - used only for examples in some obsolete RFCs)
CH 3 the CHAOS class
HS 4 Hesiod [Dyer 87]
资源记录部分
资源记录部分包含回答问题区域、权威名称服务器区域、附加信息区域。这三部分的格式都为域名+类型+类+生存时间+资源数据长度+资源数据。
资源记录格式中每个字段含义如下:
- 域名:DNS 请求的域名。
- 类型:资源记录的类型,与问题部分中的查询类型值是一样的。
- 类:地址类型,与问题部分中的查询类值是一样的。
- 生存时间:以秒为单位,表示资源记录的生命周期,一般用于当地址解析程序取出资源记录后决定保存及使用缓存数据的时间。它同时也可以表明该资源记录的稳定程度,稳定的信息会被分配一个很大的值。
- 资源数据长度:资源数据的长度。
- 资源数据:表示按查询段要求返回的相关资源记录的数据。
资源记录部分只有在 DNS 响应包中才会出现。下面通过 DNS 响应包来进一步了解资源记录部分的字段信息
其中,方框中标注的信息为 DNS 响应报文的资源记录部分信息。该部分信息主要分为三部分信息,即回答问题区域、权威名称服务器区域、附加信息区域。
笔者这里非权威服务器响应,参考其他人的内容:
回答问题区域字段的资源记录部分信息如下:
Answers #“回答问题区域”字段
baidu.com: type A, class IN, addr 220.181.57.216 #资源记录部分
Name: baidu.com #域名字段, 这里请求的域名为baidu.com
Type: A (Host Address) (1) #类型字段, 这里为A类型
Class: IN (0x0001) #类字段
Time to live: 5 #生存时间
Data length: 4 #数据长度
Address: 220.181.57.216 #资源数据, 这里为IP地址
baidu.com: type A, class IN, addr 123.125.115.110 #资源记录部分
Name: baidu.com
Type: A (Host Address) (1)
Class: IN (0x0001)
Time to live: 5
Data length: 4
Address: 123.125.115.110
其中,Name 的值为 baidu.com,表示 DNS 请求的域名为 baidu.com;类型为 A,表示要获取该域名对应的 IP 地址。Address 的值显示了该域名对应的 IP 地址。这里获取到了 2 个 IP 地址,分别为 220.181.57.216 和 123.125.115.110。
权威名称服务器区域字段的资源记录部分信息如下:
Authoritative nameservers #“权威名称服务器区域”字段
baidu.com: type NS, class IN, ns ns7.baidu.com #资源记录部分
Name: baidu.com
Type: NS (authoritative Name Server) (2) #类型字段, 这里为NS类型
Class: IN (0x0001)
Time to live: 5
Data length: 6
Name Server: ns7.baidu.com #权威名称服务器
baidu.com: type NS, class IN, ns dns.baidu.com #资源记录部分
Name: baidu.com
Type: NS (authoritative Name Server) (2) #类型字段, 这里为NS类型
Class: IN (0x0001)
Time to live: 5
Data length: 6
Name Server: dns.baidu.com #权威名称服务器
baidu.com: type NS, class IN, ns ns3.baidu.com #资源记录部分
Name: baidu.com
Type: NS (authoritative Name Server) (2)
Class: IN (0x0001)
Time to live: 5
Data length: 6
Name Server: ns3.baidu.com #权威名称服务器
baidu.com: type NS, class IN, ns ns4.baidu.com #资源记录部分
Name: baidu.com
Type: NS (authoritative Name Server) (2)
Class: IN (0x0001)
Time to live: 5
Data length: 6
Name Server: ns4.baidu.com #权威名称服务器
baidu.com: type NS, class IN, ns ns2.baidu.com #资源记录部分
Name: baidu.com
Type: NS (authoritative Name Server) (2)
Class: IN (0x0001)
Time to live: 5
Data length: 6
Name Server: ns2.baidu.com #权威名称服务器
其中,Name 的值为 baidu.com,表示 DNS 请求的域名为 baidu.com;类型为 NS,表示要获取该域名的权威名称服务器名称。Name Server 的值显示了该域名对应的权威名称服务器名称。这里总共获取到 5 个,如 ns7.baidu.com。
附加信息区域字段的资源记录部分信息如下:
Additional records #“附加信息区域”字段
dns.baidu.com: type A, class IN, addr 202.108.22.220 #资源记录部分
Name: dns.baidu.com #“权威名称服务器”名称
Type: A (Host Address) (1) #类型字段, 这里为A类型
Class: IN (0x0001)
Time to live: 5
Data length: 4
Address: 202.108.22.220 #“权威名称服务器”的IP地址
ns2.baidu.com: type A, class IN, addr 61.135.165.235 #资源记录部分
Name: ns2.baidu.com #“权威名称服务器”名称
Type: A (Host Address) (1) #类型字段, 这里为A类型
Class: IN (0x0001)
Time to live: 5
Data length: 4
Address: 61.135.165.235 #“权威名称服务器”的IP地址
ns3.baidu.com: type A, class IN, addr 220.181.37.10 #资源记录部分
Name: ns3.baidu.com #“权威名称服务器”名称
Type: A (Host Address) (1) #类型字段, 这里为A类型
Class: IN (0x0001)
Time to live: 5
Data length: 4
Address: 220.181.37.10 #“权威名称服务器”的IP地址
ns4.baidu.com: type A, class IN, addr 220.181.38.10 #资源记录部分
Name: ns4.baidu.com #“权威名称服务器”名称
Type: A (Host Address) (1) #类型字段, 这里为A类型
Class: IN (0x0001)
Time to live: 5
Data length: 4
Address: 220.181.38.10 #“权威名称服务器”的IP地址
ns7.baidu.com: type A, class IN, addr 180.76.76.92 #资源记录部分
Name: ns7.baidu.com #“权威名称服务器”名称
Type: A (Host Address) (1) #类型字段, 这里为A类型
Class: IN (0x0001)
Time to live: 5
Data length: 4
Address: 180.76.76.92 #“权威名称服务器”的IP地址
其中,Name 的值为“权威名称服务器”名称,Type 的值为 A,表示获取域名对应的 IP 地址;Address 的值显示了所有获取到的权威名称服务器对应的 IP 地址。
例如,权威名称服务器名称 ns7.baidu.com 对应的 IP 地址为 180.76.76.92。
参考链接
https://www.jianshu.com/p/23cf03e98652
https://blog.51cto.com/369369/812889
https://blog.csdn.net/iknow_nothing/article/details/107115189?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242
http://c.biancheng.net/view/6457.html
https://blog.csdn.net/answer3lin/article/details/84638845