本文主要介绍了计算机网络的体系结构和一些重要的协议。文章首先介绍了OSI七层模型和TCP/IP四层模型,并对每一层的功能和常见协议进行了详细说明。接下来,文章讨论了ARP和IP协议,以及与之相关的DHCP、NAT/NAPT等协议。此外,文章还介绍了TCP协议的定义、特点和重传机制,并与UDP进行了比较。最后,文章提到了HTTP、HTTPS、WebSocket等与网络通信相关的协议,并简要介绍了CDN、RPC和HTTPS的工作原理。整体来说,这篇文章对于理解计算机网络的基本知识和常用协议非常有帮助。
体系结构
OSI 七层模型
- 基本结构
- 应用层:为用户提供服务,常见协议有 HTTP/HTTPS(80/443)、FTP(21)、SSH(22)、SMTP(25)、POP3(110)、P2P、Telnet、DNS(53)。
- 表示层:数据处理,编解码、加解密、加解压缩。
- 会话层:建立、管理、维护应用程序间的对话,常见协议有 RPC。
- 传输层:为两台主机进程间通信提供通用数据传输服务(建立、管理、维护端到端连接),常见协议有 TCP、UDP。
- 网络层:路由和(逻辑地址)寻址,常见协议有 IP、ARP、RARP、ICMP(分差错报告报文和询问报文)、IGMP(组播管理)、NAT、OSPF、RIP。
- 数据链路层:帧编码、检错(CRC)和(物理地址)寻址,常见协议有 Ethernet、IEEE802.3、PPP(点对点协议,使用最多,简单且只检错不纠错,无流量控制且可同时支持多种网络层协议)、ARQ(自动重传请求,包括停止等待 ARQ 和连续 ARQ)。
- 物理层:比特流传输。
- 问题
- 实现过于复杂,效率低。
- 部分功能在多层重复出现。
- 制定缺乏实际经验,制定时间周期过长。
TCP/IP 四层模型
- 应用层:对应 OSI 模型应用层、表示层、会话层,传输单位是报文/消息,工作在用户态,其余三层都工作在内核态。
- 传输层:对应 OSI 模型传输层,由于传输层的报文中会携带端口号,因此接收方可以识别出该报文是发送给哪个应用,传输单位是段。
- 网络层:对应 OSI 模型网络层,传输单位是包。
- 网络接口层:对应 OSI 模型数据链路层、物理层,为网络层提供「链路级别」传输的服务,使用 MAC 地址来标识网络上的设备,传输单位是帧。
ARP
- MAC 地址:以太网地址或物理地址,长度 6 字节(48 比特),理论上,一个网络设备中的网卡上的 MAC 地址是永久的,前 24 比特由 IEEE 分配,后面由厂商管理,保证不会重复。同时全 1 地址,即 FF-FF-FF-FF-FF-FF,表示广播地址。
- MAC 的作用是实现「直连」的两个设备之间通信,而 IP 则负责在「没有直连」的两个网络之间进行通信传输。
- ARP/RARP:解决的是网络层地址(IP)和链路层地址(MAC)之间的转换问题,每个网络设备都自己维护了一个 ARP 表,以
<IP, MAC, TTL>
三元组的形式存储,TTL 是存活时间,该缓存可减少大量网络通信量。协议可归结为广播问询,单播响应。
IP
IPv4
- 分类
- A 类:0 + 【7 位网络号】 + 【24 位主机号】 即范围是 0.0.0.0-127.255.255.255,其中 10.0.0.0-10.255.255.255 是私有 IP 地址,除此之外 127.0.0.1 是环回地址,数据包不会流向网络。
- B 类:10 + 【14 位网络号】 + 【16 位主机号】 即范围是 128.0.0.0-191.255.255.255,其中 172.16.0.0-172.31.255.255 是私有 IP 地址。
- C 类:110 + 【21 位网络号】 + 【8 位主机号】 即范围是 192.0.0.0-223.255.255.255,其中 192.168.0.0-192.168.255.255 是私有 IP 地址。
- D 类:1110 + 【28 位组播地址】
- E 类:1111 + 【28 位留待后用】
- 主机号全为 1 指定某个网络下的所有主机,用于广播。
- 主机号全为 0 表示指向本网,常用于路由表中。
- 优点:简单明了、选路(基于网络地址)简单。
- 缺点:缺少层次划分,如 B 类下无法进一步方便划分生产、开发和测试环境;C 类包含的主机数量过少,而 B 类又过多,无法很好地同现实匹配。
- CIDR:无类域间路由选择,消除了传统的 A 类、B 类和 C 类地址以及划分子网的概念,并使用各种长度的网络前缀来代替分类地址中的网络号和子网号。比如 10.100.122.2/24,意指前 24 位是网络号,后 8 位则是主机号。
分片
- 以太网最大传输单元(MTU)是 1500 字节。因而 TCP 引入了 MSS(除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度),在 TCP 层分片,防止 IP 某个分片丢失导致整个数据包作废,进而因为某个分片丢失而重传整个 IP 报文的分片。
IPv6
- 16 字节(128 比特),16 进制表示。
- 包头包首部长度固定 40 字节,自动分配 IP 地址,提高传输性能,也有更多的安全功能。同时去除了分片/重新组装相关字段,只允许在源与目标主机分片重组,提高了中间路由的传输效率。
部分相关协议
- DHCP:动态获取 IP 地址
- DHCP 客户端进程监听的是 68 端口号,DHCP 服务端进程监听的是 67 端口号。
- UDP 广播通信。
- NAT/NAPT:网络地址(与端口)转换
- 如将 192.168.1.10 和 192.168.1.11 两个私有地址都转换为公有地址 120.229.175.121,以不同端口号加以区分,进而实现同时和服务端通信。
- 依赖转换表:性能开销、NAT 设备重启后所有 TCP 连接将被重置。
- 解决方案:NAT 穿透技术,客户端自己请求 NAT 设备,并自行维护映射条目。
TCP
定义
- TCP:面向连接的、可靠的、基于字节流的传输层通信协议。
- 面向连接:一对一才能连接,不能一个主机同时对多个主机发送消息。
- 可靠:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定到达接收端。
- TCP 的可靠性是传输层可靠性,也即仅保证数据从 A 传输层可靠发送至 B 传输层,传输层到应用层这部分,并不是 TCP 保证。
- 在二者间引入服务器,也有保证应用层可靠性的作用。
- 除此之外,也可以减少连接建立数,消耗更少资源。
- 便于鉴权校验,提高安全性。
- 软件版本问题,借助服务器强制部分过低版本升级,否则不能正常使用,同时加入兼容逻辑,解决大部分兼容问题。
- 基于字节流:一个 TCP 报文不能被对应为一个消息,消息可能会被操作系统分组成多个的 TCP 报文。
- TCP 粘包:不知道一个用户消息的边界。
- 固定长度消息:规定用户消息长度,灵活性太低,很少使用。
- 特殊字符作为边界:两个用户消息中间插入特殊字符串,如 HTTP 通过设置回车符、换行符作为边界。除此之外 HTTP 还有 Content-Length 字段说明数据长度,进一步解决粘包问题。
- 自定义消息结构:加入说明数据长度的字段。
- TCP 粘包:不知道一个用户消息的边界。
- TCP 头部
- 序列号(32 位):建立连接时通过时钟周期随机生成,SYN 包发向另一方,每发送一次数据,就「累加」一次该「数据字节数」的大小,SYN 和 FIN 包被认为累加 1,其余累加字节数,用来解决网络包乱序问题。
- 确认应答号(32 位):指下一次「期望」收到的数据的序列号,用来解决丢包的问题。
和 UDP 区别
- UDP:无连接的、不可靠的、基于数据报的传输层通信协议。头部仅 8 个字节(源端口号、目的端口号、包长度、校验和)。
- 连接:TCP 面向连接,传输前要先建立连接,UDP 即刻传输数据。
- 服务对象:TCP 只能一对一服务,UDP 支持一对一、一对多、多对多的交互通信。
- 可靠性:TCP 按序可靠交付,UDP 尽最大努力交付,可以基于其实现可靠传输协议,即 QUIC。
- 基于字节流和数据报:TCP 是基于字节流的,不能像 UDP 一样认为一个报文对应一个消息,消息可能被操作系统分组成多个 TCP 报文传输。
- 拥塞控制、流量控制:TCP 通过二者保证数据安全传输,UDP 在网络拥堵时依旧正常发送。
- 首部开销:TCP 至少有 20 字节首部长度(是否使用选项字段),UDP 首部固定 8 字节,因而 UDP 也无需首部长度字段,TCP 中有 4 比特的首部长度字段。
- 传输方式:TCP 流式传输,无边界但保证顺序,UDP 有边界,但可能丢包和乱序。
- 分片不同:TCP 在传输层分片和重组(是否大于 MSS),分片丢失只需重传丢失的,UDP 在 IP 层分片(是否大于 MTU)。
- 应用场景:TCP 注重可靠,UDP 注重实时发送、简单高效。
- TCP:FTP、HTTP/HTTPS、SMTP、SSH、Telnet
- UDP:DNS、TFTP(端口 69,简单文件传输协议)、SNMP(简单网络管理协议,161 端口,Trap 消息 162 端口)
- 二者可以同时使用同一个端口,端口在此仅用于区分同一个主机上不同应用程序的数据包,而 TCP/UDP 数据包可以通过 IP 包头协议号字段区分。
三次握手建立连接
- 【SYN+client_isn】→【SYN+ACK+(client_isn+1)+server_isn】→【ACK+(server_isn+1)】
- 第三次握手可携带数据,前两次不可。
- 查看 TCP 状态命令
netstat -napt
,UDP 对应参数为 u - 三次握手的原因
- 防止「历史连接」初始化了连接。两次握手则被连接方没有中间状态,旧 SYN 报文到达即进入 Established 状态,造成资源浪费。
- 同步双方各自的序列号,正常是连接方 SYN→ 接收方 ACK→ 接收方 SYN→ 连接方 ACK,中间两步可以合并,所以是三次握手。序列号保证按序、不重复、不缺失传输数据包。
- 两次握手还有可能导致被连接方不确定发出的 ACK 报文对方是否收到,只能选择收到 SYN 就主动建立连接,造成资源浪费。
- 另,若第三次握手(即 ack 报文)丢失,还是可以正常建立连接,被连接方在收到数据报文时有 ACK 标识位和确认号(确认收到第二次握手)。
- 每次建立连接序列号 ISN 不同:防止收到历史连接的数据包,防止接收到伪造的报文。
- ISN 基于时钟,每 4 微秒+1,存在回绕现象,4.55 小时循环一次。
- 引入 TCP 时间戳:便于精确计算 RTT,同时防止 ISN 回绕,时间戳不是递增的,则表示数据包是过期的,丢弃。
- 第一次握手丢失:重传,1s,2s,4s,8s,16s 后依次重试,再等待 32s,若这总计近 1min 内未收到回复,则断开连接,可以通过对应参数
tcp_syn_retries
修改重传次数。 - 第二次握手丢失:连接方重传 SYN(没收到第二次握手中的 ACK),被连接方重传 SYN+ACK。
- 第三次握手丢失:被连接方重传 SYN+ACK。
- 被连接方收到 SYN(第一次握手)后创建半连接对象,加入「半连接队列」(哈希表实现),收到 ACK(第三次握手)后从「半连接队列」中取出一个对象,创建新的连接对象加入「 Accept 队列」(链表实现),等待应用通过
accept()
取出连接对象。 - 服务端如果只
bind()
了 IP 地址和端口,而没有调用listen()
,然后客户端对服务端发起 SYN 报文,服务端回送 RST 报文。 - SYN 泛洪攻击:打满半连接队列,导致后续正常 SYN 报文都被丢弃,无法正常建立连接。
- 调大半连接队列大小,调大保存暂时来不及处理数据包队列大小。
- 减少 SYN+ACK(第二次握手)重传次数
- 开启 syncookies,收到 SYN 包后计算 cookie,并放到第二次握手报文中,第三次握手检查合法性,合法直接放到「 Accept 队列」。0 值关闭,1 值表示仅半连接队列满时启用,2 值无条件开启。
- cookie 方案的缺陷:编码解码消耗 CPU 资源,如果攻击者构造大量 ACK 包(第三次握手),则服务端需要大量编码解码,容易耗尽 CPU 资源而无法响应正式请求。同时如果第二次握手丢失,也不会重发第二次握手的信息。
- 客户端在第二次握手
connect
成功,服务端accept
成功返回是在第三次握手成功之后,没有accept
也能顺利建立连接,其主要从全连接队列中拿出一条连接。
四次挥手断开连接
- 【断连方 FIN,进入 FIN_WAIT_1】→【被断连方 ACK,进入 CLOSED_WAIT】→【被断连方 FIN,进入 LAST_ACK】→【断连方 ACK,进入 TIME_WAIT,停留 2MSL】→【被断连方收到 ACK 后关闭连接,断连方 2MSL 结束后断连】
- 四次原因:被断连方通常还有数据未处理发送,因而 ACK 和 FIN 一般分开发送。
- 第一次挥手丢失/第二次挥手丢失:均为断连方重传 FIN,ACK 报文不重传。
- 第三次挥手丢失:被断连方重传 FIN,FIN_WAIT_2 时长到达阈值后断连方断开连接。
- 第四次挥手丢失:被断连方重传 FIN,断连方收到 FIN 则重置 2MSL 定时器。
- 2MSL(报文最大生存时间)原因:发送方数据包到达接收方处理,接收方处理后发送响应,一来一回需要等待 2 倍时间,Linux 系统默认 2MSL 是 60 秒。
- 2MSL 足以让历史报文都被丢弃,避免新建立相同四元组连接接收到历史数据包。
- 确保最后的 ACK(第四次挥手)能够被被断连方接收到,若直接关闭则被断连方没收到 ACK 会重发 FIN,发现断连方已断开连接,收到 RST 报文,RST 报文被解释为错误,不是优雅终结方式。
- TIME_WAIT 过多:如果是客户端,可能导致端口资源受限,而客户端和服务端均可能占用系统资源,比如文件描述符、内存资源、CPU 资源等。
- 如果服务端要避免过多的 TIME_WAIT 状态的连接,就永远不要主动断开连接,让客户端去断开,由分布在各处的客户端去承受 TIME_WAIT。
- TCP 的 keepalive:保活机制,类似心跳机制,默认检测时间 7875 秒,也就是至少 7875 秒后才可以发现一个「死亡」连接,较长。
- 收到 RST 报文:直接关闭,不走四次挥手流程。
- 调用
close
和shutdown
发起 FIN 报文。- 前者完全断开连接,发起方不能接收和发送数据,成为「孤儿连接」,不够优雅。
- 后者可以允许只关闭写和读中一个方向上的连接。
- 双方同时断开连接,均认为自己是主动方,各自收到对方 ACK 后进入 TIME_WAIT,等待 2MSL 后连接关闭。
- ACK 延迟确认机制:没有响应数据要发送时,ACK 会延迟一段时间,若数据报文到达,则会立即发送 ACK。因而四次挥手中的二三次也可能合并(并非只有在没有数据要发送的时候),变成三次挥手。
重传机制
- 超时重传
- 往返时延 RTT:数据发送时刻到接收到确认的时刻的差值。
- 超时重传时间 RTO 应略大于 RTT,过大则效率低,丢失很久才重发,而过小则可能导致未丢失就重发,增大网络负担。
- RTT 动态变化,因而每次超时重传都把 RTO 设置为原先的两倍,多次超时说明网络环境差,暂时避免重复尝试发送。
- 快速重传:解决超时重传周期较长的问题。
- 收到三个相同的 ACK 报文时,会直接重传原先丢失的报文。
- 存在问题:无法确定究竟重传哪部分报文。
- 如果只重传丢失的那一个报文,可能后面的连续两个也丢失了,仍需要依次触发三次 ACK,效率太低。
- 如果全部重传,那未丢失的部分已经顺利接收,浪费资源。
- SACK 方法(选择性确认)
- TCP 头部「选项」字段加入
SACK
,接收方可以把收到的数据告知发送方,实现只重传丢失的数据。
- TCP 头部「选项」字段加入
- D-SACK(重复选择性确认)
- 告诉发送方,哪些数据被重复接收了,减少资源浪费,确认丢包的是数据还是接收方回应的 ACK。
- 案例场景
- 接收方 ACK 丢包。
- 网络延时,后续数据包顺利到达,触发了快速重传。
滑动窗口
- 原因:发送数据和回送确认的往返降低了通信效率,窗口大小即无需等待确认应答,而可以继续发送数据的最大值。
- 通常窗口的大小是由接收方的窗口大小来决定的。
- 接收方和发送方的窗口大小并不一定相等,随数据读取动态变化,而传输存在时延。
流量控制
- 原因:避免接收方无法及时处理,发送方不断触发重发机制,浪费网络资源。
- 同时减少缓存又收缩窗口可能会出现丢包现象,因而采用先收缩窗口,稍后再减少缓存的方式。
- 缓存的作用:数据包不能立刻从内存中删除,在重传的时候可能会用到。
- 窗口关闭潜在风险:后续通报窗口非零大小 ACK 报文丢失,则发送方一直等待窗口非零大小 ACK,接收方一直等待数据。
- 死锁解决:TCP 连接一方收到零窗口通知,就启动计时器,超时则发送窗口探测报文。
- 糊涂窗口综合征:接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节。
- 接收方不通告小窗口给发送方
- 发送方避免发送小数据
- Nagle 算法,延时处理,满足一个条件才可发送
- 要等到窗口大小 >=
MSS
并且 数据大小 >=MSS
。 - 收到之前发送数据的 ACK 回包。
- 要等到窗口大小 >=
- Nagle 算法,延时处理,满足一个条件才可发送
- 同时满足上面两个条件才能避免糊涂窗口综合征。
拥塞控制
- 原因:避免发送方的数据填满整个网络。
- 拥塞判断:发送方没有在规定时间内接收到 ACK 应答报文,即触发了超时重传。
- 控制算法
- 慢启动
- 每收到一个 ACK,拥塞窗口大小+1,呈现指数型增长。
- 慢启动门限 ssthresh,超过则进入拥塞避免。
- 拥塞避免
- 每收到一个 ACK,拥塞窗口大小增加 1/拥塞窗口大小,即线性增长。
- 拥塞发生
- 超时重传
- 慢启动门限设置为拥塞窗口大小一半。
- 拥塞窗口大小重置为初始化值。
- 缺陷:反应过于强烈,可能造成网络卡顿。
- 快速重传
- 接收方发现丢了中间包,发送三次前一个包的 ACK,不必等待超时重传。
- 慢启动门限设置为拥塞窗口大小一半。
- 拥塞窗口大小减半,而非重置为初始化值。
- 超时重传
- 快速恢复
- 通常和快速恢复算法同时使用,还能收到三次 ACK,说明网络情况不至于需要做出强烈调整。
- 重传丢失的数据包,若收到重复 ACK,则拥塞窗口大小增加 1。
- 收到新数据 ACK,将拥塞窗口大小设置为慢启动门限值,说明恢复过程已经结束,可以进入拥塞避免状态。
- 慢启动
关闭 TCP 连接
- 杀掉进程
- 客户端杀掉进程,会给服务端发送 FIN 报文,影响范围只有该客户端进程建立的连接,其余进程或客户端和服务端的连接不受影响。
- 服务端杀掉进程,服务端无法继续提供服务,与之相连的 TCP 连接全部被关闭,影响显著。
- 伪造 RST 报文
- 存在问题:序列号不一定正好落入接收的窗口范围内,若被丢弃则无法关闭连接。
- 伪造 RST 报文需要满足「四元组相同」和「序列号落在窗口内」,窗口可能因为数据传输在频繁变化中,直接伪造相对困难。
- 解决方案:伪造一个四元组相同的 SYN 报文,拿到合法序列号,再借助此发送 RST 报文。
重启 TCP 服务
- 重启 TCP 服务进程,出现“Address in use”的报错信息。
- 即通过服务端发起关闭连接操作,于是服务端作为主动方发起四次挥手,会在 TIME_WAIT 停留 2MSL 时间,因而在执行
bind()
函数的时候,就会返回对应错误。 - 解决方案:在调用 bind 前,对 socket 设置 SO_REUSEADDR 属性,不会产生危害,可以帮助更快重启服务端程序。
- 即通过服务端发起关闭连接操作,于是服务端作为主动方发起四次挥手,会在 TIME_WAIT 停留 2MSL 时间,因而在执行
TCP Fast Open
- 首次连接后客户端本地缓存服务端第二次握手返回报文中的 Fast Open Cookie。
- 第二次及之后建立连接,第一次握手 SYN 报文可以携带「应用数据」,若 Cookie 有效则接收数据,因而可减少握手带来的 1 个 RTT 时间消耗。
TCP 缺陷
- 升级 TCP 的工作难度大,很多新特性需要客户端和服务端同时支持。
- TCP 建立连接的延迟。由于 TCP 实现在内核,TLS 在应用层实现,所以 TLS 无法加密 TCP 头部,因而 TCP 序列号是明文传输,有安全风险,因而需要三次握手来同步各自生成的序列号,增加安全性。
- TCP 队头阻塞问题。TCP 面向字节流且保证完整有序,因而前面的 TCP 包丢失了,即使后面的到达了,应用层也无法从内核中读取到这部分数据。
- 网络变化需要重新建立 TCP 连接。当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立 TCP 连接。用户会明显感觉到一刻卡顿,因而重新建立连接成本很高。
QUIC
- 序列号严格递增,重传的包序列号更大。TCP 重传的序列号一致产生了歧义问题,在计算 RTT 的时候容易偏差,进而影响 RTO 的变化。同时严格递增也可以让 QUIC 支持乱序确认,不会因为丢包而阻塞窗口移动,解决了队头阻塞问题。
- 两种级别的流量控制
- Stream 级别的流量控制。每个 Stream 都可以认为是一条 HTTP 请求,每个 Stream 都有各自的滑动窗口,因而可以防止因为单个 Stream 影响整个接收缓冲。
- Connection 级别的流量控制。限制 Stream 加起来的总字节数,防止发送方发送超过缓冲容量。
- QUIC 并不与 TLS 分层,而是在内部包含了 TLS,可以携带连接信息和 TLS 信息,在非首次连接时数据包也可以一起发送,进而更快建立连接。
- QUIC 协议没有用四元组的方式来“绑定”连接,而是通过连接 ID 来标记通信的两个端点,只要上下文信息仍然保留,即使 IP 地址发生了变化,原始连接 ID 依旧有效,也可以复用原连接,消除重连的成本。
- 因此 QUIC 在网络切换的响应速度上优势明显,这对于移动设备非常重要,避免了 TCP 协议超时、中断然后重新创建握手带来的高延迟。
HTTP
基础内容
- 定义:超文本传输协议。
- 状态码
- 204 No Content:成功状态码,但没有 body 数据。
- 206 Partial Content:成功状态码,应用于 HTTP 分块下载或断点续传,表示 body 数据并不是资源的全部。
- 301 Moved Permanently:永久重定向,请求资源已经不存在了,需要改新 URL 访问。
- 302 Found:临时重定向,请求资源还在,暂时需要用另一个 URL 访问。和 301 均会在响应头使用 Location 字段指定后续要跳转的 URL。
- 304 Not Modified:缓存重定向,告诉对方可以继续使用缓存资源。
- 协商缓存:
- Etag 优先级比 Last-Modified 高,Etag 一致的情况下再对比 Last-Modified,避免一些 HTTP 服务器未将文件修改时间纳入哈希范围。
- Etag 是 HTTP 中一致性最强的缓存机制,Last-Modified 只能精确到秒级,以及某些文件可能定期重新生成,但内容没有实际变化,这些情况都适用 Etag 解决。
- Etag 性能差,比起简单获取修改时间,Etag 还需要对资源进行哈希计算。
- 协商缓存:
- 403 Forbidden:禁止访问资源,并非请求出错。
- 404 Not Found:未找到请求的资源。
- 501 Not Implemented:请求报文正确,但该请求的功能还不支持。
- 502 Bad Gateway:服务器工作正常,通常是代理或网关返回的错误码。
- 503 Service Unavailable:服务器当前暂时无法响应客户端,如资源占用满等情况。
- GET:安全且幂等,可以做缓存,也可以保存为书签。
- POST:不安全不幂等的,一般不缓存 POST 请求,不能保存为书签。
- HTTPS 和 HTTP:HTTPS 建立连接多了 TLS 握手过程。传输内容时候 HTTPS 会加密数据,通常是对称加密。
HTTP1.1
- 相比 HTTP1.0 的改进:
- 长连接,无需反复断开建立连接,改善性能开销。
- 管道传输,默认未打开,只要一个请求发出,不必等待其返回即可发送第二个请求,减少整体响应时间。
- 性能优化
- 避免发送:缓存技术。
- 减少 HTTP 请求次数
- 减少重定向请求:借助代理服务器。
- 合并请求:减少了重复发送的 HHTTP 头部,同时每个请求可能是不同的 TCP 连接(HTTP1.1 未使用管道的情况及 HTTP1.1 之前),还能减少 TCP 握手和慢启动过程耗费的时间。
- 小图合并为大图。
- 图片编码为 base64,不需要单独发起图片相关请求,直接解码即可。
- 延迟请求:如只获取用户视野的页面资源,用户滑动页面时再向服务器请求接下来的资源。
- 减少 HTTP 响应的数据大小
- 有损压缩:多为图片,Accept 字段中 q 告诉对方期望的资源质量,如 Webp 和 PNG 之间。
- 无损压缩:多为文本文件、程序可执行文件和源代码,content-encoding 字段指定如 gzip 等压缩算法。
HTTP2
- 相比 HTTP1.1 的改进:
- 头部压缩:消除重复部分。
- 二进制格式:全面采用二进制格式,头信息帧和数据帧,无需明文报文转二进制,增加数据传输效率。
- 并发传输:引入 Stream,多个 Stream 复用一条 TCP 连接,支持乱序发送,解决 HTTP 队头堵塞的问题,无法解决 TCP 队头堵塞,TCP 基于字节流,需要收到的字节数据完整且连续,同时一旦丢包,所有 HTTP 请求都需要等待这个包重传。
- 服务器主动推送:服务端不是被动响应,如在客户端请求 HTML 文件时服务端可以主动推送 CSS,减少了消息传递的次数。
- 一些原有优化措施并不一定再有价值,甚至成为反模式。
- 多图合并(CSS Sprites/雪碧图),意味着哪怕只要用一张小图,也必须完整加载大图,对任何一张小图修改,也会导致缓存整体失效,样式、脚本等类似的资源也同理。引入多路复用后,HTTP2 反而更适合传输小资源,一个错误的 TCP 包会导致所有流等必须等待这个包重传成功。
- 合并异步请求,会导致所有请求返回时间都受最慢的那个请求的拖累,整体响应速度下降。
HTTP3
- 相比 HTTP2 的改进:
- TCP 队头阻塞。
- TCP 与 TLS 握手时延迟。
- 网络迁移时需要重新连接。
- 实现方案:QUIC
RPC
- 定义:远程过程调用,本身并非具体协议,而是一种调用方式,调用本地方法一样调用远端服务器暴露方法,屏蔽掉一些网络细节。
- RPC 主要用于 client/server 架构,而 HTTP 主要用于 browser/server 架构,二者现在也在融合。
- RPC 可以采用体积更小的 protobuf 或其他序列化协议去保存结构体数据,同时不需要考虑浏览器重定向行为等,所以相比 HTTP1.1 性能略好,常用于公司内部微服务。
WebSocket
- HTTP1.1 的缺陷:半双工,TCP 是全双工的(即同一时间里,双方都可以主动给对方发送数据),HTTP1.1 只能采用长轮询或不断轮询的方式获取数据,在游戏等需要双方大量数据交互的场景完全不适用。
- 兼容 WebSocket:浏览器在 TCP 三次握手建立连接后,都统一先使用 HTTP 通信,建立 Websocket 连接则带上特殊请求头,对方回状态码 101(表示切换协议):
- 适用场景:服务器和客户端(浏览器)频繁交互。
HTTPS
- 数字证书相关,略。
CDN
- 内容分发网络,其获取源站资源的过程即是内容分发,也是其核心价值。
- 主动分发(PUSH):分发由源站主动发起,将内容从源站推送到各个 CDN 缓存节点,无固定业界标准。适用于网站需要预载大量资源的场景,如淘宝、京东等在双十一前把活动中需要的资源推送到 CDN 缓存节点中,特别常用的资源甚至直接缓存到手机 APP 存储空间或浏览器 LocalStorage 中。
- 被动回源(PULL):由用户访问所触发全自动、双向透明的资源缓存过程。首次访问 CDN 缓存节点发现自己没有对应资源,就实时从源站获取。首次访问通常较慢(但 CDN 网络质量一般远高于普通用户,因而不一定比用户访问源站慢),不适用大数据量资源的场景,适用于小型站点使用 CDN 服务节点。
- 通常采用超时被动失效和手工主动失效两种方式结合,管理更新资源。
- CDN 的应用
- 加速静态资源,CDN 的初衷和核心价值。
- 安全防御,源站只对 CDN 提供服务,由 CDN 为外部用户服务,相当于网站堡垒机,对 DDoS 攻击等尤其有效,但不能寄托安全保证在 CDN 上。
- 协议升级,不少 CDN 提供商都同时对接(代售 CA 的)SSL 证书服务,可以实现源站是 HTTP 协议的,而对外开放的网站是基于 HTTPS 的。同理,可以实现源站到 CDN 是 HTTP/1.x 协议,CDN 提供的外部服务是 HTTP/2 或 HTTP/3 协议、实现源站是基于 IPv4 网络的,CDN 提供的外部服务支持 IPv6 网络,等等。
- 状态缓存,缓存源站状态,减轻源站压力,在网站状态改变时及时刷新缓存。
- 修改资源,如提供跨域能力,对源站未压缩的资源自动压缩并修改等。
- 访问控制,实现 IP 黑/白名单功能等。
- 注入功能,不修改源站代码的前提下,为源站注入各种功能。
键入网址到网页显示,发生了什么
- 解析 URL,生成 HTTP 请求信息(HTTP 数据包)。
- DNS 查询(浏览器、操作系统、hosts 文件、本地 DNS 服务器缓存、均未找到再查询)。
- 三次握手建立 TCP 连接,TCP 报文生成。
- IP 报文生成。
- 加上 MAC 头,网卡、交换机、路由器转发。
- 互相解析数据包(逐层扒),服务器处理响应请求。
- 浏览器渲染页面。
- TCP 四次挥手断开连接。