likes
comments
collection
share

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

作者站长头像
站长
· 阅读数 27

相信大家在面试中都遇到过这样一个问题:从用户在浏览器中输入url,到看到整个页面,中间经历了哪些过程?

不知道大家还记得自己曾经的回答吗?记得的话,欢迎评论区留言讨论

然后,本文将旧事重提,鸟枪换炮的重新探究、深刻讨论从url到dom中间的爱恨情仇......

一句话介绍从url到dom的爱恨情仇

url--> dns --> tcp -->http --> html --> dom

一、请求阶段

1、url究竟是什么

URL是统一资源定位符(Uniform Resource Locator)的缩写,它是用来表示互联网上资源的地址,一个 URL 包含了资源的位置以及使用的协议。

它的组成如下:

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

一个标准的模式为:

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

2、dns解析之迷

DNS 是域名系统 (Domain Name System) 的缩写,是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。

主要作用

它的主要作用包括:域名解析、分布式数据库、域名层级结构管理、负载均衡、邮件路由、安全性

  • 域名解析

    • 将用户输入的域名(如 www.example.com)转换为对应的 IP 地址(如 192.0.2.1)。这种域名解析使得用户可以使用容易记忆的域名来访问互联网上的各种资源,而无需直接记住复杂的 IP 地址。
  • 分布式数据库

    • DNS 是一个分布式数据库系统,它通过多个 DNS 服务器来存储和管理域名与 IP 地址之间的映射关系。这种分布式架构提高了系统的可靠性和可扩展性。 前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段
  • 域名层级结构管理

    • 由于因特网的用户数量较多,所以因特网在命名时采用的是层次树状结构的命名方法。任何一个连接在因特网上的主机或路由器,都有一个唯一的层次结构的名字,即域名(domain name)。这里,域(domain)是名字空间中一个可被管理的划分。从语法上讲,每一个域名都是由标号(label)序列组成,而各标号之间用点(小数点)隔开。域名可以划分为各个子域,子域还可以继续划分为子域的子域,这样就形成了顶级域、主域名、子域名等。 前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段
    • 每个层的域名上都有自己的域名服务器,最顶层的是根域名服务器,每一级域名服务器都知道下级域名服务器的IP地址,以便于一级一级向下查询
  • 负载均衡

    • DNS中的负载均衡是dns服务器为同一台主机在配置IP地址上的一种负载均衡的技术。
    • 它的实现原理是在dns服务器中为同一个主机名配置多个IP地址,在应答DNS查询时,DNS服务器对每个查询都将以dns文件中主机记录的IP地址按顺序返回不同的解析结果,将客户端的访问引导到不同的机器上去,使得不同的客户端访问不同的服务器,从而达到负载均衡的目的
  • 邮件路由

    • DNS中的邮件路由是指通过 DNS (Domain Name System) 来确定电子邮件应该被传送到哪个邮件服务器的过程。当你发送一封电子邮件时,邮件客户端会查找目标邮件服务器的 MX 记录(一种记录类型,用于指定负责接收特定域名下邮件的邮件服务器),这些记录是存储在域名系统中的。MX 记录指定了负责接收该域名邮件的邮件服务器。邮件客户端使用这些记录将邮件传送到目标服务器,然后目标服务器将该邮件送达给目标用户。
  • 安全性

    • 域名劫持
      • 域名劫持是指攻击者篡改DNS查询结果,将用户重定向到恶意网站或服务器。这种攻击可能导致用户信息泄漏、欺诈和恶意软件感染。DNSSEC(DNS Security Extensions)是一种用于确保DNS数据完整性和认证性的安全扩展,通过数字签名来验证DNS查询结果的真实性,防止了域名劫持攻击。
    • DNS缓存投毒
      • DNS缓存投毒是指攻击者在本地DNS服务器注入恶意的DNS记录,使得用户在进行DNS查询时被重定向到恶意网站。DNSSEC可以有效防止这种攻击,因为它能够验证查询结果的真实性,确保用户接收到的DNS数据是可信的。
    • 反射型和放大型DDos攻击
      • DNS服务器常常成为分布式拒绝服务(DDos)攻击的目标。反射型和放大型攻击利用DNS协议的特性,通过向具有欺骗性的DNS请求发送大量数据包,使得受害者服务器过载。DNSSEC以及DNS服务器的防护机制能够减轻这类攻击对DNS基础设施的影响。
    • 域名劫持和欺诈
      • 攻击者可能通过非法手段获取域名的控制权,并将其指向恶意服务器。域名注册商通常提供额外的安全功能,包括域名锁定和域名验证,以帮助防止域名被劫持。
    • 隐私保护
      • DNS查询可能包含敏感信息,如用户访问的网站。DNS over HTTPS(DoH)和DNS over TLS(DoT)是通过加密DNS查询流量来保护用户隐私的方法。这些技术将DNS查询数据包装在加密通道中,防止中间人攻击和网络监听器获取用户浏览信息。

