这里是《写给前端工程师的 HTTP 系列》,记得有位大佬曾经说过:“大厂前端面试对 HTTP 的要求比 CSS 还要高”,因而可知 HTTP 的重要程度不可小视。文章写做计划以下,视状况可能有必定的删减,本篇是该系列的第 1 篇 —— 《从 TCP/UDP 到 DNS 解析》。html
更多文章可关注个人 interview 系列。前端
从 TCP/UDP 到 DNS 解析nginx
HTTP 协议那些事git
Web 服务器 (nginx/caddy)github
HTTPS (对称加密/非对称加密/SSL)面试
JWT (我本身博客的后台用到了,因此专门要写一篇文章)算法
SPDY / HTTP/2 / Websockets编程
网络攻击segmentfault
跨域跨域
缓存机制
浏览器原理
终章:从输入 url 到页面呈现发生了什么
HTTP/0.9
标准于 1990 年问世,由于当时的 HTTP 没有做为正式的标准被确立,该版本含有 HTTP/1.0 以前版本的意味。
HTTP/1.0
标准于 1996 年 5 月做为第一份标准被公布,它被记载于 RFC1945 - Hypertext Transfer Protocol -- HTTP/1.0
HTTP/1.1
标准于 1999 年 6 月被公布,截止到目前它应该是最主流的 HTTP 协议版本,它被记载于 RFC2616 - Hypertext Transfer Protocol -- HTTP/1.1
HTTP/2
标准于 2015 年 5 月被正式发布,它被记载于 RFC7540 - Hypertext Transfer Protocol -- HTTP/2,它的特色是 ① 采用二进制而非明文来打包,② 多路复用,③ 修复队头堵塞,④ 容许设置设定请求优先级,⑤ 服务器推送,⑥ WebSocket 等等。
据 w3techs 统计,截止到 2019/04/22,HTTP/2 的全球占有率为 36%。个人 我的博客 在上线之初就支持了 HTTP/2。
在讲解 TCP/IP 通讯传输流以前,首先复习一下 TCP/IP 的五层协议。
应用层:决定向用户提供应用服务时通讯的活动。TCP/IP 协议族内预存了各种通用的应用服务。好比:FTP、DNS、HTTP 协议。
传输层:传输层对上层应用层,提供处于网络链接中的两台计算机之间的数据传输。在传输层有两个性质不一样的协议,分别是 TCP (Transmission Control Protocol,传输控制协议) 和 UDP (User Data Protocol,用户数据报协议)
网络层:网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了经过怎样的路径到达对方计算机,并把数据包传送给对方。与对方计算机经过多台计算机或网络设备进行传输时,网络层所起的做用就是在众多的选项内选择一条传输路线。
数据链路层: 在物理层提供比特流服务的基础上,创建相邻结点之间的数据链路,经过差错控制提供数据帧 (Frame)在信道上无差错的传输,并进行各电路上的动做系列。数据的单位称为帧 (frame)
物理层:物理层创建在物理通讯介质的基础上,做为系统和通讯介质的接口,用来实现数据链路实体间透明的比特 (bit) 流传输。只有该层为真实物理通讯,其它各层为虚拟通讯。
客户端在应用层 (HTTP 协议) 发出一个 HTTP 请求。
为了方便传输,在传输层 (TCP 协议) 把从应用层处收到的数据 (HTTP 请求报文) 进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
在网络层 (IP 协议),增长做为通讯目的地的 MAC 地址后转发给数据链路层。这样,发送给服务端的请求就准备齐全了。
当服务端在链路层接收到数据时,按序往上层发送,一直到应用层。当传输到应用层时,才算真正的接收到由客户端发送过来的请求。
媒体访问控制地址 (Media Access Control Address),也称为局域网地址 (LAN Address),以太网地址 (Ethernet Address) 或物理地址 (Physical Address),它是一个用来确认网上设备位置的地址。ARP (Address Resolution Protocol) 是一种用来解析地址的协议,它能够根据 IP 地址反查出对应的 MAC 地址。
下图展现了一台电脑内网 IP 和 MAC 地址。在终端 (MAC OS 环境) 输入 ifconfig
,找到 en0
,即可查找本地以太网的信息。
那么什么是 MAC 地址呢?咱们知道 IP 地址是可变的,能够经过各类方式分配 IP 地址给一个设备,好比 DHCP, PPP,静态 IP 等。而 MAC 地址通常来说是不会变的,设备在生产时就被“烙”上了 惟一的标识
,这个 惟一的标识
就是 MAC 地址。
逼乎上有个颇有趣的例子:你中午在公司点了份外卖,收货地址必定是写公司的地址;晚上回到家,再点外卖时就得把地址写成家 (IP 是动态的)。但不管在哪儿点外卖,订单上的姓名和手机号必定是你本身的 (MAC 地址)。
中午外卖小哥把午饭送到公司门口,但收外卖的人确定不止你一个 (多台设备在同一个 broadcast 网络里),所以他会经过手机号和姓名来找到你。
用户数据报协议 (User Datagram Protocol),又称使用者资料包协议,是一个简单的面向数据报的传输协议。在 TCP/IP 模型中,UDP 为网络层以上和应用层如下提供了一个简单的接口。UDP 只提供数据的 不可靠传递
,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。UDP 在 IP 数据报的头部仅仅加入了复用和数据校验 (字段)。
它的特色以下:
UDP 缺少可靠性。UDP 自己不提供确认序号,序列号,超时重传等机制。UDP 数据报可能在网络中被复制,被从新排序。即 UDP 不保证数据报会到达其最终目的地,也不保证各个数据报的前后顺序,也不保证每一个数据报只到达一次。
UDP 头部开销小,它包含如下几个数据:
两个十六位的端口号,分别是源端口和目的端口。
整个数据报文的长度。
整个数据报文的校验和,用于发现头部信息和数据中的错误
UDP 是面向无链接的。UDP 客户端和服务器以前没必要存在长期的关系。UDP 发送数据报以前也不须要通过握手建立链接的过程。
UDP 不只支持单播,还支持多播和广播。
基于 UDP 协议的有:
域名系统 (DNS)
简单网络管理协议 (SNMP)
动态主机配置协议 (DHCP)
路由信息协议 (RIP)
自举协议 (BOOTP)
简单文件传输协议 (TFTP)
TCP (Transmission Control Protocol, 传输控制协议) 是一种面向链接的、可靠的、基于字节流服务的传输层通讯协议,由 IETF 的 RFC 793 定义。其中字节流服务 (Byte Stream Service) 是指为了方便传输,将大块数据分割成以报文段 (segment) 为单位的数据包进行管理。
TCP 提供一种面向链接的、可靠的字节流服务
在一个 TCP 链接中,仅有两方进行彼此通讯。广播和多播不能用于 TCP
TCP 使用校验和,确认和重传机制来保证可靠传输
TCP 给数据分节进行排序,并使用累积确认保证数据的顺序不变和非重复
TCP 使用滑动窗口机制来实现流量控制,经过动态改变窗口的大小进行拥塞控制
端口号:包括源端口号和目的端口号,用来标识同一台计算机的不一样的应用进程。TCP 报头中的源端口号和目的端口号同 IP 数据报中的源 IP 与目的 IP 惟一肯定一条 TCP 链接。
源端口号:源端口和 IP 地址的做用是标识报文的返回地址。
目的端口号:目的端口指明接收方计算机上的应用程序接口。
序号:它是当前报文段发送的数据组的第一个字节的序号。在 TCP 传送的流中,每个字节一个序号。好比一个报文段的序号为 300,此报文段的数据部分共有 100 字节,则下一个报文段的序号为 400。序号 确保了 TCP 传输的有序性。
确认号:即 ACK(acknowledgement),指明下一个期待收到的字节序号,代表该序号以前的全部数据已经正确无误的收到。确认号只有当 ACK 标志为 1 时才有效。好比创建链接时,SYN 报文的 ACK 标志位为 0。
首部长度:因为首部可能含有可选项内容,所以 TCP 报头的长度是不肯定的,报头不包含任何任选字段则长度为 20 字节,4 位首部长度字段所能表示的最大值为 1111,转化为 10 进制为 15,15*32/8 = 60,故报头最大长度为 60 字节。首部长度也叫数据偏移,是由于首部长度实际上指示了数据区在报文段中的起始偏移值。
保留:为未来定义新的用途保留,如今通常置 0。
控制位 | 说明 |
---|---|
URG | 紧急指针标志,为 1 时表示紧急指针有效,为 0 则忽略紧急指针。 |
ACK (acknowledgement) | 确认序号标志,为 1 时表示确认号有效,为 0 表示报文中不含确认信息,忽略确认号字段。 |
PSH | push 标志,为 1 表示是带有 push 标志的数据,指示接收方在接收到该报文段之后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。 |
RST | 重置链接标志,用于重置因为主机崩溃或其余缘由而出现错误的链接。或者用于拒绝非法的报文段和拒绝链接请求。 |
SYN (synchronize) | 同步序号,用于创建链接过程,在链接请求中,SYN=1 和 ACK=0 表示该数据段没有使用捎带的确认域,而链接应答捎带一个确认,即 SYN=1 和 ACK=1。 |
FIN (Finish) | finish 标志,用于释放链接,为 1 时表示发送方已经没有数据发送了,即关闭本方数据流。 |
窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小是一个 16bit 字段,所以窗口大小最大为 65535。
校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。
紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另外一端发送紧急数据的一种方式。
选项和填充:最多见的可选字段是最长报文大小,又称为 MSS (Maximum Segment Size),每一个链接方一般都在通讯的第一个报文段 (为创建链接而设置 SYN 标志为 1 的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不必定是 32 位的整数倍,因此要加填充位,即在这个字段中加入额外的零,以保证 TCP 头是 32 的整数倍。
数据部分:TCP 报文段中的数据部分是可选的。在一个链接创建和一个链接终止时,双方交换的报文段仅有 TCP 首部。若是一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多状况中,也会发送不带任何数据的报文段。
emmmmm,单身久了,看三次握手都那么眉清目秀。
所谓三次握手 (three-way handshaking) 是指创建一个 TCP 链接时,须要客户端和服务端共发送三个包。它的目的是链接服务器指定端口,创建 TCP 链接,并同步双方的 序列号
和 确认号
,交换 TCP 窗口大小信息。在 socket 编程中,当客户端执行 connect() 函数时,将触发三次握手。
第一次握手
客户端首先发送一个 SYN 为 1 的包给服务端,指明客户端要链接服务端的哪一个接口以及初始序号 x。发送完毕后,客户端进入 SYN_SEND
状态。
第二次握手
服务端收到后,回传一个带有 SYN/ACK 的确认包以示应答。即 SYN=1,ACK=1。服务端选择本身的 ISN 序列号,放到 seq 中,同时将确认序号 ack 设置为客户端的 ISN+1,即 x+1。发送完毕后,服务端进入 SYN_RCVD (同步收到)
状态。
第三次握手
客户端收到确认后,再次发送一个带 ACK 标志的数据包。即 ACK=1,ack=y+1,并将本身的序列号 seq=x+1。发送完毕后,客户端和服务器双双进入 ESTABLISHED
状态。至此,三次握手结束。
TAT,好虐。
第一次挥手
客户端调用 close() 函数,并发送一个 FIN (finish) 标志为 1 的数据包给服务端,来表示本方的数据已经所有发送完毕,此时客户端进入 FIN_WAIT_1
状态。TCP 规定,FIN 报文段即便不携带数据,也要消耗一个序号。
第二次挥手
服务端收到客户端的释放报文后,发出确认报文,其中 ACK=1, ack=u+1,而且带上本身的序列号 seq=v。代表本身接受到了客户端关闭链接的请求,但还没准备好关闭链接 (半关闭状态),也就是说客户端已经没有数据要发送了,但服务端仍有可能会发送数据。发送完毕后,服务端进入 CLOSE_WAIT
状态。
当客户端收到该报文后,客户端就进入 FIN-WAIT-2
状态,等待服务器发送链接释放报文。
第三次挥手
服务器端准备好关闭链接时,会向客户端发送一个链接释放报文,其中 FIN=1,ack=u+1。因为在半关闭状态,服务器极可能又发送了一些数据,假定此时的序列号为 seq=w。发送完毕后,服务端便进入 LAST-ACK
(最后确认) 状态。
第四次挥手
客户端收到服务器的链接释放报文后,须要发送一个确认包,其中 ACK=1,ack=w+1,而本身的序列号是 seq=u+1,并进入了 TIME-WAIT
(时间等待)状态,等待过程可能出现的要求重传的 ACK 包。
此时 TCP 链接尚未释放,必须通过 2 * MSL (Maximum Segment Lifetime, 最长报文段寿命) 时间后,当客户端撤销相应的 TCB 后,才会进入 CLOSED
状态。
而服务器只要收到了客户端发出的确认,当即进入 CLOSED
状态。一样,撤销 TCB 后,就结束了此次的 TCP 链接。所以,服务器结束 TCP 链接的时间要比客户端早一些。
UDP | TCP | |
---|---|---|
是否链接 | 无链接 | 面向链接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
链接对象个数 | 支持一对一,一对多,多对一和多对多交互通讯 | 只能是一对一通讯 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅 8 字节 | 首部最小 20 字节,最大 60 字节 |
适用场景 | 适用于实时应用 (IP 电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
请求报文和 DNS 服务器返回的应答报文都是统一的格式:
会话标识 (2 字节):它是 DNS 报文的 ID 标识,对于请求报文和其对应的应答报文,这个字段是相同的,经过它能够区分 DNS 应答报文是哪一个请求的响应。
标志 (2 字节):它有 8 个部分,以下图所示:
字段 | 说明 |
---|---|
QR (1bit) | 查询/响应标志,0 为查询报文,1 为响应报文 |
opcode (4bit) | 0 表示标准查询,1 表示反向查询,2 表示服务器状态请求,3-15 是保留值 |
AA (1bit) | 表示受权回答,该字段在应答的时候才有意义,指出给出应答的服务器是查询域名的受权解析服务器; |
TC (1bit) | 表示可截断的,用来指出报文比容许的长度还要长,致使被截断 |
RD (1bit) | 表示指望递归,该字段被请求设置,应答的时候使用的相同的值返回。若是设置了 RD,就建议域名服务器进行递归解析,递归查询的支持是可选的 |
RA (1bit) | 表示可用递归,该字段在应答中设置或取消,用来表明服务器是否支持递归查询 |
RCODE (4bit) | 应答码,0 表示没有差错,3 表示名字差错,2 表示服务器错误 |
Z | 保留值 |
Questions 查询字段
QNAME 无符号 8bit 为单位长度不限表示查询名。
QTYPE 无符号 16bit 整数表示查询的协议类型。
QCLASS 无符号 16bit 整数表示查询的类。
Answer/Authority/Additional
三者的格式相同,以下所示:
NAME 资源记录包含的域名。
TYPE 表示 DNS 协议的类型。
CLASS 表示 RDATA 的类。
TTL 表示资源记录能够缓存的时间。0 表明只能被传输,可是不能被缓存。
RDLENGTH 表示 RDATA 的长度。
RDATA 不定长字符串来表示记录,格式根 TYPE 和 CLASS 有关。好比,TYPE 是 A,CLASS 是 IN,那么 RDATA 就是一个 4 个字节的 ARPA 网络地址。
折腾过搭建网站的小伙伴们必定对 DNS 解析记录不会陌生,下面经过表格复习一下。
类型 | 助记词 | 说明 |
---|---|---|
1 | A | 由域名得到 IPv4 地址 (经常使用) |
2 | NS | 查询域名服务器 (经常使用) |
5 | CNAME | 设置域名别名 (经常使用) |
6 | SOA | 开始受权 |
11 | WKS | 熟知服务 |
12 | PTR | 把 IP 地址转换成域名 |
13 | HINFO | 主机信息 |
15 | MX | 邮件交换 (经常使用) |
28 | AAAA | 由域名得到 IPv6 地址 (经常使用) |
252 | AXFR | 传送整个区的请求 |
255 | ANY | 对所用记录的请求 |
系统会检查浏览器缓存中有没有这个域名对应的解析过的 IP 地址,若是缓存中有,这个解析过程就将结束。浏览器缓存是受这个域名的失效时间和缓存的空间大小控制的。
若是用户的浏览器缓存中没有,浏览器会查找操做系统缓存中即为本地的 Host 文件。
路由器也可能会有缓存。
若是前几步都没有找到,就会到 LDNS (Local DNS) 中查找,LDNS 是你的 ISP 分配给你的 DNS (通常为两个),大部分状况下域名都会在这里获得解析。下图是 cloudflare 提供给个人两个 DNS。
若是在 LDNS 没有找到,那就要去 Root Server 域名服务器请求解析了。根域名服务器返回给本地域名服务器一个 查询主域名服务器(gTLD Server)地址
。gTLD 是国际顶级域名服务器,如.com,.cn、.org 等,全球只有 13 台左右。
本地域名服务器会向 gTLD Server 地址发送请求,它会返回一个 Name Server 域名服务器的地址,这个 Name Server 一般就是你注册域名的厂家,好比 NameCheap、狗爹、万网等等。
Name Server 域名服务器会查询存储域名和 IP 的关系映射表,正常状况下均可以根据域名获得目标 IP 记录,并连同一个 TTL 返回给本地域名服务器。
本地域名服务器根据 TTL 缓存这个 IP,并将解析结果返回给客户端,客户端再根据 TTL 将 IP 信息缓存到本地系统缓存里。至此,域名解析过程结束。
CDN 全称为内容分发网络 (Content Delivery Network),它可以实时地根据网络流量和各节点的链接、负载情况以及到用户的距离和响应时间等综合信息将用户的请求从新导向离用户最近的服务节点上,以提升用户访问网站的相应速度。
通俗来说,本来用户访问的资源是存放在你本身的服务器,而如今访问的资源来自 CDN 缓存服务器。在实际操做中,咱们只须要将域名的 DNS 解析指向 CDN 服务商提供的域名服务器便可。
我一直在用 cloudflare 的免费版,很赞。它提供了 HTTP/2,IPV6,brotli (一种压缩算法,比 gzip 压缩率还要高的多)。
典型的 CDN 系统由下面两个部分组成:
分发服务系统的基元是 Cache 设备,它会同步源站点的内容并负责响应用户的访问请求,把缓存在本地的内容快速的提供给用户。
对发起请求的用户进行访问调度,肯定提供给用户的最终实际访问地址。该系统分为全局负载均衡 (GSLB) 和本地负载均衡 (SLB)。GBLB 主要根据“就近原则”,经过对每一个服务节点进行最优判断,向用户提供最合适的 Cache 设备。SLB 主要负责节点内部的设备负载均衡。
见上文。
为了方便传输,TCP 协议将大块数据分割成以报文段为单位的数据块进行管理。
当 TCP 发出一个报文段时,它会启动一个定时器,等待目的端确认收到这个报文段。若是不能及时收到一个确认,将重发这个报文段。
当收到来自另外一端的数据时,它会发送一个确认,但该确认不是当即发送的,之因此推迟,是要对包作完整校验。
TCP 经过检验 校验和
的方式来检测数据的准确性,当检测到数据出错后,会丢给客户端一个携带 NCK 标志的包,当客户端收到后会重现发送一遍数据包。
TCP 协议给每个字节设置一个序号,序号确保了 TCP 传输的有序性,当报文段出现失序的问题,TCP 会根据序号从新排序。
IP 数据报有可能会发生重复,TCP 接收端会丢弃重复的数据。
TCP 提供流量控制。TCP 链接的每一方都有固定大小的缓冲空间,TCP 的接收端只容许另外一端发送接收端缓冲区所能接纳的数据。TCP 使用的流量控制协议是可变大小的滑动窗口协议。
为了实现可靠数据传输,TCP 协议的通讯双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。三次握手的过程便是通讯双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤
若是只是两次握手,至多只有链接发起方的起始序列号能被确认,另外一方选择的序列号则得不到确认。
TCP 还设有一个保活计时器 (keep-alive),若是客户端出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会从新复位这个计时器,时间一般是设置为 2 小时,若两小时尚未收到客户端的任何数据,服务器就会发送一个探测报文段,之后每隔 75 秒发送一次。若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭链接。
见上文。
下一篇将对 HTTP 协议进行全面剖析,敬请期待。
欢迎关注个人微信公众号:进击的前端
《图解 HTTP》 -- 上野宣
[面试 ∙ 网络] TCP/IP (四):TCP 与 UDP 协议简介