作者:韦秉芮 @SIPC后端组
时间:2024-07-31
概述
计算机网络是所有计算机学者必知必会的基础,在各大厂面试中也是重点考察的知识点,本文将以后端开发的角度整理高优的知识点,精力有限的情况下,不得不考虑知识的性价比,有时间的同学理应更加系统的学习。我们以一个很经典的问题来引入话题:“当浏览器地址栏输入www.baidu.com
后发生了什么?”
知识点
DNS解析
以首次访问举例,DNS解析分为浏览器缓存、系统缓存、Hosts文件、路由器缓存、递归DNS服务器。
浏览器缓存
每个浏览器都有一个DNS缓存,记录了一些常用域名和IP地址的对应关系(有过期时间),当浏览器缓存没有命中时,浏览器会查询系统缓存。
如果你使用的是Chrome浏览器,可以在地址栏输入chrome://net-internals/#dns
来查询浏览器DNS缓存。
系统缓存
浏览器会进行一个系统调用,查询RAM中的DNS缓存。我们也可以使用nslooup
手动检查是否存在对应域名的缓存(Linux和MacOS适用),如果没命中缓存则会去查询Hosts文件。
Hosts文件
Hosts文件中是用户手动配置的域名与IP的映射,在许多公司的内网中都会对数据库、中间件或者其他IP敏感的服务配置对于域名映射。可以通过修改/etc/hosts
文件来进行自定义配置。
递归DNS服务器
Hosts文件没找到就会查询路由器缓存,路由器缓存没命中就会递归查询本地DNS服务器,其递归的过程其实就是对根域名、顶级域名、权威域名的递归查询过程。
而当本地DNS服务器找不到目标域名时(假设是baidu.com
),就会以迭代地方式依次访问根域名服务器,顶级域名服务器,权威域名服务器从而找到对应的IP地址。这个迭代过程具体而言就是本地服务器向根域名服务器查询baidu.com
,根域名服务器返回目标域名所在地区的顶级域名服务器IP地址,本地域名服务器再根据给定IP地址访问顶级域名服务器,以此迭代下去找到baidu.com
的IP地址。
我们尝试在终端模拟本地DNS服务器迭代查询目标域名(假设是bilibili.com
)的完整过程。其中我们只要用到dig
命令来进行DNS记录的查询。
这一步我们向根域名服务器找到了包含bilibili.com
的.com
顶级域名服务器地址。使用任意一个IP地址继续迭代查询bilibili.com
的IP地址。后续就不在演示了,因为这个过程并不真实,当我执行dig
命令时本地DNS服务器就直接命中缓存了,不再向根域名服务器进行查询。
建立连接
一旦获取到IP地址,浏览器会通过IP地址与目标服务器建立TCP连接,通常使用HTTP的默认端口80,或者HTTPS的443端口,而这个过程我们就叫做“TCP的三次握手”。
TCP报文
TCP是面向连接的可靠的全双工通信,其报文头格式如下:
相关字段含义如下(每个字段都十分重要),这些字段总和为20bytes,也就是TCP报文头的长度:
Name | Desc | Size (bits) |
---|---|---|
Source Port | 源端口 | 16 |
Destination Port | 目的端口 | 16 |
Sequence Number | 序列号,也就是我们熟知的seq,在TCP传输流中每一个字节都会按顺序编号,当`SYN=1`时,即为ISN(初始序列号),用于对序列号进行同步,第一个字节的序列号比这个值大1,也就是`ISN+1`,否则SYN就是当前数据分段的第一个字母的序列号。 | 32 |
Acknowledgment Number | 确认号,也就是我们熟知的ack,表示接收方期望收到下一个TCP报文段的第一个字节的序列号,所以通常是`seq+1`。 | 32 |
Header Length | 首部长度,也是数据偏移字段,数据偏移是指数据段中的“数据”部分起始处距离TCP数据段起始处的字节偏移量。其实这里的“数据偏移”也是在确定TCP数据段头部分的长度,告诉接收端的应用程序,数据从何处开始。 | 4 |
Reserved | 保留字段,为TCP将来的发展预留空间,目前必须全部为0。 - CWR(Congestion Window Reduce):拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率。 - ECE(ECN Echo):用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵。 - URG(Urgent):表示本报文段中发送的数据是否包含紧急数据。URG=1 时表示有紧急数据。当 URG=1 时,后面的紧急指针字段才有效。 - ACK:表示前面的确认号字段是否有效。ACK=1 时表示有效。只有当 ACK=1 时,前面的确认号字段才有效。TCP 规定,连接建立后,ACK 必须为 1。 - PSH(Push):告诉对方收到该报文段后是否立即把数据推送给上层。如果值为 1,表示应当立即把数据提交给上层,而不是缓存起来。 - RST:表示是否重置连接。如果 RST=1,说明 TCP 连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。 - SYN:在建立连接时使用,用来同步序号。当 SYN=1,ACK=0 时,表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时,表示对方同意建立连接。SYN=1 时,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才为 1。 - FIN:标记数据是否发送完毕。如果 FIN=1,表示数据已经发送完成,可以释放连接。 |
4 |
Window Size | 窗口大小,它表示从 Ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间。该字段可以用于 TCP 的流量控制。 | 16 |
TCP Checksum | 校验位,它用于确认传输的数据是否有损坏。发送端基于数据内容校验生成一个数值,接收端根据接收的数据校验生成一个值。两个值必须相同,才能证明数据是有效的。如果两个值不同,则丢掉这个数据包。Checksum 是根据 `伪头+TCP头+TCP数据` 三部分进行计算的。 | 16 |
Urgent Pointer | 紧急指针,仅当前面的 URG 控制位为 1 时才有意义。它指出本数据段中为紧急数据的字节数。当所有紧急数据处理完后,TCP 就会告诉应用程序恢复到正常操作。即使当前窗口大小为 0,也是可以发送紧急数据的,因为紧急数据无须缓存。 | 16 |
Option | 可选项字段,长度不定,但长度必须是 32 bits 的整数倍。 | 32X |
三次握手
三次握手其实带入现实场景十分生动,我们用打电话举例子,假设甲要和乙打电话:
甲:喂,听得到吗?
乙:听得到,你听得到吗?
甲:我也听得到,咱们说正事。。。
最开始两方都是CLOSED
关闭状态,主动打开连接的是客户端,被动打开的是服务端,(服务端的)TCP进程会创建传输控制快TCB,时刻准备接受客户进程的连接请求,此时服务器进入到LISTEN
监听状态。
第一次握手,客户端的TCP进程也先创建TCB,然后向服务端发送请求报文,报文首部同步位SYN=1
,即代表初始一个序列号seq=x
,此时TCP客户端进入到SYN-SENT
同步已发送状态。
第二次握手,服务端收到请求后,如果同意连接,则向客户端发送确认报文,包含ACK=1
,SYN=1
,也初始化一个序列号seq=y
,并对上一段报文确认ack=x+1
,此时TCP服务端进入SYN-RCVD
同步收到状态。
第三次握手,客户端收到确认后,还要给服务端给出确认,确认报文的ACK=1
,自己的序列号seq=x+1
,同时确认上一段报文ack=y+1
,此时TCP连建立,客户端进入ESTABLISHED
已建立连接状态。
为什么一定是三次握手,其实可以看出第一次握手是证明客户端发送能力正常,第二次握手证明服务端发送能力和接收能力都正常,第三次握手是证明客户端的接收能力也正常。
不妨假设只有两次握手:
第一次握手客户端发起连接,第二次握手服务端确认后直接建立连接。
如果某一次客户端发起的连接在网络中滞留了,那么客户端收不到服务端的确认会重新发起连接,这次服务端收到并确认后建立连接,这时第一次滞留的请求到达服务端,服务端再次确认与客户端建立连接。客户端认为只有一个连接,而服务端认为有两个连接,状态不对等了。
如果有三次握手,那么滞留的连接没有收到客户端的确认就不会建立连接。所以两次握手无法抵抗网络波动。
四次挥手
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED
状态,然后客户端主动关闭,服务器被动关闭。
第一次挥手,客户端发出连接释放报文,并停止发送数据。报文首部FIN=1
,其序列号为seq=u
(等于前面已经传送过来的数据的最后一个字节的序号加1),此时客户端进入FIN-WAIT-1
终止等待1状态。
第二次挥手,服务端收到连接释放报文,发出确认报文,ACK=1
,ack=u+1
,并带上自己的序列号seq=v
,此时,服务端就进入了CLOSE-WAIT
关闭等待状态。
第三次挥手,客户端接收到服务端的确认请求,FIN=1
,ACK=1
,seq=w
(如果服务端在发送确认报文后没有发生过其他报文,那么w就是v+1),ack=u+1
(此处还是对客户端上一条报文的确认,ack不变)客户端进入FIN-WAIT-2
终止等待2状态,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,服务器就进入了LAST-ACK
最后确认状态,等待客户端的确认。
第四次挥手,客户端收到服务端的连接释放报文后发出确认,ACK=1
,ack=w+1
,序列号是seq=u+1
,此时客户端就进入TIME-WAIT
超时等待状态,但此时TCP连接还未终止,必须要经过2MSL(Maximum Segment Lifetime,最大报文生存时间,通常是2min),当客户端撤销相应的TCB后,客户端才会进入CLOSED
关闭状态,服务器端接收到确认报文后,会立即进入CLOSED
关闭状态,到这里TCP连接就断开了,四次挥手完成。
为什么客户端要等待2MSL?
主要是为了保证客户端最后发送的ACK报文能到到服务端,因为这个ACK报文可能丢失,而2MSL是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。同时服务端一直没收到确认报文则会重新进行第三次挥手,增加了容错性。(2ML通常是2-8min,对应ML是1-4min)
连续AQR协议
TCP协议通过使用连续ARQ协议和滑动窗口协议,来保证数据传输的正确性,从而提供可靠的传输。
ARQ协议,即自动重传请求(Automatic Repeat-reQuest),是OSI模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议和连续ARQ协议,拥有错误检测(Error Detection)、正面确认(Positive Acknowledgment)、超时重传(Retransmission after Timeout)和 负面确认及重传(Negative Acknowledgment and Retransmission)等机制。
连续ARQ协议:由于停止等待ARQ协议信道利用率太低,所以需要使用连续ARQ协议来进行改善。这个协议会连续发送一组数据包,然后再等待这些数据包的ACK。
发送方采用流水线传输。流水线传输就是发送方可以连续发送多个分组,不必每发完一个分组就停下来等待对方确认。如下图所示:
滑动窗口
滑动窗口是一种流量控制技术,在早期的网络通信中,双方不会考虑网络的拥挤情况而发生数据。由于大家不知道网络的拥塞情况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动窗口来解决问题。滑动窗口是一种改善网络吞吐量的协议,即允许发送方在接收方发出任何应答之前发送附加包,接收方告知发送方某一时刻能发送多少包(即窗口尺寸字段,Window Size
)。
TCP中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为0时,发送方一般不能再发送数据报。滑动窗口是TCP中实现诸如ACK确认、流量控制、拥塞控制的承载结构(说是一般,不是绝对,因为紧急指针可以继续发送紧急数据)。
拥塞控制
拥塞控制的目标是确保网络中的数据传输速率与网络的承载能力相匹配,以维持网络的高效和公平使用。
拥塞控制算法主要分为四个阶段:
- 慢启动(Slow Start):
- 在连接开始时,TCP拥塞窗口(cwnd)从一个较小的值开始,通常是一个段的大小。
- 每收到一个确认(ACK),cwnd加倍,导致窗口大小指数增长。
- 当cwnd达到一个阈值(ssthresh)时,停止使用慢启动,转而使用拥塞避免。
- 拥塞避免(Congestion Avoidance):
- 在拥塞避免阶段,每经过一个往返时间(RTT),cwnd增加1个MSS(最大段大小)。
- 这使得cwnd的增长速度比慢启动阶段慢,有助于防止网络拥塞。
- 快重传和快恢复(Fast Retransmit and Fast Recovery):
- 当接收方连续收到三个重复的ACK时,它会触发快重传,发送方立即重传丢失的数据包,而不是等待重传计时器到期。
- 快恢复算法则调整ssthresh的值,并将cwnd设置为ssthresh加上3个MSS的大小,然后进入拥塞避免阶段。
请求&响应
HTTP1.0/1.1/2.0/3.0
HTTP(超文本传输协议)随着时间的推移经历了多个版本,每个版本都针对性能、安全性等方面进行了改进。以下是HTTP 1.0、1.1、2.0和3.0之间的主要区别:
HTTP 1.0
- 连接:HTTP 1.0默认为非持久连接,意味着每个请求/响应后都会关闭TCP连接,需要重新建立连接。
- 请求和响应:每个请求/响应都是单独的,没有请求优先级或多路复用。
- 头部:HTTP 1.0的头部信息在每个请求中都会重复发送,增加了开销。
- 缓存:缓存机制不如后续版本完善,例如,没有ETag或If-None-Match等头部支持。
HTTP 1.1
- 持久连接:引入了持久连接(默认开启),允许在同一条TCP连接上发送多个请求和响应,减少了建立和关闭连接的开销。
- 管线化:支持请求管线化,客户端可以在一个连接中发送多个请求而无需等待前一个请求的响应。
- 缓存:增加了更多的缓存控制头部,如
Cache-Control
、ETag
和If-None-Match
等。 - 分块传输编码:支持分块传输编码,允许服务器发送不确定大小的响应。
- 虚拟主机支持:通过
Host
头部,支持在同一个IP地址和端口上托管多个域名。
HTTP 2.0
- 二进制分帧:HTTP 2.0将请求和响应消息分解为独立的帧,并且它们在二进制格式中传输,这使得数据传输更加高效和健壮。
- 多路复用:在同一个连接中,客户端和服务器可以同时发送多个请求和响应,解决了HTTP 1.1中的队头阻塞问题。
- 头部压缩:使用HPACK算法压缩请求和响应头部,减少了开销。
- 服务器推送:服务器可以主动向客户端推送资源,而不是等待客户端明确请求。
HTTP 3.0
- 基于QUIC的传输:HTTP 3.0使用QUIC(Quick UDP Internet Connections)作为传输层协议,而不是TCP。QUIC在UDP上实现了类似TCP的可靠性和拥塞控制,同时减少了连接建立时间和提高了移动性。
- 改进的拥塞控制:QUIC提供了更快的拥塞控制反应,因为它将拥塞控制从TCP层移动到了应用层。
- 更好的移动性:QUIC支持在IP地址变化时无缝地迁移连接,这对于移动设备尤其有用。
- 内置安全性:QUIC默认使用TLS 1.3进行加密,提高了安全性。
总结来说,HTTP 1.1相对于HTTP 1.0引入了持久连接和管线化,HTTP 2.0引入了二进制分帧、多路复用和头部压缩,而HTTP 3.0则通过基于QUIC的传输进一步提高了性能和安全性。每个新版本都在努力减少延迟,提高网络资源的利用率。
HTTPS
HTTP over SSL,简称HTTPS,是一种相对安全的传输协议。
SSL
SSL (Secure Sockets Layer)安全套接层。是由Netscape公司于1990年开发,用于保障Word Wide Web(WWW)通讯的安全。主要任务是提供私密性,信息完整性和身份认证。1994年改版为SSLv2,1995年改版为SSLv3。
TLS
TLS(Transport Layer Security)安全传输层协议,)用于在两个通信应用程序之间提供保密性和数据完整性。该标准协议是由IETF于1999年颁布,整体来说TLS非常类似SSLv3,只是对SSLv3做了些增加和修改。
TLS/SSL握手
SSL(安全套接层)握手过程是客户端和服务器之间建立安全连接的过程。以下是SSL握手过程的详细步骤:
- 客户端发起握手:
- 客户端向服务器发送一个Client Hello消息,包含以下信息:
- 支持的SSL/TLS版本
- 一个随机数(Client Random),用于生成会话密钥
- 支持的加密套件列表
- 支持的压缩方法列表
- 客户端向服务器发送一个Client Hello消息,包含以下信息:
- 服务器响应:
- 服务器回应一个Server Hello消息,包含以下信息:
- 确定的SSL/TLS版本
- 一个随机数(Server Random),用于生成会话密钥
- 从客户端提供的加密套件列表中选择的一个加密套件
- 从客户端提供的压缩方法列表中选择的一个压缩方法
- 服务器发送其数字证书(Server Certificate),该证书通常包含服务器的公钥
- 服务器可能会请求客户端的证书(可选步骤,通常用于双向认证)
- 服务器回应一个Server Hello消息,包含以下信息:
- 客户端验证服务器证书:
- 客户端验证服务器证书的合法性,包括:
- 确认证书是由受信任的证书颁发机构(CA)签发
- 检查证书是否过期
- 确认证书域名与服务器实际域名匹配
- 如果服务器要求客户端证书,客户端还会发送自己的证书
- 客户端验证服务器证书的合法性,包括:
- 交换密钥:
- 客户端生成一个随机数(Pre-Master Secret),并使用服务器的公钥加密它,然后发送给服务器
- 服务器使用自己的私钥解密接收到的Pre-Master Secret
- 客户端和服务器均使用Client Random、Server Random和Pre-Master Secret生成Master Secret
- Master Secret用于生成会话密钥,包括对称加密密钥、消息认证码密钥和初始化向量
- 完成握手:
- 客户端发送一个Change Cipher Spec消息,告知服务器后续通信将使用协商好的加密套件和密钥进行加密
- 客户端发送一个Finished消息,该消息包含握手过程中所有消息的哈希值,用于验证握手过程是否被篡改
- 服务器同样发送Change Cipher Spec消息和Finished消息
- 服务器和客户端现在可以开始使用协商好的密钥和加密算法进行安全通信
整个SSL握手过程确保了客户端和服务器之间的安全连接,通过公钥加密和私钥解密技术保证了密钥交换的安全性,同时使用数字证书验证了通信双方的身份。通过这个过程,即使是在不安全的网络环境中,通信内容也能得到机密性和完整性的保护。
常见状态码
状态码 | 类别 | 含义 |
---|---|---|
100 | 信息性状态码 | Continue,继续发送请求 |
101 | 信息性状态码 | Switching Protocols,服务器根据客户端的请求切换协议 |
200 | 成功状态码 | OK,请求成功 |
201 | 成功状态码 | Created,请求已成功,并创建了新的资源 |
202 | 成功状态码 | Accepted,请求已被接受,但尚未处理完成 |
204 | 成功状态码 | No Content,服务器成功处理了请求,但没有返回任何内容 |
301 | 重定向状态码 | Moved Permanently,永久重定向,资源已被分配了新的URI |
302 | 重定向状态码 | Found,临时重定向,资源现在临时从不同的URI响应请求 |
303 | 重定向状态码 | See Other,对请求的响应可以在另一个URI找到,并且应该使用GET方法获取 |
304 | 重定向状态码 | Not Modified,资源未修改,可以使用缓存版本 |
307 | 重定向状态码 | Temporary Redirect,临时重定向,与302类似,但不会改变请求方法 |
400 | 客户端错误状态码 | Bad Request,请求格式错误,服务器无法理解 |
401 | 客户端错误状态码 | Unauthorized,请求需要用户认证 |
403 | 客户端错误状态码 | Forbidden,服务器理解请求,但拒绝执行 |
404 | 客户端错误状态码 | Not Found,请求的资源不存在 |
405 | 客户端错误状态码 | Method Not Allowed,请求行中指定的方法不允许对请求的资源执行 |
408 | 客户端错误状态码 | Request Timeout,服务器等待客户端发送的请求时间过长 |
500 | 服务器错误状态码 | Internal Server Error,服务器内部错误,无法完成请求 |
501 | 服务器错误状态码 | Not Implemented,服务器不支持请求的功能 |
502 | 服务器错误状态码 | Bad Gateway,作为网关或代理的服务器从上游服务器收到了无效的响应 |
503 | 服务器错误状态码 | Service Unavailable,服务器目前无法处理请求,可能是暂时性的服务器过载或维护 |
504 | 服务器错误状态码 | Gateway Timeout,作为网关或代理的服务器没有及时从上游服务器收到响应 |
505 | 服务器错误状态码 | HTTP Version Not Supported,服务器不支持请求的HTTP协议版本 |
浏览器渲染
浏览器渲染页面的原理可以分为以下几个主要步骤:
- 解析HTML构建DOM树:浏览器从服务器获取HTML内容后,首先会对HTML文档进行解析,将HTML标签转换成DOM(文档对象模型)树中的节点。DOM树反映了页面结构,是后续渲染的基础。
- 解析CSS构建CSSOM树:与解析HTML类似,浏览器也会解析CSS样式,构建CSSOM(CSS对象模型)树。CSSOM树包含了所有的CSS规则,用于确定DOM节点在页面上的具体样式。
- 构建渲染树:渲染树是DOM树和CSSOM树的结合。浏览器会遍历DOM树中的所有可见节点,并找到对应的CSS样式,将这些节点和样式结合起来,生成渲染树。
- 布局(Layout):布局阶段,浏览器会计算渲染树中每个节点的确切位置和大小。这个过程也称为重排(Reflow)。
- 绘制(Painting):在绘制阶段,浏览器会根据布局阶段计算出的信息,将内容绘制到屏幕上。这个过程也称为重绘(Repaint)。
- 合成(Compositing):现代浏览器通常会采用合成技术,将页面的不同部分分别绘制到不同的层(Layer)上,然后合成到一起显示。这样可以提高页面的渲染性能,减少重绘和重排的开销。
整个渲染过程是逐步进行的,浏览器会尽可能优化性能,例如通过异步加载脚本、延迟执行样式计算等方式。了解浏览器渲染原理有助于开发者写出更高效、性能更优的网页代码。