DNS的解析过程

dns查询的结果通常会在本地域名服务器中进行缓存,如果本地域名服务器中有缓存,则直接从本地域名服务器中返回解析结果,如果没有则如下:

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

DNS术语

  • 递归查询
    • DNS服务器在收到用户发起的请求时,必须向用户返回一个准确的查询结果。如果DNS服务器本地没有存储与之对应的信息,则该服务器需要询问其他服务器,并将返回的查询结果提交给用户
  • 迭代查询
    • DNS服务器在收到用户发起的请求时,并不直接回复查询结果,而是告诉另一台DNS服务器的地址,用户再向这台DNS服务器提交请求,这样依次反复,直到返回查询结果
  • DNS缓存
    • DNS缓存是将解析数据存储在靠近发起请求的客户端的位置,也可以说DNS数据是可以缓存在任意位置,最终目的是以此减少递归查询过程,可以更快的让用户获得请求结果
  • TTL
    • Time To Live,这是告诉本地域名服务器,域名解析结果可缓存的最长时间,缓存时间到期后本地域名服务器则会删除该解析记录的数据,删除之后,如有用户请求域名,则会重新进行递归查询/迭代查询的过程
  • IPV4、IPV6双栈
    • 双栈英文Dual IP Stack,就是在一个系统中可同时使用IPv6/IPv4这两个可以并行工作的协议栈。
  • DNS Query Flood Attack
    • 指域名查询攻击,攻击方法是通过操纵大量傀儡机器,发送海量的域名查询请求,当每秒域名查询请求次数超过DNS服务器可承载的能力时,则会造成解析域名超时从而直接影响业务的可用性。
  • URL转发
    • Url Forwarding,也可称地址转向,它是通过服务器的特殊设置,将一个域名指向另一个已存在的站点
  • eds-client-subnet
    • google提交了一份DNS扩展协议,允许DNS resolver传递用户的IP地址给权威域名服务器
  • DNSSEC
    • 域名系统安全扩展(DNS Security Extensions),简称DNSSEC。它是通过数字签名来保证DNS应答报文的真实性和完整性,可有效防止DNS欺骗和缓存污染等攻击,能够保护用户不被重定向到非预期的地址,从而提高用户对互联网的信任

3、三握手、四挥手的纠缠不清

为什么需要TCP

IP协议是无连接的:IP并不维护任何关于后续数据报的状态信息,每个数据报的处理相互独立。这种无连接的优点是不占用线路,降低了对网络线路的要求。

IP协议是不可靠的:不能保证IP数据报能成功到达目的地,是一种尽力而为的传输服务,路由器对IP报文出现错误的处理方式是丢包,并发送ICMP(Internet Control Message Protocol,互联网控制协议)控制消息给源地址。

因此,需要上层TCP来建立连接和差错重传,实现面向连接的、可靠的、基于字节流的传输层通信协议。

TCP的报文格式

TCP的三次握手和四次挥手需要依赖TCP报文中的一些字段: 前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

  • 源端口(Source Port)和目的端口(Destination Port)
    • 分别指发送端的应用程序使用的端口号以及接收端的应用程序期望接收的端口号。
    • 源端口和目标端口,加上IP首部里的源IP目标IP,就可以确定一个唯一连接
    • 长度各为16位,即2个字节,
    • 计算机的端口范围为[1,2^16 - 1],即为[1,65535], 0和65536不使用。
  • 序列号(Sequence Number)
    • 用来解决网络包乱序问题,实现可靠的数据传输和流量控制。
    • 在建立连接时,由计算机生成的随机数作为其初始值ISN,initial Sequence Number,初始序列号),通过SYN包传给接收端主机,每发送一次数据,就累加一次该数据字节数的大小。
    • 范围为[0,2^32 -1],即为[0,4294967295],当序列号增加到最大值后,下一个序列号将从0重新开始。
  • 确认号(Acknowledgment Number)
    • 只有在ACK标志位被设置时才有效,它指示期望接收的下一个字节的序列号(所以该字段一般都是上次接收成功的数据字节序号加1),用于确认已经成功接收的数据。
    • 如果ISN的初始值为X,那么确认号的范围就是[X+1,X+1+N-1],其中N表示已经成功接收的字节数。
    • 发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收
    • 确认号长度为32位,范围是[0,2^32-1],也就是[0,4294967295]
  • 数据偏移(Data Offset)
    • 指TCP报文的数据起始处距离TCP报文起始处的距离有多远,以4字节为单位计算出数据段开始地址的偏移值。
    • 没有选项时,该值为5,即20字节;且该值能表示的最大整数是15,也就说明TCP报文里数据开始的位置距离报文起点是60个字节(4*15)。这意味着TCP的首部长度是20-60个字节。
  • 保留(Reserved)
    • 保留供将来使用,目前设置为0
    • 长度为3位
  • 控制标志(Flags)
    • 长度为9位,用于控制和管理TCP连接。各控制标志位说明如下:
      • NS(Nonce Sum) :用于支持一种称为ECN-nonce的TCP扩展机制,该机制用于增加拥塞控制的安全性,防止拥塞控制信息被恶意篡改。
      • CWR(Congestion Window Reduced) :用于指示发送方减小拥塞窗口(Congestion Window)的大小。CWR标志位通常与拥塞控制机制一起使用,以应对网络拥塞的情况。
      • ECE(ECN-Echo) :ECE标志被设置表示发送方支持显式拥塞通知(Explicit Congestion Notification, ECN)机制,并请求接收方通知其关于网络拥塞的情况。接收方在收到设置了ECE标志的TCP报文段后,如果网络出现拥塞,则可以在回复的TCP报文段中设置ECN-Echo标志作为响应。通过使用ECE标志和ECN-Echo回复,TCP连接的发送方和接收方可以共同协调拥塞控制,以提高网络的性能和稳定性。
      • URG(Urgent) :指示报文段中包含紧急数据。当URG=1时,表明开启了urgent mode,通知接收方在处理数据时要特别注意紧急数据的处理。URG标志位的设置与紧急指针字段(Urgent Pointer)一起使用。
      • ACK(Acknowledgment) :指示确认号字段有效。仅当ACK=1时确认号字段才有效,当ACK=0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。
      • PSH(Push) :指示接收方应立即将数据推送给应用程序,而不是等待缓冲区填满。当两个应用进程进行交互式的通信时,有时一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。
      • RST(Reset) :用于复位连接,中断当前的通信。当 RST=1 时,表示 TCP 连接中出现异常(如主机崩溃或其他原因)必须强制断开连接,然后再重新建立连接进行传输。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。
      • SYN(Synchronize) :用于建立连接,发起连接请求。在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。
      • FIN(Finish) :用于关闭连接,请求终止连接。当FIN=1时,表示发送方没有数据要传输了,要求释放连接。
  • 窗口大小(Window Size)
    • 指示接收方的接收窗口大小,用于流量控制。
    • 窗口指的是发送本报文段一方的接受窗口(而不是自己的发送窗口)。
    • 窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。
    • 长度为16位,最大的窗口大小为2^16-1=65535=64k,但是对于现在的网络应用,可以在选项里加一个窗口扩大选项,来传输更多的数据。
  • 校验和(Checksum)
    • 用于检测TCP报文段是否在传输过程中发生了错误。校验和计算包括报头数据
    • 长度为16位
  • 紧急指针(Urgent Pointer)
    • 它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。
    • 即使窗口为0时也可以发送紧急数据。
    • 只有在URG标志位被设置时才有效。
    • 长度为16位
  • 选项(Options)
    • 选项字段用于提供额外的功能和控制,每个选项的开始是 1 字节的kind字段,说明选项的类型。
    • 可选字段,长度可变,最长可达40个字节。当没有使用“选项”时,TCP的首部长度是20字节。
    • 一些常见的选项举例如下:
      • 最大报文段长度(Maximum Segment Size, MSS) :占用4字节,通常在创建连接而设置SYN标志的数据包中指明这个选项,指明本端所能接收的最大长度的报文段。通常将MSS设置为(MTU-40)字节,携带TCP报文段的IP数据报的长度就不会超过MTU(MTU最大长度为1518字节,最短为64字节),从而避免本机发生IP分片。只能出现在同步报文段中,否则将被忽略。
      • 窗口扩大因子(Window Scale Factor) :占用3字节,取值0-14。用来把TCP的窗口的值左移的位数,使窗口值乘倍。只能出现在同步报文段中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节。
      • 时间戳选项(TCP Timestamps Option, TSopt) :占用10字节,其中最主要的字段是时间戳字段(Timestamp Value field, TSval, 4字节)和时间戳回送回答字段(Timestamp Echo Reply field, TSecr, 4字节)。时间戳选项允许通信的两端在TCP报文段中包含时间戳值,以便进行一些时间相关的操作和计算。
      • 安全摘要选项(TCP Authentication Option, TCP Option) :用于提供数据完整性和身份验证的功能。该选项用于对TCP报文段进行保护,防止数据篡改和未经授权的访问。

TCP三次握手

  • 第一次握手

    • 客户端发起连接请求,向服务器发送一个SYN(同步)报文段,段中包含了目的端口本机端口
    • 设置SYN=1
    • 设置seq(初始序列号)为一个随机选择的x,即seq=x,如果是第一个连接,很可能是0
    • 此时服务器对应的端口要处于监听状态,客户端发起请求后进入SYN_SENT 状态,等待服务器的确认。
  • 第二次握手

    • 服务端收到客户端发来的 SYN 报文段,对这个SYN报文段进行确认
    • 服务器客户端发送一个SYN-ACK报文段作为回应,报文段中的标志位设置为SYN=1ACK=1,表示同时作为确认和同步;
    • seq设置为服务器的随机选择的初始序号y(服务端的TCP段序号),即seq=y
    • ack(确认号字段)设置为客户端的初始序号加1,即ack=x+1
    • 服务器端将上述所有信息放到一个TCP段(即SYN+ACK段)中,一并发送给客户端,此时服务器进入SYN_RECV状态。
  • 第三次握手

    • 客户端接收到服务端发来的SYN+ACK 报文段后,要向服务端发送一个ACK(确认)报文段,对连接请求的确认进行确认。
    • 报文段中的标志位设置为ACK=1
    • 确认号字段设置为服务器的初始序号加1,即ack=y+1
    • 序号字段设置为客户端的初始序号加1,即seq=x+1
    • 此时客户端进入 ESTABLISHED(已连接)状态
    • 服务端接收到TCP段,也将进入ESTABLISHED状态
    • 三次握手结束,连接成功建立。

三次握手完成之后,TCP连接就正式建立起来了,双方可以开始进行数据的可靠传输。三次握手的目的是确保双方的初始序号和确认号的同步,并验证双方的可达性。通过这个过程,TCP可以建立一个可靠的双向通信通道,在后续的数据传输中保证数据的可靠性和顺序性。

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

TCP四次挥手

  • 第一次挥手

    • 客户端数据发送完成,则向服务端发送连接释放请求的FIN报文(请求连接终止:FIN=1),主动关闭TCP连接。
    • 报文中会指定一个序列号seq=u,并停止再发送数据,但依然能够接收数据。
    • 此时客户端处于 FIN_WAIT_1 状态,等待服务端确认(TCP规定,FIN报文即使不携带数据,也要消耗一个序号)。
  • 第二次挥手

    • 服务端收到FIN报文之后,通知相应的高层应用进程,告诉它客户端向服务端这个方向的连接已经释放了。
    • 服务端向客户端发出连接释放的应答ACK报文,并进入了CLOSE_WAIT(关闭等待)状态。ACK报文头包含:ACK=1ack=u+1,并且带上自己的序列号seq=v。这里ack=u+1第一次挥手的序列值+1,表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。
    • 客户端收到服务端的确认后,进入 FIN_WAIT_2 状态,等待服务端发出的连接释放报文段。
    • 前两次挥手既让服务端知道了客户端想释放连接,也让客户端知道了服务端已了解自己想要释放连接的请求。
  • 第三次挥手

    • 如果服务端也想断开连接,就向客户端发送连接释放报文。由于在CLOS_WAIT状态,服务端很可能又发送了一些数据,假定此时连接释放报文的序列号为seq=wack也是取第一次挥手的seq+1,即ack=u+1,这和第二次挥手时是一样的。
    • 此时服务端就进入了LAST_ACK(最后确认)状态,等待客户端的确认,并停止向客户端发送数据,但服务端仍能够接收从客户端传输过来的数据。
  • 第四次挥手

    • 客户端收到服务器的连接释放报文后,一样发送一个 ACK 报文作为应答(ack=w+1seq=u+1)
    • 此时客户端处于TIME_WAIT(时间等待)状态,并在这个状态等待 2MSL(Two Maximum Segment Lifetime, 最大报文生存时间)。
    • 服务端收到从客户端发出的 TCP 报文之后结束 LAST-ACK 阶段,进入 CLOSED 阶段。
    • 客户端等待完 2MSL之后,结束 TIME-WAIT 阶段,进入 CLOSED 阶段。
    • 由此完成四次挥手

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

问题

为什么是三次握手而不是两次

三次握手的本质是服务端和客户端进行约定,建立连接的过程,那为啥需要三次,而不是两次呢,就比如你和女朋友去约会:

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段 那如果没有第三条消息,你女朋友不会觉得去看电影这件事情的约定环节已经完成。 再比如说你真的中间有啥事情,会是这样的情况:

前端必知之:从url到dom的中间爱恨情仇——系列一:请求阶段

这样你们都知道彼此去不了了,不会让你女朋友在电影院门口白白等了好久,不会出现消息不同步的情况。

那么服务端和客户端建立连接三次也是同理:第一次握手,客户端将SYN报文发送到服务器,服务器接收到报文后,即可确认客户端到服务器是可达的第二次握手,服务端向客户端发送响应的SYNACK报文,客户端接收到后,即可确认服务器到客户端也是可达的第三次握手,客户端向服务端发送ACK报文,服务端接收到后,即可确认可以开始数据传输了。

如果没有第三次握手,那么有一种情况:

当客户端第一次握手发送了SYN报文,由于各种突发原因(网络延迟过长等),客户端没有接收到服务端返回到的SYNACK的报文,会以为SYN报文丢失,然后再发一次,这次成功接收到了返回SYNACK报文,然后建立连接,传送数据。那此时第一次发送的SYN报文这时候又到了,服务器会以为这是一次新的tcp连接,不知道已经建立过了,然后又开始建立,直到因为太久没有接收到数据而释放。这种情况太浪费资源了,所以需要第三次握手,进一步的确认。

客户端为啥要等待一段时间再释放资源

为啥四次挥手之后,客户端还需要等2MSL之后,才释放资源呢?

  • 原因一
    • 客户端接收到服务器发送的FIN报文后(第三次挥手),会回送一条确认报文(第四次挥手),但是,客户端并不知道这条确认报文是否可以顺利到达服务器。若这条确认报文在传送到服务器的过程中损坏、丢失或超时,将引起服务器重新发送FIN报文,客户端接收到后,将需要再次发送一条确认报文,直到服务器正确接收。但是,客户端发送确认报文后,立刻释放资源,将导致无法处理重传的FIN报文,所以客户端需要等待一段时间,直到确认没有出现上述情况出现再释放资源。
  • 原因二
    • TCP四次挥手完成后,理论上已经断开了连接,但是这不代表之前通过这条连接发送的所有数据都处理完毕了,有些可能还在网络中传输。若在四次挥手后,立即释放客户端的资源,然后客户端立即以同一个源端口,向服务器的同一个目的端口再次建立一个TCP连接,这个连接和上一个的 源端口+源IP+目的端口+目的IP 都一模一样,此时将会产生问题。若上一次连接遗留在网络中的报文此时到达,将会被当做新连接传输的数据处理,于是可能会产生一些不可预估的错误。所以,客户端在断开连接后,需要等待一段时间,直到网络中遗留的数据都死掉,才释放资源,而在资源没有被释放前,是不允许建立一个 源端口+源IP+目的端口+目的IP 都一模一样的TCP连接的(因为TCP套接字由这四部分标识)。

断开连接为啥需要四次挥手

因为TCP连接是全双工的:A与B建立连接,则A可以向B发送数据,而B也可以向A发送数据。

我们知道断开连接的请求什么时候发起?当然就是在不再有数据需要发送时。我们依旧以客户端向服务器断开连接为例。假设客户端和服务器建立了一个TCP连接,在客户端需要向服务器发送的数据都发送完后,客户端就可以向服务器发送一个FIN报文段,请求断开连接;服务器接收到后,将会回送一个ACK报文,告诉客户端,自己已经收到了它断开连接的请求。若只有两次挥手,这个时候连接就算是断开了。但是这样真的合理吗?答案当然是否定的。   客户端发送完数据后,告诉服务器,我没有数据了,可以和你断开,但是不代表服务器没有数据需要发送到客户端了呀。TCP是一个全双工的连接,代表服务器也有可能有数据需要发送到客户端。所以,只有当两端的数据都发送完毕,连接才能安全的断开。因此,服务器接收到了客户端的FIN报文段,他会等到自己所有的数据发送完,然后也向客户端发送一个FIN报文,告诉客户端我也没数据了,这时候连接才能真正断开,两端各自释放资源。

下一期预告

  • 二、响应阶段
    • 1、响应回来的是啥
    • 2、缓存

引用

阿里云- DNS的基本概念是什么

TCP报文基础+三次握手和四次挥手

计算机网络————TCP的三次握手与四次挥手(超详细)

转载自:https://juejin.cn/post/7363491538289606694
评论
请登录