+tcpdump -nn icmp
+```
+
+只看到 `SYN` 重传,通常说明 TCP 层还没通;TCP 已建立但 TLS 卡住,再继续看 `ClientHello`、SNI、证书、代理和安全策略。
+
+## HTTPS 也可能卡在 SNI
+
+还有个容易误判的地方:同样是 `443`,同样是 HTTPS,也不代表一定能过。
+
+HTTPS 的正文内容会加密,但 TLS 握手一开始的 `ClientHello` 里,通常会带 SNI(Server Name Indication,TLS 扩展)。SNI 的作用是告诉服务器“我要访问哪个域名”,这样同一个 IP 才能挂多个 HTTPS 站点。
+
+问题是,传统 SNI 通常是明文的。
+
+
+
+从上图可以看到,TLS 握手分为多个阶段:ClientHello(携带 SNI、支持的密码套件)→ ServerHello(选定密码套件)→ 证书 → 密钥交换 → 双方计算共享秘密 → 握手完成。中间设备不需要解密 HTTPS 内容,只需要看一眼 `ClientHello`,就可能知道你要访问哪个域名,并按域名策略处理这条连接。
+
+TLS 生态后来引入了 ECH(Encrypted ClientHello)来加密更多 `ClientHello` 信息,包括真实 SNI。不过 ECH 是否生效取决于客户端、服务端、DNS `HTTPS` / SVCB 记录和网络环境,不能默认所有 HTTPS 都已经隐藏 SNI。
+
+命中策略后,中间设备可能静默丢弃、注入 `RST`、终止 TLS、返回拦截页,或者让连接卡在 TLS 握手阶段。具体表现取决于防火墙、代理或安全设备实现。
+
+这类问题抓包时会比较迷惑:TCP 三次握手可能已经成功,连接看起来也建立了,但 `ClientHello` 发出去之后就没响应,或者很快被重置。
+
+所以,“TCP 通了”和“HTTPS 能正常访问”也不是同一句话。前者看三次握手,后者还要看 TLS 握手、SNI、证书、代理和安全策略。
+
+## 小结
+
+`ping` 测的是 ICMP;TCP 要看目标端口有没有监听、三次握手能不能完成、中间设备有没有放行;HTTPS 还可能卡在 TLS 握手,尤其是 SNI 这一步。
+
+反过来也一样:Ping 不通,不代表 TCP 一定不通。有些服务器或云安全组会直接禁 ICMP,但业务端口仍然正常。所以排查时不要用一个命令下结论,要按层验证。
+
+小 G 一般会按这个顺序查:如果是域名,先看 DNS;再用 `ping` 看 ICMP;然后用 `nc` 测端口;最后用 `curl` 或 `openssl s_client` 看 HTTPS/TLS。别让一个 `ping` 过早把问题定性了。
+
+
diff --git a/docs/cs-basics/network/can-tcp-and-udp-use-the-same-port.md b/docs/cs-basics/network/can-tcp-and-udp-use-the-same-port.md
new file mode 100644
index 00000000000..b04b97cf26c
--- /dev/null
+++ b/docs/cs-basics/network/can-tcp-and-udp-use-the-same-port.md
@@ -0,0 +1,129 @@
+---
+title: TCP 和 UDP 可以使用同一个端口吗?
+description: 讲清 TCP 和 UDP 是否可以使用同一个端口,以及端口空间、绑定规则和常见例子。
+category: 计算机基础
+tag:
+ - 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP,UDP,端口,socket,bind,DNS 53,HTTP3,QUIC,UDP 443
+---
+
+面试里经常会碰到这个问题:一台机器上,TCP 已经监听了 `8080`,UDP 还能不能再监听 `8080`?
+
+先说结论:**可以。TCP 和 UDP 的端口绑定命名空间按传输层协议区分,同一个数字端口在不同协议下不冲突。** 一个进程监听 `TCP/8080`,另一个进程监听 `UDP/8080`,内核会根据协议栈分别分发。
+
+## 端口号到底归谁管?
+
+端口是传输层用来区分应用进程的编号。IP 地址定位主机,端口号定位这台主机上的具体应用。
+
+TCP 和 UDP 报文头里都有源端口和目的端口字段,字段长度都是 16 位(16 bits),所以端口号范围都是 `0~65535`。不过端口 `0` 在实际 API 里通常有特殊含义,比如让系统自动分配临时端口,不适合作为普通服务监听端口讲解。
+
+**数字范围相同,不代表绑定对象相同**。服务注册、监听、抓包、防火墙和安全组规则里,通常都要把传输层协议和端口一起看,比如 `TCP/53`、`UDP/53`、`TCP/443`、`UDP/443`。
+
+`TCP/443` 和 `UDP/443` 只是数字一样,协议栈处理路径不同。收到 IP 包后,内核会先看 IP 层的协议标识:IPv4 里是 Protocol 字段,IPv6 里对应 Next Header。TCP 的协议号是 `6`,UDP 是 `17`。在进入端口分发之前,内核已经根据协议号把报文交给对应的 TCP 或 UDP 协议栈。
+
+
+
+TCP 和 UDP 虽然都在传输层,但差异很大。下表从 8 个维度对比一下,方便建立整体认知:
+
+| 特性 | TCP | UDP |
+| ------------ | --------------------------------------------- | ----------------------------------- |
+| **连接性** | 面向连接(三次握手建连、四次挥手释放) | 无连接,直接发 |
+| **可靠性** | 可靠(序列号、ACK、重传、流量控制、拥塞控制) | 不可靠,尽最大努力交付 |
+| **状态维护** | 有状态,维护连接信息 | 无状态,发完就不管了 |
+| **传输效率** | 较低(建连、确认、重传开销大) | 较高(结构简单、开销小) |
+| **传输形式** | 面向字节流,不保留消息边界 | 面向报文,天然保留消息边界 |
+| **首部开销** | 20~60 字节 | 固定 8 字节 |
+| **通信模式** | 点对点(单播) | 单播、多播、广播都支持 |
+| **常见应用** | HTTP/HTTPS、FTP、SMTP、SSH | DNS、DHCP、SNMP、TFTP、VoIP、视频流 |
+
+正因为 TCP 和 UDP 是两套完全独立的传输层协议,内核才会在端口分发之前先把它们分开处理。
+
+## socket 绑定时为什么不冲突?
+
+服务端程序通常会先创建 socket,再通过 `bind()` 绑定本地 IP 和端口。一个 TCP socket 绑定 `8080`,另一个 UDP socket 也绑定 `8080`,通常可以同时存在。内核判断冲突时,不只看端口数字,还会看协议、本地地址等信息。
+
+对于 TCP 来说,一条已建立连接通常可以用四元组标识:源 IP、源端口、目的 IP、目的端口。在防火墙、NAT、抓包和流量排查里,也常把传输层协议加进去,称为五元组:
+
+```text
+协议、源 IP、源端口、目的 IP、目的端口
+```
+
+两条通信的目的端口都可以是 `8080`,只要协议不同,内核就不会把它们当成同一条通信。UDP 没有 TCP 那种连接状态机,但收发数据时同样会带上源 IP、源端口、目的 IP、目的端口。
+
+## 简单验证一下
+
+可以用 `nc` 快速试一下。不过不同系统里的 `nc` 实现不完全一样,`-l`、`-u` 和端口参数写法可能有差异。以下是 OpenBSD netcat 常见写法,命令报错时可以先用 `nc -h` 看本机帮助。
+
+先启动 TCP 监听:
+
+```bash
+nc -l 8000
+```
+
+再启动 UDP 监听:
+
+```bash
+nc -u -l 8000
+```
+
+两个命令可以同时存在。在 Linux 上可以再查看:
+
+```bash
+ss -tulnp | grep 8000
+```
+
+通常会看到一条 `tcp` 和一条 `udp` 监听,端口号一样,但协议不同。
+
+如果想避开 `nc` 参数差异,也可以用代码验证:Java 里 `ServerSocket(8000)` 和 `DatagramSocket(8000)` 可以同时创建;Go 里 `net.Listen("tcp", ":8000")` 和 `net.ListenPacket("udp", ":8000")` 也可以同时存在。再用同一种协议重复监听一次,通常就会看到地址已被占用。
+
+## 什么时候会冲突?
+
+
+
+TCP 和 UDP 之间不冲突,不代表端口可以随便重复绑定。
+
+更常见的冲突发生在**同一个协议**里。比如一个进程已经绑定 `0.0.0.0:8080`,通常会覆盖本机所有 IPv4 地址上的 `8080`,另一个进程再绑定某个具体 IPv4 地址的 `TCP/8080` 往往会冲突;但最终行为还会受绑定顺序、`SO_REUSEADDR`、`SO_REUSEPORT` 和操作系统实现影响。
+
+如果两个进程绑定的是不同本地 IP,同协议同端口也可能成立,例如 `192.168.1.10:8080` 和 `192.168.1.11:8080` 都是 TCP。
+
+还有一个容易被忽略的点:IPv6 的通配地址 `[::]:8080` 在一些环境下可能同时接收 IPv6 和 IPv4-mapped 地址,`IPV6_V6ONLY` 会影响它是否和 IPv4 socket 冲突。排查时可以用 `ss -tulnp` 同时看 `0.0.0.0:端口` 和 `[::]:端口`。
+
+`SO_REUSEADDR`、`SO_REUSEPORT` 也会改变绑定规则,常用于快速重启、多进程监听、负载分摊等场景。这里小 G 建议先记住:
+
+**TCP 和 UDP 分别监听同一个数字端口,靠的是协议不同,不需要 `SO_REUSEPORT`。`SO_REUSEADDR` / `SO_REUSEPORT` 主要影响同一协议下的地址端口复用、快速重启和多进程监听,但是否允许、如何分流,要看操作系统和具体 socket 类型。**
+
+## 分享两个实际案例
+
+### DNS 为什么同时用 TCP/UDP 53?
+
+
+
+DNS 是最经典的例子。IANA 注册表里,`domain` 服务同时注册了 `TCP/53` 和 `UDP/53`,实际 DNS 服务也经常同时监听这两个端口。
+
+日常域名查询通常走 UDP,因为查询和响应比较小,UDP 不需要建连,速度快。但以下几种情况会切换到 TCP:UDP 响应被截断(DNS 报文头 `TC` 标志位置 1,常见于响应超过 UDP 长度限制时)、区域传送(Zone Transfer,需要可靠传输保证数据完整性)、或者 DNSSEC 响应过大。这里不是“`UDP/53` 被占了,所以 `TCP/53` 不能用”,而是 DNS 本来就可以同时使用两套协议的 `53`。
+
+### HTTPS 和 HTTP/3 的 443 也是这个道理
+
+传统 HTTPS 通常是 HTTP/1.1 或 HTTP/2 over TLS over TCP,默认使用 `TCP/443`。HTTP/3 跑在 QUIC 上,而 QUIC 基于 UDP。浏览器通常会通过 `Alt-Svc` 或 `HTTPS` DNS 记录获知服务端支持 HTTP/3,然后尝试建立 QUIC 连接;常见部署是同时开放 `TCP/443` 和 `UDP/443`。
+
+
+
+这不会和原来的 `TCP/443` 冲突。一个服务器完全可以同时提供:
+
+```text
+HTTPS(HTTP/1.1、HTTP/2) -> TCP/443
+HTTP/3 -> UDP/443
+```
+
+从外部看都是 `443`,从协议栈看是两条路。
+
+生产环境里也要注意:只放行 `TCP/443` 时,HTTP/1.1 和 HTTP/2 可能都正常,但 HTTP/3 不会生效。云安全组、负载均衡、Nginx / 网关和主机防火墙都要分别检查 `TCP/443` 和 `UDP/443`,再用 `curl --http3` 或浏览器开发者工具确认协议是否真的切到 HTTP/3。
+
+## 面试怎么回答?
+
+TCP 和 UDP 可以使用同一个数字端口,因为它们是不同的传输层协议;内核会先按 IP 协议号分发到 TCP 栈或 UDP 栈,再在各自协议栈内按地址和端口找 socket,所以 `TCP/8080` 和 `UDP/8080` 可以共存。
+
+真正容易冲突的是同协议下的绑定,比如两个 TCP 服务通常不能同时监听同一个本地 IP 和端口;这时才会涉及 `SO_REUSEADDR`、`SO_REUSEPORT` 这类 socket 复用选项。例子记两个就够了:DNS 同时使用 `UDP/53` 和 `TCP/53`;HTTP/3 常见部署是 `UDP/443`,可以和传统 HTTPS 的 `TCP/443` 同时存在。
diff --git a/docs/cs-basics/network/computer-network-xiexiren-summary.md b/docs/cs-basics/network/computer-network-xiexiren-summary.md
index fc9f60fd39a..f82a47d56ad 100644
--- a/docs/cs-basics/network/computer-network-xiexiren-summary.md
+++ b/docs/cs-basics/network/computer-network-xiexiren-summary.md
@@ -1,22 +1,36 @@
---
title: 《计算机网络》(谢希仁)内容总结
+description: 基于《计算机网络》教材的学习笔记,梳理术语与分层模型等核心知识点,便于期末复习与面试巩固。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: 计算机网络,谢希仁,术语,分层模型,链路,主机,教材总结
---
-本文是我在大二学习计算机网络期间整理, 大部分内容都来自于谢希仁老师的[《计算机网络》第七版](https://www.elias.ltd/usr/local/etc/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%88%E7%AC%AC7%E7%89%88%EF%BC%89%E8%B0%A2%E5%B8%8C%E4%BB%81.pdf)这本书。为了内容更容易理解,我对之前的整理进行了一波重构,并配上了一些相关的示意图便于理解。
+这篇笔记来自我大二学习计算机网络时的整理,大部分内容参考谢希仁老师的[《计算机网络》第七版](https://www.elias.ltd/usr/local/etc/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%88%E7%AC%AC7%E7%89%88%EF%BC%89%E8%B0%A2%E5%B8%8C%E4%BB%81.pdf)。
-
+计算机网络教材内容很散:术语、分层、链路、路由、运输层、应用层都要串起来看。为了复习起来更顺,我对原来的笔记做了一次重构,并补充了一些示意图。
-相关问题:[如何评价谢希仁的计算机网络(第七版)? - 知乎](https://www.zhihu.com/question/327872966) 。
+这篇文章主要回答几个问题:
+
+1. 计算机网络里常见基础术语分别是什么意思?
+2. OSI、TCP/IP 分层模型分别如何理解?
+3. 链路层、网络层、运输层、应用层各自解决什么问题?
+4. 复习《计算机网络》这本书时,哪些概念最容易混淆?
+
+
+
+相关问题:[如何评价谢希仁的计算机网络(第七版)? - 知乎](https://www.zhihu.com/question/327872966)。
## 1. 计算机网络概述
### 1.1. 基本术语
-1. **结点 (node)**:网络中的结点可以是计算机,集线器,交换机或路由器等。
-2. **链路(link )** : 从一个结点到另一个结点的一段物理线路。中间没有任何其他交点。
+1. **结点(node)**:网络中的结点可以是计算机,集线器,交换机或路由器等。
+2. **链路(link)**:从一个结点到另一个结点的一段物理线路。中间没有任何其他交点。
3. **主机(host)**:连接在因特网上的计算机。
4. **ISP(Internet Service Provider)**:因特网服务提供者(提供商)。
@@ -28,7 +42,7 @@ tag:
https://labs.ripe.net/Members/fergalc/ixp-traffic-during-stratos-skydive
-6. **RFC(Request For Comments)**:意思是“请求评议”,包含了关于 Internet 几乎所有的重要的文字资料。
+6. **RFC(Request For Comments)**:意思是“请求评议”,包含了关于 Internet 几乎所有的重要的文字资料。
7. **广域网 WAN(Wide Area Network)**:任务是通过长距离运送主机发送的数据。
8. **城域网 MAN(Metropolitan Area Network)**:用来将多个局域网进行互连。
9. **局域网 LAN(Local Area Network)**:学校或企业大多拥有多个互连的局域网。
@@ -37,19 +51,19 @@ tag:
http://conexionesmanwman.blogspot.com/
-10. **个人区域网 PAN(Personal Area Network)**:在个人工作的地方把属于个人使用的电子设备用无线技术连接起来的网络 。
+10. **个人区域网 PAN(Personal Area Network)**:在个人工作的地方把属于个人使用的电子设备用无线技术连接起来的网络。

- https://www.itrelease.com/2018/07/advantages-and-disadvantages-of-personal-area-network-pan/
+ https://www.itrelease.com/2018/07/advantages-and-disadvantages-of-personal-area-network-pan/
-11. **分组(packet )**:因特网中传送的数据单元。由首部 header 和数据段组成。分组又称为包,首部可称为包头。
-12. **存储转发(store and forward )**:路由器收到一个分组,先检查分组是否正确,并过滤掉冲突包错误。确定包正确后,取出目的地址,通过查找表找到想要发送的输出端口地址,然后将该包发送出去。
+11. **分组(packet)**:因特网中传送的数据单元。由首部 header 和数据段组成。分组又称为包,首部可称为包头。
+12. **存储转发(store and forward)**:路由器收到一个分组,先检查分组是否正确,并过滤掉冲突包错误。确定包正确后,取出目的地址,通过查找表找到想要发送的输出端口地址,然后将该包发送出去。
- 
+ 
13. **带宽(bandwidth)**:在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为 b/s。
-14. **吞吐量(throughput )**:表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。
+14. **吞吐量(throughput)**:表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。
### 1.2. 重要知识点总结
@@ -64,7 +78,7 @@ tag:
9. 网络协议即协议,是为进行网络中的数据交换而建立的规则。计算机网络的各层以及其协议集合,称为网络的体系结构。
10. **五层体系结构由应用层,运输层,网络层(网际层),数据链路层,物理层组成。运输层最主要的协议是 TCP 和 UDP 协议,网络层最重要的协议是 IP 协议。**
-
+
下面的内容会介绍计算机网络的五层体系结构:**物理层+数据链路层+网络层(网际层)+运输层+应用层**。
@@ -76,31 +90,31 @@ tag:
1. **数据(data)**:运送消息的实体。
2. **信号(signal)**:数据的电气的或电磁的表现。或者说信号是适合在传输介质上传输的对象。
-3. **码元( code)**:在使用时间域(或简称为时域)的波形来表示数字信号时,代表不同离散数值的基本波形。
-4. **单工(simplex )**:只能有一个方向的通信而没有反方向的交互。
-5. **半双工(half duplex )**:通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)。
+3. **码元(code)**:在使用时间域(或简称为时域)的波形来表示数字信号时,代表不同离散数值的基本波形。
+4. **单工(simplex)**:只能有一个方向的通信而没有反方向的交互。
+5. **半双工(half duplex)**:通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)。
6. **全双工(full duplex)**:通信的双方可以同时发送和接收信息。
- 
+ 
7. **失真**:失去真实性,主要是指接受到的信号和发送的信号不同,有磨损和衰减。影响失真程度的因素:1.码元传输速率 2.信号传输距离 3.噪声干扰 4.传输媒体质量
- 
+ 
8. **奈氏准则**:在任何信道中,码元的传输的效率是有上限的,传输速率超过此上限,就会出现严重的码间串扰问题,使接收端对码元的判决(即识别)成为不可能。
9. **香农定理**:在带宽受限且有噪声的信道中,为了不产生误差,信息的数据传输速率有上限值。
10. **基带信号(baseband signal)**:来自信源的信号。指没有经过调制的数字信号或模拟信号。
11. **带通(频带)信号(bandpass signal)**:把基带信号经过载波调制后,把信号的频率范围搬移到较高的频段以便在信道中传输(即仅在一段频率范围内能够通过信道),这里调制过后的信号就是带通信号。
-12. **调制(modulation )**:对信号源的信息进行处理后加到载波信号上,使其变为适合在信道传输的形式的过程。
-13. **信噪比(signal-to-noise ratio )**:指信号的平均功率和噪声的平均功率之比,记为 S/N。信噪比(dB)=10\*log10(S/N)。
-14. **信道复用(channel multiplexing )**:指多个用户共享同一个信道。(并不一定是同时)。
+12. **调制(modulation)**:对信号源的信息进行处理后加到载波信号上,使其变为适合在信道传输的形式的过程。
+13. **信噪比(signal-to-noise ratio)**:指信号的平均功率和噪声的平均功率之比,记为 S/N。信噪比(dB)=10\*log10(S/N)。
+14. **信道复用(channel multiplexing)**:指多个用户共享同一个信道。(并不一定是同时)。

-15. **比特率(bit rate )**:单位时间(每秒)内传送的比特数。
+15. **比特率(bit rate)**:单位时间(每秒)内传送的比特数。
16. **波特率(baud rate)**:单位时间载波调制状态改变的次数。针对数据信号对载波的调制速率。
17. **复用(multiplexing)**:共享信道的方法。
-18. **ADSL(Asymmetric Digital Subscriber Line )**:非对称数字用户线。
+18. **ADSL(Asymmetric Digital Subscriber Line)**:非对称数字用户线。
19. **光纤同轴混合网(HFC 网)**:在目前覆盖范围很广的有线电视网的基础上开发的一种居民宽带接入网
### 2.2. 重要知识点总结
@@ -125,11 +139,11 @@ tag:
#### 2.3.2. 几种常用的信道复用技术
-1. **频分复用(FDM)**:所有用户在同样的时间占用不同的带宽资源。
+1. **频分复用(FDM)**:所有用户在同样的时间占用不同的带宽资源。
2. **时分复用(TDM)**:所有用户在不同的时间占用同样的频带宽度(分时不分频)。
-3. **统计时分复用 (Statistic TDM)**:改进的时分复用,能够明显提高信道的利用率。
-4. **码分复用(CDM)**:用户使用经过特殊挑选的不同码型,因此各用户之间不会造成干扰。这种系统发送的信号有很强的抗干扰能力,其频谱类似于白噪声,不易被敌人发现。
-5. **波分复用( WDM)**:波分复用就是光的频分复用。
+3. **统计时分复用(Statistic TDM)**:改进的时分复用,能够明显提高信道的利用率。
+4. **码分复用(CDM)**:用户使用经过特殊挑选的不同码型,因此各用户之间不会造成干扰。这种系统发送的信号有很强的抗干扰能力,其频谱类似于白噪声,不易被敌人发现。
+5. **波分复用(WDM)**:波分复用就是光的频分复用。
#### 2.3.3. 几种常用的宽带接入技术,主要是 ADSL 和 FTTx
@@ -145,16 +159,16 @@ tag:
2. **数据链路(data link)**:把实现控制数据运输的协议的硬件和软件加到链路上就构成了数据链路。
3. **循环冗余检验 CRC(Cyclic Redundancy Check)**:为了保证数据传输的可靠性,CRC 是数据链路层广泛使用的一种检错技术。
4. **帧(frame)**:一个数据链路层的传输单元,由一个数据链路层首部和其携带的封包所组成协议数据单元。
-5. **MTU(Maximum Transfer Uint )**:最大传送单元。帧的数据部分的的长度上限。
-6. **误码率 BER(Bit Error Rate )**:在一段时间内,传输错误的比特占所传输比特总数的比率。
-7. **PPP(Point-to-Point Protocol )**:点对点协议。即用户计算机和 ISP 进行通信时所使用的数据链路层协议。以下是 PPP 帧的示意图:
- 
-8. **MAC 地址(Media Access Control 或者 Medium Access Control)**:意译为媒体访问控制,或称为物理地址、硬件地址,用来定义网络设备的位置。在 OSI 模型中,第三层网络层负责 IP 地址,第二层数据链路层则负责 MAC 地址。因此一个主机会有一个 MAC 地址,而每个网络位置会有一个专属于它的 IP 地址 。地址是识别某个系统的重要标识符,“名字指出我们所要寻找的资源,地址指出资源所在的地方,路由告诉我们如何到达该处。”
+5. **MTU(Maximum Transfer Uint)**:最大传送单元。帧的数据部分的长度上限。
+6. **误码率 BER(Bit Error Rate)**:在一段时间内,传输错误的比特占所传输比特总数的比率。
+7. **PPP(Point-to-Point Protocol)**:点对点协议。即用户计算机和 ISP 进行通信时所使用的数据链路层协议。以下是 PPP 帧的示意图:
+ 
+8. **MAC 地址(Media Access Control 或者 Medium Access Control)**:意译为媒体访问控制,或称为物理地址、硬件地址,用来定义网络设备的位置。在 OSI 模型中,第三层网络层负责 IP 地址,第二层数据链路层则负责 MAC 地址。因此一个主机会有一个 MAC 地址,而每个网络位置会有一个专属于它的 IP 地址。地址是识别某个系统的重要标识符,“名字指出我们所要寻找的资源,地址指出资源所在的地方,路由告诉我们如何到达该处。”

9. **网桥(bridge)**:一种用于数据链路层实现中继,连接两个或多个局域网的网络互连设备。
-10. **交换机(switch )**:广义的来说,交换机指的是一种通信系统中完成信息交换的设备。这里工作在数据链路层的交换机指的是交换式集线器,其实质是一个多接口的网桥
+10. **交换机(switch)**:广义的来说,交换机指的是一种通信系统中完成信息交换的设备。这里工作在数据链路层的交换机指的是交换式集线器,其实质是一个多接口的网桥
### 3.2. 重要知识点总结
@@ -185,13 +199,13 @@ tag:
### 4.1. 基本术语
1. **虚电路(Virtual Circuit)** : 在两个终端设备的逻辑或物理端口之间,通过建立的双向的透明传输通道。虚电路表示这只是一条逻辑上的连接,分组都沿着这条逻辑连接按照存储转发方式传送,而并不是真正建立了一条物理连接。
-2. **IP(Internet Protocol )** : 网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一,是 TCP/IP 体系结构网际层的核心。配套的有 ARP,RARP,ICMP,IGMP。
+2. **IP(Internet Protocol)**:网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一,是 TCP/IP 体系结构网际层的核心。配套的有 ARP,RARP,ICMP,IGMP。
3. **ARP(Address Resolution Protocol)** : 地址解析协议。地址解析协议 ARP 把 IP 地址解析为硬件地址。
-4. **ICMP(Internet Control Message Protocol )**:网际控制报文协议 (ICMP 允许主机或路由器报告差错情况和提供有关异常情况的报告)。
-5. **子网掩码(subnet mask )**:它是一种用来指明一个 IP 地址的哪些位标识的是主机所在的子网以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合 IP 地址一起使用。
-6. **CIDR( Classless Inter-Domain Routing )**:无分类域间路由选择 (特点是消除了传统的 A 类、B 类和 C 类地址以及划分子网的概念,并使用各种长度的“网络前缀”(network-prefix)来代替分类地址中的网络号和子网号)。
+4. **ICMP(Internet Control Message Protocol)**:网际控制报文协议(ICMP 允许主机或路由器报告差错情况和提供有关异常情况的报告)。
+5. **子网掩码(subnet mask)**:它是一种用来指明一个 IP 地址的哪些位标识的是主机所在的子网以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合 IP 地址一起使用。
+6. **CIDR(Classless Inter-Domain Routing)**:无分类域间路由选择(特点是消除了传统的 A 类、B 类和 C 类地址以及划分子网的概念,并使用各种长度的“网络前缀”(network-prefix)来代替分类地址中的网络号和子网号)。
7. **默认路由(default route)**:当在路由表中查不到能到达目的地址的路由时,路由器选择的路由。默认路由还可以减小路由表所占用的空间和搜索路由表所用的时间。
-8. **路由选择算法(Virtual Circuit)**:路由选择协议的核心部分。因特网采用自适应的,分层次的路由选择协议。
+8. **路由选择算法(Routing Algorithm)**:路由选择协议的核心部分。因特网采用自适应的、分层次的路由选择协议。
### 4.2. 重要知识点总结
@@ -222,7 +236,7 @@ tag:
6. **端口(port)**:端口的目的是为了确认对方机器的哪个进程在与自己进行交互,比如 MSN 和 QQ 的端口不同,如果没有端口就可能出现 QQ 进程和 MSN 交互错误。端口又称协议端口号。
7. **停止等待协议(stop-and-wait)**:指发送方每发送完一个分组就停止发送,等待对方确认,在收到确认之后在发送下一个分组。
-8. **流量控制** : 就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。
+8. **流量控制**:就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。
9. **拥塞控制**:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。
### 5.2. 重要知识点总结
@@ -230,8 +244,8 @@ tag:
1. **运输层提供应用进程之间的逻辑通信,也就是说,运输层之间的通信并不是真正在两个运输层之间直接传输数据。运输层向应用层屏蔽了下面网络的细节(如网络拓补,所采用的路由选择协议等),它使应用进程之间看起来好像两个运输层实体之间有一条端到端的逻辑通信信道。**
2. **网络层为主机提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。**
3. 运输层的两个重要协议是用户数据报协议 UDP 和传输控制协议 TCP。按照 OSI 的术语,两个对等运输实体在通信时传送的数据单位叫做运输协议数据单元 TPDU(Transport Protocol Data Unit)。但在 TCP/IP 体系中,则根据所使用的协议是 TCP 或 UDP,分别称之为 TCP 报文段或 UDP 用户数据报。
-4. **UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式。 TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的传输服务,难以避免地增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。**
-5. 硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层各种协议进程与运输实体进行层间交互的一种地址。UDP 和 TCP 的首部格式中都有源端口和目的端口这两个重要字段。当运输层收到 IP 层交上来的运输层报文时,就能够根据其首部中的目的端口号把数据交付应用层的目的应用层。(两个进程之间进行通信不光要知道对方 IP 地址而且要知道对方的端口号(为了找到对方计算机中的应用进程))
+4. **UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式。TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的传输服务,难以避免地增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。**
+5. 硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层各种协议进程与运输实体进行层间交互的一种地址。UDP 和 TCP 的首部格式中都有源端口和目的端口这两个重要字段。当运输层收到 IP 层交上来的运输层报文时,就能够根据其首部中的目的端口号把数据交付应用层的目的应用层。(两个进程之间进行通信不光要知道对方 IP 地址而且要知道对方的端口号(为了找到对方计算机中的应用进程))
6. 运输层用一个 16 位端口号标志一个端口。端口号只有本地意义,它只是为了标志计算机应用层中的各个进程在和运输层交互时的层间接口。在互联网的不同计算机中,相同的端口号是没有关联的。协议端口号简称端口。虽然通信的终点是应用进程,但只要把所发送的报文交到目的主机的某个合适端口,剩下的工作(最后交付目的进程)就由 TCP 和 UDP 来完成。
7. 运输层的端口号分为服务器端使用的端口号(0˜1023 指派给熟知端口,1024˜49151 是登记端口号)和客户端暂时使用的端口号(49152˜65535)
8. **UDP 的主要特点是 ① 无连接 ② 尽最大努力交付 ③ 面向报文 ④ 无拥塞控制 ⑤ 支持一对一,一对多,多对一和多对多的交互通信 ⑥ 首部开销小(只有四个字段:源端口,目的端口,长度和检验和)**
@@ -240,7 +254,7 @@ tag:
11. 停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
12. 为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停下来等待对方确认。这样可使信道上一直有数据不间断的在传送。这种传输方式可以明显提高信道利用率。
13. 停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求 ARQ。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续 ARQ 协议可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。
-14. TCP 报文段的前 20 个字节是固定的,其后有 40 字节长度的可选字段。如果加入可选字段后首部长度不是 4 的整数倍字节,需要在再在之后用 0 填充。因此,TCP 首部的长度取值为 20+4n 字节,最长为 60 字节。
+14. TCP 报文段的前 20 个字节是固定的,其后有 40 字节长度的可选字段。如果加入可选字段后首部长度不是 4 的整数倍字节,需要在再在之后用 0 填充。因此,TCP 首部的长度取值为 20+4n 字节,最长为 60 字节。
15. **TCP 使用滑动窗口机制。发送窗口里面的序号表示允许发送的序号。发送窗口后沿的后面部分表示已发送且已收到确认,而发送窗口前沿的前面部分表示不允许发送。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口的前沿通常是不断向前移动的。一般来说,我们总是希望数据传输更快一些。但如果发送方把数据发送的过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。**
16. 在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
17. **为了进行拥塞控制,TCP 发送方要维持一个拥塞窗口 cwnd 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。**
@@ -265,46 +279,46 @@ tag:
### 6.1. 基本术语
-1. **域名系统(DNS)**:域名系统(DNS,Domain Name System)将人类可读的域名 (例如,www.baidu.com) 转换为机器可读的 IP 地址 (例如,220.181.38.148)。我们可以将其理解为专为互联网设计的电话薄。
+1. **域名系统(DNS)**:域名系统(DNS,Domain Name System)将人类可读的域名(例如,www.baidu.com)转换为机器可读的 IP 地址(例如,220.181.38.148)。我们可以将其理解为专为互联网设计的电话薄。
- 
+ 
https://www.seobility.net/en/wiki/HTTP_headers
-2. **文件传输协议(FTP)**:FTP 是 File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于 Internet 上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的 FTP 应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在 FTP 的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。 "下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用 Internet 语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。
+2. **文件传输协议(FTP)**:FTP 是 File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于 Internet 上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的 FTP 应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在 FTP 的使用当中,用户经常遇到两个概念:“下载”(Download)和“上传”(Upload)。 “下载”文件就是从远程主机拷贝文件至自己的计算机上;“上传”文件就是将文件从自己的计算机中拷贝至远程主机上。用 Internet 语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。

3. **简单文件传输协议(TFTP)**:TFTP(Trivial File Transfer Protocol,简单文件传输协议)是 TCP/IP 协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为 69。
4. **远程终端协议(TELNET)**:Telnet 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用 telnet 程序,用它连接到服务器。终端使用者可以在 telnet 程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个 telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。
-5. **万维网(WWW)**:WWW 是环球信息网的缩写,(亦作“Web”、“WWW”、“'W3'”,英文全称为“World Wide Web”),中文名字为“万维网”,"环球网"等,常简称为 Web。分为 Web 客户端和 Web 服务器程序。WWW 可以让 Web 客户端(常用浏览器)访问浏览 Web 服务器上的页面。是一个由许多互相链接的超文本组成的系统,通过互联网访问。在这个系统中,每个有用的事物,称为一样“资源”;并且由一个全局“统一资源标识符”(URI)标识;这些资源通过超文本传输协议(Hypertext Transfer Protocol)传送给用户,而后者通过点击链接来获得资源。万维网联盟(英语:World Wide Web Consortium,简称 W3C),又称 W3C 理事会。1994 年 10 月在麻省理工学院(MIT)计算机科学实验室成立。万维网联盟的创建者是万维网的发明者蒂姆·伯纳斯-李。万维网并不等同互联网,万维网只是互联网所能提供的服务其中之一,是靠着互联网运行的一项服务。
+5. **万维网(WWW)**:WWW 是环球信息网的缩写,(亦作“Web”、“WWW”、“'W3'”,英文全称为“World Wide Web”),中文名字为“万维网”,“环球网”等,常简称为 Web。分为 Web 客户端和 Web 服务器程序。WWW 可以让 Web 客户端(常用浏览器)访问浏览 Web 服务器上的页面。是一个由许多互相链接的超文本组成的系统,通过互联网访问。在这个系统中,每个有用的事物,称为一样“资源”;并且由一个全局“统一资源标识符”(URI)标识;这些资源通过超文本传输协议(Hypertext Transfer Protocol)传送给用户,而后者通过点击链接来获得资源。万维网联盟(英语:World Wide Web Consortium,简称 W3C),又称 W3C 理事会。1994 年 10 月在麻省理工学院(MIT)计算机科学实验室成立。万维网联盟的创建者是万维网的发明者蒂姆·伯纳斯-李。万维网并不等同互联网,万维网只是互联网所能提供的服务其中之一,是靠着互联网运行的一项服务。
6. **万维网的大致工作工程:**

7. **统一资源定位符(URL)**:统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
-8. **超文本传输协议(HTTP)**:超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。1960 年美国人 Ted Nelson 构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了 HTTP 超文本传输协议标准架构的发展根基。
+8. **超文本传输协议(HTTP)**:超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。1960 年美国人 Ted Nelson 构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了 HTTP 超文本传输协议标准架构的发展根基。
HTTP 协议的本质就是一种浏览器与服务器之间约定好的通信格式。HTTP 的原理如下图所示:
- 
+ 
-9. **代理服务器(Proxy Server)**:代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
-10. **简单邮件传输协议(SMTP)** : SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。
+9. **代理服务器(Proxy Server)**:代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
+10. **简单邮件传输协议(SMTP)**:SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。

https://www.campaignmonitor.com/resources/knowledge-base/what-is-the-code-that-makes-bcc-or-cc-operate-in-an-email/
-11. **搜索引擎** :搜索引擎(Search Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。
+11. **搜索引擎**:搜索引擎(Search Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。
12. **垂直搜索引擎**:垂直搜索引擎是针对某一个行业的专业搜索引擎,是搜索引擎的细分和延伸,是对网页库中的某类专门的信息进行一次整合,定向分字段抽取出需要的数据进行处理后再以某种形式返回给用户。垂直搜索是相对通用搜索引擎的信息量大、查询不准确、深度不够等提出来的新的搜索引擎服务模式,通过针对某一特定领域、某一特定人群或某一特定需求提供的有一定价值的信息和相关服务。其特点就是“专、精、深”,且具有行业色彩,相比较通用搜索引擎的海量信息无序化,垂直搜索引擎则显得更加专注、具体和深入。
13. **全文索引** :全文索引技术是目前搜索引擎的关键技术。试想在 1M 大小的文件中搜索一个词,可能需要几秒,在 100M 的文件中可能需要几十秒,如果在更大的文件中搜索那么就需要更大的系统开销,这样的开销是不现实的。所以在这样的矛盾下出现了全文索引技术,有时候有人叫倒排文档技术。
-14. **目录索引**:目录索引( search index/directory),顾名思义就是将网站分门别类地存放在相应的目录中,因此用户在查询信息时,可选择关键词搜索,也可按分类目录逐层查找。
+14. **目录索引**:目录索引(search index/directory),顾名思义就是将网站分门别类地存放在相应的目录中,因此用户在查询信息时,可选择关键词搜索,也可按分类目录逐层查找。
### 6.2. 重要知识点总结
-1. 文件传输协议(FTP)使用 TCP 可靠的运输服务。FTP 使用客户服务器方式。一个 FTP 服务器进程可以同时为多个用户提供服务。在进行文件传输时,FTP 的客户和服务器之间要先建立两个并行的 TCP 连接:控制连接和数据连接。实际用于传输文件的是数据连接。
+1. 文件传输协议(FTP)使用 TCP 可靠的运输服务。FTP 使用客户服务器方式。一个 FTP 服务器进程可以同时为多个用户提供服务。在进行文件传输时,FTP 的客户和服务器之间要先建立两个并行的 TCP 连接:控制连接和数据连接。实际用于传输文件的是数据连接。
2. 万维网客户程序与服务器之间进行交互使用的协议是超文本传输协议 HTTP。HTTP 使用 TCP 连接进行可靠传输。但 HTTP 本身是无连接、无状态的。HTTP/1.1 协议使用了持续连接(分为非流水线方式和流水线方式)
3. 电子邮件把邮件发送到收件人使用的邮件服务器,并放在其中的收件人邮箱中,收件人可随时上网到自己使用的邮件服务器读取,相当于电子邮箱。
4. 一个电子邮件系统有三个重要组成构件:用户代理、邮件服务器、邮件协议(包括邮件发送协议,如 SMTP,和邮件读取协议,如 POP3 和 IMAP)。用户代理和邮件服务器都要运行这些协议。
diff --git a/docs/cs-basics/network/dns.md b/docs/cs-basics/network/dns.md
index 3d3ef0e2254..8ffb5832bad 100644
--- a/docs/cs-basics/network/dns.md
+++ b/docs/cs-basics/network/dns.md
@@ -1,30 +1,56 @@
---
title: DNS 域名系统详解(应用层)
+description: 详解 DNS 的层次结构与解析流程,覆盖递归/迭代、缓存与权威服务器,明确应用层端口与性能优化要点。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: DNS,域名解析,递归查询,迭代查询,缓存,权威DNS,端口53,UDP
---
-DNS(Domain Name System)域名管理系统,是当用户使用浏览器访问网址之后,使用的第一个重要协议。DNS 要解决的是**域名和 IP 地址的映射问题**。
+在浏览器地址栏输入域名之后,真正发起 HTTP 请求之前,通常要先经过 DNS 解析。
-
+DNS 要解决的是**域名和 IP 地址的映射问题**。它看起来只是“把域名翻译成 IP”,但背后涉及本地缓存、递归查询、迭代查询、权威服务器、根服务器、UDP/TCP 切换等一整套机制。
-在实际使用中,有一种情况下,浏览器是可以不必动用 DNS 就可以获知域名和 IP 地址的映射的。浏览器在本地会维护一个`hosts`列表,一般来说浏览器要先查看要访问的域名是否在`hosts`列表中,如果有的话,直接提取对应的 IP 地址记录,就好了。如果本地`hosts`列表内没有域名-IP 对应记录的话,那么 DNS 就闪亮登场了。
+这篇文章主要回答几个问题:
-目前 DNS 的设计采用的是分布式、层次数据库结构,**DNS 是应用层协议,基于 UDP 协议之上,端口为 53** 。
+1. DNS 为什么需要分层设计?
+2. 一次完整的域名解析通常会经过哪些步骤?
+3. 递归查询和迭代查询有什么区别?
+4. DNS 为什么通常基于 UDP,什么情况下会改用 TCP?
+
+
+
+在实际使用中,有一种情况下,浏览器是可以不必动用 DNS 就可以获知域名和 IP 地址的映射的。浏览器在本地会维护一个 `hosts` 列表,一般来说浏览器要先查看要访问的域名是否在 `hosts` 列表中,如果有的话,直接提取对应的 IP 地址记录,就好了。如果本地 `hosts` 列表内没有域名-IP 对应记录的话,那么 DNS 就闪亮登场了。
+
+目前 DNS 的设计采用的是分布式、层次数据库结构,**DNS 是应用层协议,通常基于 UDP 协议,端口为 53**。当响应数据超过 UDP 报文长度限制(512 字节,EDNS0 可扩展至更大)或进行区域传送(Zone Transfer)时,会改用 TCP 协议以保证数据完整性。

## DNS 服务器
-DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务器都属于以下四个类别之一):
+DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务器都属于以下四个类别之一):
- 根 DNS 服务器。根 DNS 服务器提供 TLD 服务器的 IP 地址。目前世界上只有 13 组根服务器,我国境内目前仍没有根服务器。
-- 顶级域 DNS 服务器(TLD 服务器)。顶级域是指域名的后缀,如`com`、`org`、`net`和`edu`等。国家也有自己的顶级域,如`uk`、`fr`和`ca`。TLD 服务器提供了权威 DNS 服务器的 IP 地址。
+- 顶级域 DNS 服务器(TLD 服务器)。顶级域是指域名的后缀,如 `com`、`org`、`net` 和 `edu` 等。国家也有自己的顶级域,如 `uk`、`fr` 和 `ca`。TLD 服务器提供了权威 DNS 服务器的 IP 地址。
- 权威 DNS 服务器。在因特网上具有公共可访问主机的每个组织机构必须提供公共可访问的 DNS 记录,这些记录将这些主机的名字映射为 IP 地址。
- 本地 DNS 服务器。每个 ISP(互联网服务提供商)都有一个自己的本地 DNS 服务器。当主机发出 DNS 请求时,该请求被发往本地 DNS 服务器,它起着代理的作用,并将该请求转发到 DNS 层次结构中。严格说来,不属于 DNS 层级结构。
-世界上并不是只有 13 台根服务器,这是很多人普遍的误解,网上很多文章也是这么写的。实际上,现在根服务器数量远远超过这个数量。最初确实是为 DNS 根服务器分配了 13 个 IP 地址,每个 IP 地址对应一个不同的根 DNS 服务器。然而,由于互联网的快速发展和增长,这个原始的架构变得不太适应当前的需求。为了提高 DNS 的可靠性、安全性和性能,目前这 13 个 IP 地址中的每一个都有多个服务器,截止到 2023 年底,所有根服务器之和达到了 600 多台,未来还会继续增加。
+**世界上真的只有 13 台根服务器吗?** 这是一个流传已久的技术误解。如果你在网上搜索,仍能看到许多陈旧文章宣称“全球仅有 13 台根服务器,且全部由美国控制”。
+
+**事实并非如此。**
+
+最初在设计 DNS(域名系统)架构时,受限于早期 IPv4 数据包的大小限制(UDP 报文需控制在 512 字节以内),预留给根服务器地址的空间确实只够容纳 13 个 IP 地址,每个 IP 地址对应一个不同的根 DNS 服务器。这 13 个地址分别被命名为 `a.root-servers.net` 到 `m.root-servers.net`。
+
+虽然**逻辑上**只有 13 个 IP 地址,但随着互联网规模的爆发,物理上的“单一服务器”早已无法承载全球的查询压力。为了提升 DNS 的可靠性、安全性和响应速度,技术人员引入了 **IP 任播(Anycast)** 技术。
+
+通过任播技术,每一个逻辑 IP 地址背后都可以对应成百上千台分布在全球各地的物理服务器。当你发起查询请求时,互联网路由协议(BGP)会自动将请求引导至地理位置或网络路径上离你**最近**的那台物理实例。
+
+截止到 2023 年底,全球根服务器物理实例总数已超过 1700 台。根据 **[Root-Servers.org](https://root-servers.org/)** 的最新实时监测数据,到 **2026 年,全球根服务器物理实例已突破 1900+ 台**,并正向 2000 台大关迈进。
+
+
## DNS 工作流程
@@ -35,22 +61,22 @@ DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务
下图是实践中常采用的方式,从请求主机到本地 DNS 服务器的查询是递归的,其余的查询时迭代的。
-
+
-现在,主机`cis.poly.edu`想知道`gaia.cs.umass.edu`的 IP 地址。假设主机`cis.poly.edu`的本地 DNS 服务器为`dns.poly.edu`,并且`gaia.cs.umass.edu`的权威 DNS 服务器为`dns.cs.umass.edu`。
+现在,主机 `cis.poly.edu` 想知道 `gaia.cs.umass.edu` 的 IP 地址。假设主机 `cis.poly.edu` 的本地 DNS 服务器为 `dns.poly.edu`,并且 `gaia.cs.umass.edu` 的权威 DNS 服务器为 `dns.cs.umass.edu`。
-1. 首先,主机`cis.poly.edu`向本地 DNS 服务器`dns.poly.edu`发送一个 DNS 请求,该查询报文包含被转换的域名`gaia.cs.umass.edu`。
-2. 本地 DNS 服务器`dns.poly.edu`检查本机缓存,发现并无记录,也不知道`gaia.cs.umass.edu`的 IP 地址该在何处,不得不向根服务器发送请求。
-3. 根服务器注意到请求报文中含有`edu`顶级域,因此告诉本地 DNS,你可以向`edu`的 TLD DNS 发送请求,因为目标域名的 IP 地址很可能在那里。
-4. 本地 DNS 获取到了`edu`的 TLD DNS 服务器地址,向其发送请求,询问`gaia.cs.umass.edu`的 IP 地址。
-5. `edu`的 TLD DNS 服务器仍不清楚请求域名的 IP 地址,但是它注意到该域名有`umass.edu`前缀,因此返回告知本地 DNS,`umass.edu`的权威服务器可能记录了目标域名的 IP 地址。
-6. 这一次,本地 DNS 将请求发送给权威 DNS 服务器`dns.cs.umass.edu`。
-7. 终于,由于`gaia.cs.umass.edu`向权威 DNS 服务器备案过,在这里有它的 IP 地址记录,权威 DNS 成功地将 IP 地址返回给本地 DNS。
+1. 首先,主机 `cis.poly.edu` 向本地 DNS 服务器 `dns.poly.edu` 发送一个 DNS 请求,该查询报文包含被转换的域名 `gaia.cs.umass.edu`。
+2. 本地 DNS 服务器 `dns.poly.edu` 检查本机缓存,发现并无记录,也不知道 `gaia.cs.umass.edu` 的 IP 地址该在何处,不得不向根服务器发送请求。
+3. 根服务器注意到请求报文中含有 `edu` 顶级域,因此告诉本地 DNS,你可以向 `edu` 的 TLD DNS 发送请求,因为目标域名的 IP 地址很可能在那里。
+4. 本地 DNS 获取到了 `edu` 的 TLD DNS 服务器地址,向其发送请求,询问 `gaia.cs.umass.edu` 的 IP 地址。
+5. `edu` 的 TLD DNS 服务器仍不清楚请求域名的 IP 地址,但是它注意到该域名有 `umass.edu` 前缀,因此返回告知本地 DNS,`umass.edu` 的权威服务器可能记录了目标域名的 IP 地址。
+6. 这一次,本地 DNS 将请求发送给权威 DNS 服务器 `dns.cs.umass.edu`。
+7. 终于,由于 `gaia.cs.umass.edu` 向权威 DNS 服务器备案过,在这里有它的 IP 地址记录,权威 DNS 成功地将 IP 地址返回给本地 DNS。
8. 最后,本地 DNS 获取到了目标域名的 IP 地址,将其返回给请求主机。
除了迭代式查询,还有一种递归式查询如下图,具体过程和上述类似,只是顺序有所不同。
-
+
另外,DNS 的缓存位于本地 DNS 服务器。由于全世界的根服务器甚少,只有 600 多台,分为 13 组,且顶级域的数量也在一个可数的范围内,因此本地 DNS 通常已经缓存了很多 TLD DNS 服务器,所以在实际查找过程中,无需访问根服务器。根服务器通常是被跳过的,不请求的。这样可以提高 DNS 查询的效率和速度,减少对根服务器和 TLD 服务器的负担。
@@ -58,12 +84,12 @@ DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务
DNS 的报文格式如下图所示:
-
+
DNS 报文分为查询和回答报文,两种形式的报文结构相同。
- 标识符。16 比特,用于标识该查询。这个标识符会被复制到对查询的回答报文中,以便让客户用它来匹配发送的请求和接收到的回答。
-- 标志。1 比特的”查询/回答“标识位,`0`表示查询报文,`1`表示回答报文;1 比特的”权威的“标志位(当某 DNS 服务器是所请求名字的权威 DNS 服务器时,且是回答报文,使用”权威的“标志);1 比特的”希望递归“标志位,显式地要求执行递归查询;1 比特的”递归可用“标志位,用于回答报文中,表示 DNS 服务器支持递归查询。
+- 标志。1 比特的“查询/回答”标识位,`0` 表示查询报文,`1` 表示回答报文;1 比特的“权威的”标志位(当某 DNS 服务器是所请求名字的权威 DNS 服务器时,且是回答报文,使用“权威的”标志);1 比特的“希望递归”标志位,显式地要求执行递归查询;1 比特的“递归可用”标志位,用于回答报文中,表示 DNS 服务器支持递归查询。
- 问题数、回答 RR 数、权威 RR 数、附加 RR 数。分别指示了后面 4 类数据区域出现的数量。
- 问题区域。包含正在被查询的主机名字,以及正被询问的问题类型。
- 回答区域。包含了对最初请求的名字的资源记录。**在回答报文的回答区域中可以包含多条 RR,因此一个主机名能够有多个 IP 地址。**
@@ -72,23 +98,23 @@ DNS 报文分为查询和回答报文,两种形式的报文结构相同。
## DNS 记录
-DNS 服务器在响应查询时,需要查询自己的数据库,数据库中的条目被称为 **资源记录(Resource Record,RR)** 。RR 提供了主机名到 IP 地址的映射。RR 是一个包含了`Name`, `Value`, `Type`, `TTL`四个字段的四元组。
+DNS 服务器在响应查询时,需要查询自己的数据库,数据库中的条目被称为 **资源记录(Resource Record,RR)**。RR 提供了主机名到 IP 地址的映射。RR 是一个包含了 `Name`、`Value`、`Type`、`TTL` 四个字段的四元组。
-
+
-`TTL`是该记录的生存时间,它决定了资源记录应当从缓存中删除的时间。
+`TTL` 是该记录的生存时间,它决定了资源记录应当从缓存中删除的时间。
-`Name`和`Value`字段的取值取决于`Type`:
+`Name` 和 `Value` 字段的取值取决于 `Type`:
-
+
-- 如果`Type=A`,则`Name`是主机名信息,`Value` 是该主机名对应的 IP 地址。这样的 RR 记录了一条主机名到 IP 地址的映射。
-- 如果 `Type=AAAA` (与 `A` 记录非常相似),唯一的区别是 A 记录使用的是 IPv4,而 `AAAA` 记录使用的是 IPv6。
-- 如果`Type=CNAME` (Canonical Name Record,真实名称记录) ,则`Value`是别名为`Name`的主机对应的规范主机名。`Value`值才是规范主机名。`CNAME` 记录将一个主机名映射到另一个主机名。`CNAME` 记录用于为现有的 `A` 记录创建别名。下文有示例。
-- 如果`Type=NS`,则`Name`是个域,而`Value`是个知道如何获得该域中主机 IP 地址的权威 DNS 服务器的主机名。通常这样的 RR 是由 TLD 服务器发布的。
-- 如果`Type=MX` ,则`Value`是个别名为`Name`的邮件服务器的规范主机名。既然有了 `MX` 记录,那么邮件服务器可以和其他服务器使用相同的别名。为了获得邮件服务器的规范主机名,需要请求 `MX` 记录;为了获得其他服务器的规范主机名,需要请求 `CNAME` 记录。
+- 如果 `Type=A`,则 `Name` 是主机名信息,`Value` 是该主机名对应的 IP 地址。这样的 RR 记录了一条主机名到 IP 地址的映射。
+- 如果 `Type=AAAA`(与 `A` 记录非常相似),唯一的区别是 A 记录使用的是 IPv4,而 `AAAA` 记录使用的是 IPv6。
+- 如果 `Type=CNAME`(Canonical Name Record,真实名称记录),则 `Value` 是别名为 `Name` 的主机对应的规范主机名。`Value` 值才是规范主机名。`CNAME` 记录将一个主机名映射到另一个主机名。`CNAME` 记录用于为现有的 `A` 记录创建别名。下文有示例。
+- 如果 `Type=NS`,则 `Name` 是个域,而 `Value` 是个知道如何获得该域中主机 IP 地址的权威 DNS 服务器的主机名。通常这样的 RR 是由 TLD 服务器发布的。
+- 如果 `Type=MX`,则 `Value` 是个别名为 `Name` 的邮件服务器的规范主机名。既然有了 `MX` 记录,那么邮件服务器可以和其他服务器使用相同的别名。为了获得邮件服务器的规范主机名,需要请求 `MX` 记录;为了获得其他服务器的规范主机名,需要请求 `CNAME` 记录。
-`CNAME`记录总是指向另一则域名,而非 IP 地址。假设有下述 DNS zone:
+`CNAME` 记录总是指向另一则域名,而非 IP 地址。假设有下述 DNS zone:
```plain
NAME TYPE VALUE
diff --git a/docs/cs-basics/network/http-status-codes.md b/docs/cs-basics/network/http-status-codes.md
index 5550e06d5b8..0e3917b6d4a 100644
--- a/docs/cs-basics/network/http-status-codes.md
+++ b/docs/cs-basics/network/http-status-codes.md
@@ -1,11 +1,25 @@
---
title: HTTP 常见状态码总结(应用层)
+description: 汇总常见 HTTP 状态码含义与使用场景,强调 201/204 等易混淆点,提升接口设计与调试效率。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: HTTP 状态码,2xx,3xx,4xx,5xx,重定向,错误码,201 Created,204 No Content
---
-HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被成功处理。
+HTTP 状态码是服务端返回给客户端的处理结果摘要。看到一个状态码,基本就能判断请求是成功、重定向、客户端出错,还是服务端出错。
+
+状态码看起来只是数字,但很多码很容易混淆:比如 301 和 302、401 和 403、500 和 502、201 和 204。
+
+这篇文章主要回答几个问题:
+
+1. 1xx、2xx、3xx、4xx、5xx 分别代表什么类型的结果?
+2. 常见成功状态码如 200、201、204 有什么区别?
+3. 常见客户端错误如 400、401、403、404 应该怎么理解?
+4. 常见服务端错误如 500、502、503、504 通常意味着什么?

@@ -22,7 +36,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
🐛 修正(参见:[issue#2458](https://github.com/Snailclimb/JavaGuide/issues/2458)):201 Created 状态码更准确点来说是创建一个或多个新的资源,可以参考:。
-
+
这里格外提一下 204 状态码,平时学习/工作中见到的次数并不多。
diff --git a/docs/cs-basics/network/http-vs-https.md b/docs/cs-basics/network/http-vs-https.md
index 71c224f1be4..7e6a521371f 100644
--- a/docs/cs-basics/network/http-vs-https.md
+++ b/docs/cs-basics/network/http-vs-https.md
@@ -1,10 +1,26 @@
---
-title: HTTP vs HTTPS(应用层)
+title: HTTP vs HTTPS:区别在哪里、HTTPS 为什么更安全(应用层)
+description: 对比 HTTP 与 HTTPS 的协议与安全机制,解析 SSL/TLS 工作原理与握手流程,明确应用层安全落地细节。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: HTTP,HTTPS,SSL,TLS,加密,认证,端口,安全性,握手流程
---
+HTTP 能传输网页内容,但默认是明文传输。请求和响应如果在网络中被监听、篡改或冒充,HTTP 本身没有足够的保护能力。
+
+HTTPS 不是一个全新的应用层协议,而是在 HTTP 和 TCP 之间加入 TLS/SSL,用加密、身份认证和完整性校验来保护通信过程。
+
+这篇文章主要回答几个问题:
+
+1. HTTP 和 HTTPS 的核心区别是什么?
+2. HTTPS 如何防止窃听、篡改和冒充?
+3. SSL/TLS 握手大致做了哪些事情?
+4. 为什么使用 HTTPS 后,证书、混合内容和性能优化仍然需要关注?
+
## HTTP 协议
### HTTP 协议介绍
@@ -13,9 +29,11 @@ HTTP 协议,全称超文本传输协议(Hypertext Transfer Protocol)。顾
并且,HTTP 是一个无状态(stateless)协议,也就是说服务器不维护任何有关客户端过去所发请求的消息。这其实是一种懒政,有状态协议会更加复杂,需要维护状态(历史信息),而且如果客户或服务器失效,会产生状态的不一致,解决这种不一致的代价更高。
+
+
### HTTP 协议通信过程
-HTTP 是应用层协议,它以 TCP(传输层)作为底层协议,默认端口为 80. 通信过程主要如下:
+HTTP 是应用层协议,它以 TCP(传输层)作为底层协议,默认端口为 80。通信过程主要如下:
1. 服务器在 80 端口等待客户的请求。
2. 浏览器发起到服务器的 TCP 连接(创建套接字 Socket)。
@@ -31,9 +49,9 @@ HTTP 是应用层协议,它以 TCP(传输层)作为底层协议,默认
### HTTPS 协议介绍
-HTTPS 协议(Hyper Text Transfer Protocol Secure),是 HTTP 的加强安全版本。HTTPS 是基于 HTTP 的,也是用 TCP 作为底层协议,并额外使用 SSL/TLS 协议用作加密和安全认证。默认端口号是 443.
+HTTPS 协议(Hyper Text Transfer Protocol Secure),是 HTTP 的加强安全版本。HTTPS 是基于 HTTP 的,也是用 TCP 作为底层协议,并额外使用 SSL/TLS 协议用作加密和安全认证。默认端口号是 443。
-HTTPS 协议中,SSL 通道通常使用基于密钥的加密算法,密钥长度通常是 40 比特或 128 比特。
+HTTPS 中,TLS 握手完成后,通信数据使用对称加密算法(如 AES-128-GCM 或 AES-256-GCM)保护,密钥通过非对称加密(如 RSA-2048/4096 或 ECDH)在握手阶段协商生成。早期 SSL 使用的 40 比特密钥因强度不足已被废弃,现代 TLS 要求对称密钥至少 128 比特。
### HTTPS 协议优点
@@ -41,19 +59,19 @@ HTTPS 协议中,SSL 通道通常使用基于密钥的加密算法,密钥长
## HTTPS 的核心—SSL/TLS 协议
-HTTPS 之所以能达到较高的安全性要求,就是结合了 SSL/TLS 和 TCP 协议,对通信数据进行加密,解决了 HTTP 数据透明的问题。接下来重点介绍一下 SSL/TLS 的工作原理。
+HTTPS 之所以能达到较高的安全性要求,就是结合 SSL/TLS 和 TCP 协议,对通信数据进行加密,解决了 HTTP 数据透明的问题。接下来重点介绍 SSL/TLS 的工作原理。
### SSL 和 TLS 的区别?
**SSL 和 TLS 没有太大的区别。**
-SSL 指安全套接字协议(Secure Sockets Layer),首次发布与 1996 年。SSL 的首次发布其实已经是他的 3.0 版本,SSL 1.0 从未面世,SSL 2.0 则具有较大的缺陷(DROWN 缺陷——Decrypting RSA with Obsolete and Weakened eNcryption)。很快,在 1999 年,SSL 3.0 进一步升级,**新版本被命名为 TLS 1.0**。因此,TLS 是基于 SSL 之上的,但由于习惯叫法,通常把 HTTPS 中的核心加密协议混称为 SSL/TLS。
+SSL 指安全套接层协议(Secure Sockets Layer),首次发布于 1996 年(SSL 3.0)。SSL 1.0 从未面世,SSL 2.0 则具有较大的缺陷(DROWN 缺陷——Decrypting RSA with Obsolete and Weakened eNcryption)。很快,在 1999 年,SSL 3.0 进一步升级,**新版本被命名为 TLS 1.0**。因此,TLS 是基于 SSL 之上的,但由于习惯叫法,通常把 HTTPS 中的核心加密协议混称为 SSL/TLS。目前 SSL 已完全废弃,TLS 1.2 和 TLS 1.3 是现代 HTTPS 的实际标准。
### SSL/TLS 的工作原理
#### 非对称加密
-SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密钥——一个公钥,一个私钥。在通信时,私钥仅由解密者保存,公钥由任何一个想与解密者通信的发送者(加密者)所知。可以设想一个场景,
+SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密钥:一个公钥,一个私钥。在通信时,私钥仅由解密者保存,公钥由任何一个想与解密者通信的发送者(加密者)所知。可以设想一个场景:
> 在某个自助邮局,每个通信信道都是一个邮箱,每一个邮箱所有者都在旁边立了一个牌子,上面挂着一把钥匙:这是我的公钥,发送者请将信件放入我的邮箱,并用公钥锁好。
>
@@ -61,7 +79,7 @@ SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密
>
> 这样,通信信息就不会被其他人截获了,这依赖于私钥的保密性。
-
+
非对称加密的公钥和私钥需要采用一种复杂的数学机制生成(密码学认为,为了较高的安全性,尽量不要自己创造加密方案)。公私钥对的生成算法依赖于单向陷门函数。
@@ -81,13 +99,13 @@ SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密
> 对称加密:通信双方共享唯一密钥 k,加解密算法已知,加密方利用密钥 k 加密,解密方利用密钥 k 解密,保密性依赖于密钥 k 的保密性。
-
+
-对称加密的密钥生成代价比公私钥对的生成代价低得多,那么有的人会问了,为什么 SSL/TLS 还需要使用非对称加密呢?因为对称加密的保密性完全依赖于密钥的保密性。在双方通信之前,需要商量一个用于对称加密的密钥。我们知道网络通信的信道是不安全的,传输报文对任何人是可见的,密钥的交换肯定不能直接在网络信道中传输。因此,使用非对称加密,对对称加密的密钥进行加密,保护该密钥不在网络信道中被窃听。这样,通信双方只需要一次非对称加密,交换对称加密的密钥,在之后的信息通信中,使用绝对安全的密钥,对信息进行对称加密,即可保证传输消息的保密性。
+对称加密的密钥生成代价比公私钥对的生成代价低得多。那么有的人会问:为什么 SSL/TLS 还需要使用非对称加密呢?因为对称加密的保密性完全依赖于密钥的保密性。在双方通信之前,需要商量一个用于对称加密的密钥。网络通信的信道是不安全的,传输报文对任何人是可见的,密钥的交换肯定不能直接在网络信道中传输。因此,使用非对称加密对对称加密的密钥进行加密,保护该密钥不在网络信道中被窃听。这样,通信双方只需要一次非对称加密,交换对称加密的密钥,在之后的信息通信中,使用绝对安全的密钥对信息进行对称加密,即可保证传输消息的保密性。
#### 公钥传输的信赖性
-SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐患,设想一个下面的场景:
+SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐患。设想下面的场景:
> 客户端 C 和服务器 S 想要使用 SSL/TLS 通信,由上述 SSL/TLS 通信原理,C 需要先知道 S 的公钥,而 S 公钥的唯一获取途径,就是把 S 公钥在网络信道中传输。要注意网络信道通信中有几个前提:
>
@@ -99,7 +117,7 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
>
> 同样的,S 公钥即使做加密,也难以避免这种信任性问题,C 被 AS 拐跑了!
-
+
为了公钥传输的信赖性问题,第三方机构应运而生——证书颁发机构(CA,Certificate Authority)。CA 默认是受信任的第三方。CA 会给各个服务器颁发证书,证书存储在服务器上,并附有 CA 的**电子签名**(见下节)。
@@ -107,7 +125,7 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
#### 数字签名
-好,到这一小节,已经是 SSL/TLS 的尾声了。上一小节提到了数字签名,数字签名要解决的问题,是防止证书被伪造。第三方信赖机构 CA 之所以能被信赖,就是 **靠数字签名技术** 。
+好,到这一小节,已经是 SSL/TLS 的尾声了。上一小节提到了数字签名,数字签名要解决的问题,是防止证书被伪造。第三方信赖机构 CA 之所以能被信赖,就是 **靠数字签名技术**。
数字签名,是 CA 在给服务器颁发证书时,使用散列+加密的组合技术,在证书上盖个章,以此来提供验伪的功能。具体行为如下:
@@ -117,7 +135,7 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
>
> 客户端对证书数据(包含服务器的公钥)做相同的散列处理,得到摘要,并将该摘要与之前从签名中解码出的摘要做对比,如果相同,则身份验证成功;否则验证失败。
-
+
总结来说,带有证书的公钥传输机制如下:
@@ -127,11 +145,11 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
4. C 获得 S 的证书,信任 CA 并知晓 CA 公钥,使用 CA 公钥对 S 证书上的签名解密,同时对消息进行散列处理,得到摘要。比较摘要,验证 S 证书的真实性。
5. 如果 C 验证 S 证书是真实的,则信任 S 的公钥(在 S 证书中)。
-
+
对于数字签名,我这里讲的比较简单,如果你没有搞清楚的话,强烈推荐你看看[数字签名及数字证书原理](https://www.bilibili.com/video/BV18N411X7ty/)这个视频,这是我看过最清晰的讲解。
-
+
## 总结
diff --git a/docs/cs-basics/network/http-vs-rpc.md b/docs/cs-basics/network/http-vs-rpc.md
new file mode 100644
index 00000000000..1bd17f4e80d
--- /dev/null
+++ b/docs/cs-basics/network/http-vs-rpc.md
@@ -0,0 +1,374 @@
+---
+title: 有了 HTTP 协议,为什么还要 RPC?HTTP 与 RPC 区别对比
+category: 计算机基础
+description: 深入对比 HTTP 与 RPC 的本质区别,解析微服务通信选型。涵盖序列化性能、连接复用、gRPC、RESTful、服务治理等核心知识点。
+head:
+ - - meta
+ - name: keywords
+ content: HTTP,RPC,HTTP vs RPC区别,微服务通信,RPC协议,TCP通信,序列化协议,RESTful,gRPC,Dubbo,Protobuf,服务调用,远程调用,HTTP协议,微服务选型
+---
+
+你好,我是小 G。在我大二下学期那年,看黑马的免费课程,第一次接触到 RPC,当时还是挺懵逼的。
+
+HTTP 接口不是已经能调了吗?
+
+前端调后端是 HTTP,服务端调服务端也可以用 HTTP。写一个 `/user/getById` 接口,传个用户 ID,返回用户信息,这不也能完成远程调用吗?
+
+那为什么还要再搞一个 RPC 增加学习成本呢?这不纯闹嘛!
+
+更容易让人混乱的是,很多文章特别喜欢把 HTTP 和 RPC 放在一起对比,好像它们是同一层的两个协议。看完之后你可能记住了几句话:**HTTP 面向资源,RPC 面向方法;HTTP 对外,RPC 对内;RPC 性能更好。**
+
+这些话不是完全错,但太粗了。
+
+真到项目里,你还是会遇到问题:**用 HTTP 行不行?用 RPC 是不是过度设计?gRPC 明明基于 HTTP/2,为什么又说它是 RPC?**
+
+这篇文章就围绕这个问题聊清楚。
+
+## RPC 不是某一个具体协议
+
+这是一个常见的误区,开始后面的文章之前,非常有必要先提一下。
+
+**HTTP 是协议。而 RPC 不是某一个具体协议,它更像是一种调用方式。**
+
+RPC 全称是 Remote Procedure Call,翻译过来就是远程过程调用。它想解决的问题很朴素:**让你调用远程服务时,尽量像调用本地方法一样。**
+
+
+
+比如本地代码里调用用户服务:
+
+```java
+User user = userService.getUser(1001);
+```
+
+如果 `userService` 就在当前进程里,这只是一次普通方法调用。
+
+但如果用户服务部署在另一台机器上,这件事就复杂了。你要发网络请求,要传方法名和参数,要序列化数据,要处理超时、失败、重试,还要拿到返回结果再反序列化。
+
+RPC 框架想做的事情,就是把这些麻烦尽量封装掉。调用方代码看起来还是:
+
+```java
+User user = userService.getUser(1001);
+```
+
+但底下已经完成了网络通信、序列化、服务寻址和结果返回。
+
+所以更准确的说法不是“HTTP 和 RPC 谁更强”,而是:
+
+**HTTP 是一种应用层协议,RPC 是一种远程调用模型。**
+
+
+
+具体到实现上,RPC 可以有很多种。Dubbo 是 RPC 框架,Thrift 是 RPC 框架,gRPC 也是 RPC 框架。gRPC 官方文档里也说得很直接:客户端可以像调用本地对象一样,调用另一台机器上服务端应用的方法;服务端定义可远程调用的方法以及参数和返回类型。
+
+
+
+这就解释了一个很容易绕晕的点:**gRPC 是 RPC,但它基于 HTTP/2。**
+
+它不是 HTTP 的反面,只是在 HTTP/2 之上提供 RPC 调用。
+
+gRPC 的 GitHub 上专门有一篇文章 [gRPC over HTTP2 基于 HTTP2 的 gRPC 协议](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) 详细介绍:
+
+
+
+## **光有 TCP 还不够**
+
+要理解 HTTP 和 RPC 的差别,最好先往下看一层。
+
+很多同学知道 HTTP 基于 TCP,RPC 也经常基于 TCP,于是会想:那我直接用 TCP 不就行了吗?
+
+理论上可以,实际很麻烦。
+
+TCP 负责的是可靠传输,它传的是一串连续的字节流。它不关心你的业务消息从哪里开始,到哪里结束。
+
+比如客户端连续发了两次请求:
+
+```text
+getUser:1001
+getOrder:8888
+```
+
+服务端收到的可能不是两段规规整整的消息,而是一段字节流。你必须自己判断:第一条消息在哪里结束,第二条消息从哪里开始。还要考虑半包、粘包、编码、超时、错误码、请求 ID 等问题。
+
+
+
+这就是为什么应用层协议一定要定义消息格式。
+
+HTTP 定义了一套通用格式:请求行、Header、Body、状态码等。MDN 对 HTTP 的定义也很清楚:它是应用层协议,最初用于浏览器和 Web 服务器通信,但也可以用于机器之间通信和 API 访问。
+
+RPC 框架也会定义自己的消息格式。只不过它通常不会围绕 URL 和资源来设计,而是围绕服务、方法、参数和返回值来设计。
+
+说白了,HTTP 和 RPC 都在解决一个问题:
+
+**两个进程隔着网络,怎么把一次业务调用说清楚。**
+
+只是它们的建模方式不一样。
+
+## **HTTP 更像访问资源,RPC 更像调用方法**
+
+HTTP / REST 常见写法是这样的:
+
+```http
+GET /users/1001
+POST /orders
+PUT /orders/888/status
+DELETE /comments/9527
+```
+
+它的心智模型是资源。
+
+`/users/1001` 是一个用户资源,`GET` 表示读取它;`POST /orders` 表示创建订单;`PUT /orders/888/status` 表示修改订单状态。
+
+这种方式很适合对外开放 API。
+
+因为它通用、好理解、好调试。浏览器能访问,Postman 能调,curl 能测,网关也好处理。你给第三方提供接口时,让对方按 HTTP 文档接入,门槛比较低。
+
+RPC 的写法更像这样:
+
+```java
+userService.getUser(1001);
+orderService.createOrder(request);
+inventoryService.deductStock(skuId, count);
+```
+
+它的心智模型是方法调用。
+
+调用方更关心的是:我要调哪个服务?哪个方法?传什么参数?返回什么对象?
+
+
+
+这和 Java 后端平时写代码的习惯更接近。尤其是微服务内部调用时,服务和服务之间本来就是围绕业务方法协作,比如创建订单、扣库存、查询余额、校验权限。RPC 把这种调用关系表达得更直接。
+
+所以 HTTP 和 RPC 最大的区别,不是一个能不能调通,另一个能不能调通。
+
+两者都能调通。
+
+区别在于:**你是把远程交互建模成一次资源访问,还是一次方法调用。**
+
+## **公司内部为什么更常见 RPC?**
+
+HTTP 当然能做内部服务调用。
+
+很多公司内部服务全用 HTTP,也跑得好好的。尤其是服务规模不大、调用链不复杂的时候,HTTP 更简单。
+
+但服务数量上来之后,RPC 的优势会慢慢变明显。
+
+**第一个明显变化是:调用方不想关心对方机器在哪。**
+
+你写业务代码的时候,最好只关心“我要调用用户服务”,而不是关心用户服务有几台机器、IP 是什么、哪台刚下线、哪台权重高。
+
+这就需要服务发现。
+
+Dubbo 官方文档里对服务发现的描述很典型:Provider 把地址注册到注册中心,Consumer 从注册中心读取并订阅地址列表,地址变化时注册中心通知消费者。Dubbo 支持 Nacos、Consul、ZooKeeper 等常见注册中心。
+
+
+
+这类能力当然也可以用 HTTP 做。你可以用注册中心、网关、负载均衡、SDK 自己拼一套。
+
+但 RPC 框架通常会把这些东西直接放进服务调用体系里。
+
+调用方写的是服务接口,底下自动完成服务发现、负载均衡、连接管理、超时控制。业务代码不用到处拼 URL。
+
+**第二个变化是:接口契约会变得更重要。**
+
+HTTP + JSON 很灵活,但灵活也意味着容易松散。
+
+字段名改了,类型改了,枚举值多了一个,调用方可能到运行时才炸。接口文档如果没及时更新,联调时就会很痛苦。
+
+RPC 框架通常会用更强的契约来约束双方。以 gRPC 为例,它常用 Protocol Buffers 作为接口定义语言和消息交换格式。Protocol Buffers 官方文档也说明,它是一种语言无关、平台无关、可扩展的结构化数据序列化机制,可以通过 `.proto` 定义结构并生成不同语言的代码。
+
+这带来的好处是,接口变更更容易被代码生成和编译阶段暴露出来。
+
+当然,契约强不代表不会出事故。
+
+字段怎么兼容,老版本客户端怎么处理,新字段能不能删,枚举能不能改,这些还是要认真设计。只是相比“大家约定一下 JSON 字段”,IDL 会更硬一点。
+
+**第三个变化是:高频内部调用会更在意机器处理效率。**
+
+HTTP + JSON 的好处是可读性强,人类看起来舒服。但机器处理时,它不是最省的方式。字段名、文本格式、解析成本,都会带来额外开销。
+
+RPC 框架常用二进制序列化,比如 Protobuf、Thrift。体积更小,解析也更适合机器处理。
+
+但这里不能说死。
+
+“RPC 一定比 HTTP 快”这句话不严谨。HTTP/2、连接复用、压缩、不同 JSON 库、不同网络环境,都会影响结果。gRPC 自己也基于 HTTP/2,它的优势并不是一句“不是 HTTP”就能解释完。
+
+更稳的说法是:
+
+**在高频服务互调场景里,RPC 框架通常会把序列化、连接复用、超时、重试、负载均衡、链路追踪这些能力做得更贴近内部服务调用。**
+
+这才是它在公司内部常见的原因。
+
+## **RPC 的价值不只是“调用快一点”**
+
+很多人讲 RPC,喜欢把重点放在性能上。
+
+性能当然重要,但我觉得 RPC 更大的价值是服务治理。
+
+一个内部调用真正上线后,不只是发请求、拿响应这么简单。你很快会遇到一堆问题:
+
+- 这个调用超时时间设多少?失败了要不要重试?重试会不会导致重复扣款?
+- 下游服务挂了,上游要不要降级?
+- 哪个接口最近错误率升高了?
+- 一次用户请求经过了几个服务?
+
+这些问题如果全靠业务代码处理,很快就会乱。
+
+RPC 框架通常会和治理能力绑在一起,比如超时控制、负载均衡、服务发现、熔断降级、链路追踪、调用统计等。gRPC 官方介绍里也提到,它支持负载均衡、Tracing、健康检查和认证等可插拔能力。
+
+HTTP 也能做这些。
+
+很多公司会用 API Gateway、服务网格、HTTP SDK、拦截器、链路追踪组件来补齐。做得好也没问题。
+
+所以不要把 RPC 理解成“比 HTTP 高级的东西”。它更像是把内部服务调用里常见的一堆问题,按“远程方法调用”这条路径整理了一遍。
+
+## **那 HTTP 就不适合内部调用吗?**
+
+并不是的哈。如果服务规模不大,团队人数也不多,反而用 HTTP 更省心。
+
+比如一个后台管理系统,拆了几个服务,调用频率也不高。你用 Spring Boot 写几个 REST 接口,配合 OpenAPI 文档、统一错误码、网关鉴权、日志追踪,完全够用。
+
+强上 RPC 可能还会带来额外成本,没意义。
+
+你要引入注册中心,要维护 IDL,要处理代码生成,要培训团队,还要解决本地调试和网关转发问题。服务没几个,调用链也不复杂的时候,这些成本不一定值得。
+
+HTTP 适合这些场景:
+
+- 对外开放 API,比如 Web、App、第三方合作方接入;
+- 团队更看重通用性和调试方便;
+- 服务调用频率不高;
+- 没有成熟 RPC 基础设施;
+- 已经有统一 HTTP 网关、SDK、限流、鉴权和监控体系。
+
+这里有个很简单的判断,分享给大家:
+
+**如果你的系统用 HTTP 已经稳定跑着,也没有明显的调用治理痛点,就没必要为了“微服务味更浓”换 RPC。**
+
+技术选型不是贴标签。
+
+能稳定解决问题更重要。
+
+## **gRPC 为什么容易把人绕晕?**
+
+gRPC 经常让人混乱,就是因为它同时踩在两个概念上。
+
+**一方面,它是 RPC 框架。**
+
+你定义服务和方法,生成客户端和服务端代码,然后像调用方法一样调用远程服务。
+
+**另一方面,它基于 HTTP/2 传输。**
+
+
+
+所以你不能把它简单理解成“HTTP 的对立面”。
+
+更准确地说: **gRPC 用 HTTP/2 做传输,默认使用 Protobuf 作为 IDL 和消息序列化格式,再用 RPC 模型组织调用。**
+
+这里要注意,Protobuf 是 gRPC 最常见的默认搭配,但不是 gRPC 的定义本身。gRPC 协议层允许 `application/grpc+proto`、`application/grpc+json` 或自定义编码。
+
+还有一点经常被忽略:正常 gRPC 响应里,HTTP 层通常是 `:status: 200`,真正的调用结果放在 HTTP/2 Trailers 里的 `grpc-status`、`grpc-message`。
+
+这会带来一个很实际的排查差异。
+
+看 HTTP 接口时,我们习惯先看 HTTP 状态码。`200` 基本代表请求成功,`404` 代表资源不存在,`500` 代表服务端异常。
+
+但看 gRPC 时,不能只看 HTTP 状态码。HTTP 是 200,不代表这次 RPC 业务调用一定成功,还要继续看 `grpc-status`。
+
+这也带来一个工程问题:网关、负载均衡、代理、Service Mesh 是否正确支持 HTTP/2 Trailers,会直接影响 gRPC 调用。如果链路里有组件处理不好 Trailers,问题会很隐蔽。
+
+所以,gRPC 不是“HTTP/2 + Protobuf”这么简单。
+
+HTTP 这一层,它跑在 HTTP/2 上。
+
+编码上,默认搭配 Protobuf,但协议允许其他编码。
+
+调用体验上,它让你像调本地方法一样调远程服务。
+
+状态返回上,它又用了 HTTP/2 Trailers 承载 RPC 调用结果。
+
+这些东西叠在一起,才是它容易把人绕晕的原因。
+
+## **真实选型时,别问哪个更高级**
+
+我更建议你按调用关系选:
+
+- 如果是浏览器、移动端、第三方系统调用,优先 HTTP。原因很简单:通用,接入成本低,调试工具多。对外接口最怕别人接不动。HTTP 在这方面优势太明显了。
+- 如果是公司内部微服务高频互调,可以考虑 RPC。尤其是服务数量多、接口数量多、调用链复杂,对超时、重试、注册发现、链路追踪、负载均衡要求都比较高的时候,RPC 框架会省掉很多重复工作。
+- 如果团队已经有成熟 HTTP 基础设施,也没必要强上 RPC。比如统一网关、服务发现、SDK、链路追踪、限流熔断都有了,大家也习惯用 HTTP,那继续用 HTTP 没问题。
+
+如果要用 gRPC,要提前想清楚几个问题:
+
+- 浏览器不能像后端服务一样直接使用标准 gRPC,通常需要 gRPC-Web 或代理层;
+- 网关和负载均衡是否支持;本地调试是不是方便;
+- 团队是否接受 `.proto` 和代码生成;
+- 线上排查时二进制消息是否会增加理解成本。
+
+gRPC 很强,但不是零成本。
+
+这点要提前说清楚。
+
+## 几个常见误解
+
+### HTTP 和 RPC 谁性能更好?
+
+不能一刀切。
+
+如果拿 HTTP/1.1 + JSON 去和基于 HTTP/2 + Protobuf 的 gRPC 比,在高频内部调用场景里,后者通常更省。
+
+但换个实现,结果就可能不一样。
+
+消息大小、序列化方式、连接复用、压缩、框架实现、网络环境都会影响结果。真正要比,应该拿你自己的接口、数据量和部署环境压测,而不是背一句“RPC 更快”。
+
+### RPC 是不是只能走 TCP?
+
+不是。
+
+RPC 是调用模型,不是传输协议。它可以基于 TCP,也可以基于 HTTP/2。gRPC 就是一个很典型的例子。
+
+### REST 和 RPC 是不是互斥?
+
+不完全互斥。
+
+REST 更偏资源建模,RPC 更偏方法调用。实际项目里经常混用:外部接口走 REST,内部服务走 RPC。这很正常。
+
+### 有了 HTTP/2,还需要 RPC 吗?
+
+HTTP/2 在 HTTP 这一层引入了帧、流、多路复用、头部压缩等能力,提高了同一条 TCP 连接上的并发利用率。
+
+但它不会自动帮你定义服务接口,不会自动生成客户端代码,也不会自动解决服务发现、超时重试、调用治理和版本契约。
+
+还有一个很容易被忽略的差异:调用模式。
+
+普通 HTTP API 大多是一问一答。gRPC 除了最常见的 Unary 调用,还原生支持服务端流、客户端流和双向流。gRPC 官方文档也明确列出了 Unary、Server streaming、Client streaming、Bidirectional streaming 这四种调用模式。
+
+比如日志订阅、长任务进度推送、批量上传、实时同步这类场景,用 streaming 会更自然。你当然也可以用 SSE、WebSocket,或者自己基于 HTTP/2 封装,但那就相当于又在补 RPC 框架已经做好的那部分能力。
+
+所以 HTTP/2 很重要,但它不是 RPC 框架的全部。
+
+### gRPC 是不是等于 HTTP/2 + Protobuf?
+
+不是。
+
+这句话只能用来帮助初学者快速建立印象,不能当严格定义。
+
+更准确的说法是:gRPC 基于 HTTP/2 承载 RPC 调用,默认使用 Protobuf 描述接口和消息,但协议本身允许 JSON 或自定义编码;同时,它还定义了请求路径、Content-Type、Length-Prefixed-Message、Trailers 里的 `grpc-status` 等一整套规则。
+
+所以 gRPC 不是单纯换了一个序列化格式,它是一套 RPC 调用协议和工程约定。
+
+## 最后
+
+HTTP 和 RPC 不是谁取代谁的关系,也不是谁更高级的问题。
+
+HTTP 能调服务,RPC 也能调服务。真正的区别在于,你是想把远程调用当成一次“资源访问”,还是当成一次“方法调用”。
+
+如果是对外接口,比如 Web、App、第三方系统接入,HTTP 通常更合适。它通用、好调试、接入成本低,别人拿 Postman、curl 就能测。
+如果是公司内部服务互调,尤其是服务多、调用链长、接口频繁调用,还要考虑服务发现、超时、重试、负载均衡、链路追踪这些问题,RPC 会更顺手一些。它不是单纯为了快,而是把内部服务调用里的很多麻烦事一起处理掉。
+
+所以,别再简单背“HTTP 对外,RPC 对内”了。
+
+这句话可以帮助入门,但真做项目时,还得看你的调用对象、团队基础设施、排查成本、性能要求和后续维护成本。
+
+系统规模不大,用 HTTP 已经跑得很稳,就别为了“看起来更微服务”强上 RPC。
+
+内部调用越来越复杂,HTTP SDK、网关、监控、重试这些东西越补越多,那就可以认真考虑 RPC。
+
+一句话:**HTTP 没那么弱,RPC 也没那么神。选哪个,主要看它能不能用更低成本解决你现在的问题。**
diff --git a/docs/cs-basics/network/http1.0-vs-http1.1.md b/docs/cs-basics/network/http1.0-vs-http1.1.md
index f0bb9850780..3ea333c64f0 100644
--- a/docs/cs-basics/network/http1.0-vs-http1.1.md
+++ b/docs/cs-basics/network/http1.0-vs-http1.1.md
@@ -1,17 +1,29 @@
---
-title: HTTP 1.0 vs HTTP 1.1(应用层)
+title: HTTP 1.0 vs HTTP 1.1:长连接、缓存、Host 头等核心差异(应用层)
+description: 细致对比 HTTP/1.0 与 HTTP/1.1 的协议差异,涵盖长连接、管道化、缓存与状态码增强等关键变更与实践影响。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: HTTP/1.0,HTTP/1.1,长连接,管道化,缓存,状态码,Host,带宽优化
---
-这篇文章会从下面几个维度来对比 HTTP 1.0 和 HTTP 1.1:
+HTTP/1.0 和 HTTP/1.1 名字只差一个小版本,但它们在连接复用、缓存、Host 头、状态码和带宽优化上都有明显差异。
-- 响应状态码
-- 缓存处理
-- 连接方式
-- Host 头处理
-- 带宽优化
+这些差异不是单纯的协议细节,它们直接影响浏览器如何发请求、服务器如何复用连接、缓存如何生效,以及虚拟主机如何工作。
+
+这篇文章主要回答几个问题:
+
+1. HTTP/1.1 相比 HTTP/1.0 新增了哪些常见状态码?
+2. HTTP/1.0 和 HTTP/1.1 的缓存机制有什么差异?
+3. HTTP/1.1 为什么默认支持长连接?
+4. Host 头和带宽优化分别解决了什么问题?
+
+开始之前,先简单回顾一下 HTTP 协议:
+
+
## 响应状态码
@@ -23,29 +35,29 @@ HTTP/1.0 仅定义了 16 种状态码。HTTP/1.1 中新加入了大量的状态
### HTTP/1.0
-HTTP/1.0 提供的缓存机制非常简单。服务器端使用`Expires`标签来标志(时间)一个响应体,在`Expires`标志时间内的请求,都会获得该响应体缓存。服务器端在初次返回给客户端的响应体中,有一个`Last-Modified`标签,该标签标记了被请求资源在服务器端的最后一次修改。在请求头中,使用`If-Modified-Since`标签,该标签标志一个时间,意为客户端向服务器进行问询:“该时间之后,我要请求的资源是否有被修改过?”通常情况下,请求头中的`If-Modified-Since`的值即为上一次获得该资源时,响应体中的`Last-Modified`的值。
+HTTP/1.0 提供的缓存机制非常简单。服务器端使用 `Expires` 标签来标志(时间)一个响应体,在 `Expires` 标志时间内的请求,都会获得该响应体缓存。服务器端在初次返回给客户端的响应体中,有一个 `Last-Modified` 标签,该标签标记了被请求资源在服务器端的最后一次修改。在请求头中,使用 `If-Modified-Since` 标签,该标签标志一个时间,意为客户端向服务器进行问询:“该时间之后,我要请求的资源是否有被修改过?”通常情况下,请求头中的 `If-Modified-Since` 的值即为上一次获得该资源时,响应体中的 `Last-Modified` 的值。
-如果服务器接收到了请求头,并判断`If-Modified-Since`时间后,资源确实没有修改过,则返回给客户端一个`304 not modified`响应头,表示”缓冲可用,你从浏览器里拿吧!”。
+如果服务器接收到了请求头,并判断 `If-Modified-Since` 时间后,资源确实没有修改过,则返回给客户端一个 `304 Not Modified` 响应头,表示“缓冲可用,你从浏览器里拿吧!”。
-如果服务器判断`If-Modified-Since`时间后,资源被修改过,则返回给客户端一个`200 OK`的响应体,并附带全新的资源内容,表示”你要的我已经改过的,给你一份新的”。
+如果服务器判断 `If-Modified-Since` 时间后,资源被修改过,则返回给客户端一个 `200 OK` 的响应体,并附带全新的资源内容,表示“你要的我已经改过的,给你一份新的”。
-
+
-
+
### HTTP/1.1
-HTTP/1.1 的缓存机制在 HTTP/1.0 的基础上,大大增加了灵活性和扩展性。基本工作原理和 HTTP/1.0 保持不变,而是增加了更多细致的特性。其中,请求头中最常见的特性就是`Cache-Control`,详见 MDN Web 文档 [Cache-Control](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control).
+HTTP/1.1 的缓存机制在 HTTP/1.0 的基础上,大大增加了灵活性和扩展性。基本工作原理和 HTTP/1.0 保持不变,而是增加了更多细致的特性。其中,请求头中最常见的特性就是 `Cache-Control`,详见 MDN Web 文档 [Cache-Control](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control)。
## 连接方式
-**HTTP/1.0 默认使用短连接** ,也就是说,客户端和服务器每进行一次 HTTP 操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源(如 JavaScript 文件、图像文件、CSS 文件等),每遇到这样一个 Web 资源,浏览器就会重新建立一个 TCP 连接,这样就会导致有大量的“握手报文”和“挥手报文”占用了带宽。
+**HTTP/1.0 默认使用短连接**,也就是说,客户端和服务器每进行一次 HTTP 操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源(如 JavaScript 文件、图像文件、CSS 文件等),每遇到这样一个 Web 资源,浏览器就会重新建立一个 TCP 连接,这样就会导致有大量的“握手报文”和“挥手报文”占用了带宽。
-**为了解决 HTTP/1.0 存在的资源浪费的问题, HTTP/1.1 优化为默认长连接模式 。** 采用长连接模式的请求报文会通知服务端:“我向你请求连接,并且连接成功建立后,请不要关闭”。因此,该 TCP 连接将持续打开,为后续的客户端-服务端的数据交互服务。也就是说在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
+**为了解决 HTTP/1.0 存在的资源浪费的问题,HTTP/1.1 优化为默认长连接模式。** 采用长连接模式的请求报文会通知服务端:“我向你请求连接,并且连接成功建立后,请不要关闭”。因此,该 TCP 连接将持续打开,为后续的客户端-服务端的数据交互服务。也就是说在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
-如果 TCP 连接一直保持的话也是对资源的浪费,因此,一些服务器软件(如 Apache)还会支持超时时间的时间。在超时时间之内没有新的请求达到,TCP 连接才会被关闭。
+如果 TCP 连接一直保持的话也是对资源的浪费,因此,一些服务器软件(如 Apache)还会支持超时时间选项。在超时时间之内没有新的请求到达,TCP 连接才会被关闭。
-有必要说明的是,HTTP/1.0 仍提供了长连接选项,即在请求头中加入`Connection: Keep-alive`。同样的,在 HTTP/1.1 中,如果不希望使用长连接选项,也可以在请求头中加入`Connection: close`,这样会通知服务器端:“我不需要长连接,连接成功后即可关闭”。
+有必要说明的是,HTTP/1.0 仍提供了长连接选项,即在请求头中加入 `Connection: Keep-Alive`。同样的,在 HTTP/1.1 中,如果不希望使用长连接选项,也可以在请求头中加入 `Connection: close`,这样会通知服务器端:“我不需要长连接,连接成功后即可关闭”。
**HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。**
@@ -53,9 +65,9 @@ HTTP/1.1 的缓存机制在 HTTP/1.0 的基础上,大大增加了灵活性和
## Host 头处理
-域名系统(DNS)允许多个主机名绑定到同一个 IP 地址上,但是 HTTP/1.0 并没有考虑这个问题,假设我们有一个资源 URL 是 的请求报文中,将会请求的是`GET /home.html HTTP/1.0`.也就是不会加入主机名。这样的报文送到服务器端,服务器是理解不了客户端想请求的真正网址。
+域名系统(DNS)允许多个主机名绑定到同一个 IP 地址上,但是 HTTP/1.0 并没有考虑这个问题。假设我们有一个资源 URL 是 `http://example1.org/home.html`,HTTP/1.0 的请求报文中,将会请求的是 `GET /home.html HTTP/1.0`,也就是不会加入主机名。这样的报文送到服务器端,服务器是理解不了客户端想请求的真正网址。
-因此,HTTP/1.1 在请求头中加入了`Host`字段。加入`Host`字段的报文头部将会是:
+因此,HTTP/1.1 在请求头中加入了 `Host` 字段。加入 `Host` 字段的报文头部将会是:
```plain
GET /home.html HTTP/1.1
@@ -68,13 +80,13 @@ Host: example1.org
### 范围请求
-HTTP/1.1 引入了范围请求(range request)机制,以避免带宽的浪费。当客户端想请求一个文件的一部分,或者需要继续下载一个已经下载了部分但被终止的文件,HTTP/1.1 可以在请求中加入`Range`头部,以请求(并只能请求字节型数据)数据的一部分。服务器端可以忽略`Range`头部,也可以返回若干`Range`响应。
+HTTP/1.1 引入了范围请求(range request)机制,以避免带宽的浪费。当客户端想请求一个文件的一部分,或者需要继续下载一个已经下载了部分但被终止的文件,HTTP/1.1 可以在请求中加入 `Range` 头部,以请求(并只能请求字节型数据)数据的一部分。服务器端可以忽略 `Range` 头部,也可以返回若干 `Range` 响应。
`206 (Partial Content)` 状态码的主要作用是确保客户端和代理服务器能正确识别部分内容响应,避免将其误认为完整资源并错误地缓存。这对于正确处理范围请求和缓存管理非常重要。
一个典型的 HTTP/1.1 范围请求示例:
-```bash
+```http
# 获取一个文件的前 1024 个字节
GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
@@ -83,8 +95,7 @@ Range: bytes=0-1023
`206 Partial Content` 响应:
-```bash
-
+```http
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024
@@ -101,7 +112,7 @@ Content-Length: 1024
客户端想要获取资源的第 0 到 499 字节以及第 1000 到 1499 字节:
-```bash
+```http
GET /path/to/resource HTTP/1.1
Host: example.com
Range: bytes=0-499,1000-1499
@@ -109,7 +120,7 @@ Range: bytes=0-499,1000-1499
服务器端返回多个字节范围,每个范围的内容以分隔符分开:
-```bash
+```http
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5
Content-Length: 376
@@ -137,13 +148,13 @@ Content-Range: bytes 1000-1099/2000
### 状态码 100
-HTTP/1.1 中新加入了状态码`100`。该状态码的使用场景为,存在某些较大的文件请求,服务器可能不愿意响应这种请求,此时状态码`100`可以作为指示请求是否会被正常响应,过程如下图:
+HTTP/1.1 中新加入了状态码 `100`。该状态码的使用场景为,存在某些较大的文件请求,服务器可能不愿意响应这种请求,此时状态码 `100` 可以作为指示请求是否会被正常响应,过程如下图:
-
+
-
+
-然而在 HTTP/1.0 中,并没有`100 (Continue)`状态码,要想触发这一机制,可以发送一个`Expect`头部,其中包含一个`100-continue`的值。
+然而在 HTTP/1.0 中,并没有 `100 (Continue)` 状态码,要想触发这一机制,可以发送一个 `Expect` 头部,其中包含一个 `100-continue` 的值。
### 压缩
@@ -151,15 +162,15 @@ HTTP/1.1 中新加入了状态码`100`。该状态码的使用场景为,存在
HTTP/1.1 则对内容编码(content-codings)和传输编码(transfer-codings)做了区分。内容编码总是端到端的,传输编码总是逐跳的。
-HTTP/1.0 包含了`Content-Encoding`头部,对消息进行端到端编码。HTTP/1.1 加入了`Transfer-Encoding`头部,可以对消息进行逐跳传输编码。HTTP/1.1 还加入了`Accept-Encoding`头部,是客户端用来指示他能处理什么样的内容编码。
+HTTP/1.0 包含了 `Content-Encoding` 头部,对消息进行端到端编码。HTTP/1.1 加入了 `Transfer-Encoding` 头部,可以对消息进行逐跳传输编码。HTTP/1.1 还加入了 `Accept-Encoding` 头部,是客户端用来指示它能处理什么样的内容编码。
## 总结
-1. **连接方式** : HTTP 1.0 为短连接,HTTP 1.1 支持长连接。
-1. **状态响应码** : HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
-1. **缓存处理** : 在 HTTP1.0 中主要使用 header 里的 If-Modified-Since,Expires 来做为缓存判断的标准,HTTP1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。
-1. **带宽优化及网络连接的使用** :HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
-1. **Host 头处理** : HTTP/1.1 在请求头中加入了`Host`字段。
+1. **连接方式**:HTTP/1.0 为短连接,HTTP/1.1 支持长连接。
+2. **状态响应码**:HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
+3. **缓存处理**:在 HTTP/1.0 中主要使用 header 里的 `If-Modified-Since`、`Expires` 来作为缓存判断的标准,HTTP/1.1 则引入了更多的缓存控制策略,例如 `Entity Tag`、`If-Unmodified-Since`、`If-Match`、`If-None-Match` 等更多可供选择的缓存头来控制缓存策略。
+4. **带宽优化及网络连接的使用**:HTTP/1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能。HTTP/1.1 则在请求头引入了 `Range` 头域,它允许只请求资源的某个部分,即返回码是 `206 (Partial Content)`,这样就方便了开发者自由选择以便于充分利用带宽和连接。
+5. **Host 头处理**:HTTP/1.1 在请求头中加入了 `Host` 字段。
## 参考资料
diff --git a/docs/cs-basics/network/https-rsa-vs-ecdhe.md b/docs/cs-basics/network/https-rsa-vs-ecdhe.md
new file mode 100644
index 00000000000..2cda6aa813f
--- /dev/null
+++ b/docs/cs-basics/network/https-rsa-vs-ecdhe.md
@@ -0,0 +1,452 @@
+---
+title: HTTPS 握手里的 RSA 和 ECDHE,到底差在哪?(应用层)
+description: 对比 TLS 握手中 RSA 密钥交换与 ECDHE 密钥交换的核心差异,讲清前向安全、密码套件命名、TLS 1.3 变化及面试要点。
+category: 计算机基础
+tag:
+ - 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: HTTPS,RSA,ECDHE,TLS,握手,前向安全,密钥交换,密码套件,TLS 1.3,PreMasterSecret
+---
+
+很多人第一次学 HTTPS,脑子里会留下一个很粗的印象:
+
+**HTTPS = HTTP + 加密,加密 = RSA。所以,HTTPS = RSA 加密。**
+
+这个理解不是凭空来的。早期很多 HTTPS 部署确实大量使用 RSA 相关的密码套件,很多入门讲解也喜欢拿 RSA 举例。
+
+但严格说,HTTPS 从来不等于 RSA 加密。即使在 TLS 1.0、TLS 1.1 时代,RSA 也只是可选方案之一,协议里还存在 DHE 这类密钥交换方式。到了 TLS 1.3,静态 RSA 密钥交换已经被移除,RSA 更多出现在证书签名、身份认证这类位置。
+
+所以,这篇文章真正要对比的不是“RSA 和 ECDHE 谁更高级”。
+
+**RSA 握手里,会话密钥材料是客户端生成后加密发给服务端;ECDHE 握手里,会话密钥材料不是直接传过去的,而是客户端和服务端各自算出来的。**
+
+这篇文章主要回答几个问题:
+
+1. HTTPS 为什么不等于 RSA 加密?
+2. RSA 握手和 ECDHE 握手的会话密钥材料分别是怎么来的?
+3. ECDHE 为什么能提供前向安全性?
+4. TLS 1.3 为什么移除静态 RSA 密钥交换?
+
+把这些问题讲清楚了,`PreMasterSecret`、`Server Key Exchange`、前向安全、TLS 1.3 为什么移除静态 RSA,后面都能顺着理解。
+
+
+
+## TLS 握手的两个核心问题
+
+HTTPS 仍然基于 HTTP,也仍然依赖 TCP。区别在于,HTTP 报文不会直接裸跑在 TCP 之上,而是先经过 TLS 完成身份认证、密钥协商和加密保护。
+
+握手完成后,真正保护业务数据的通常是 AES-GCM 这类对称加密算法,而不是拿 RSA 去加密完整的请求和响应。
+
+这里有两个问题。
+
+**第一个问题:浏览器和服务器需要协商出一份会话密钥。**
+
+后面传输 HTTP 请求、Cookie、响应体时,就用这份会话密钥做对称加密。对称加密更适合处理大量数据;非对称加密计算成本高,一般不拿来直接加密完整网页内容。
+
+**第二个问题:浏览器需要确认对面真的是目标网站。**
+
+如果只是“服务器发一个公钥给浏览器”,那中间人也可以发自己的公钥。浏览器以为那是目标网站的公钥,后面就把秘密信息加密给了攻击者。证书、CA、数字签名解决的是这件事:证明这个公钥确实和这个域名绑定,而不是路上某个人塞进来的。
+
+RSA 握手和 ECDHE 握手都会面对这两个问题。只是它们解决“会话密钥怎么来”的方式不同。
+
+## RSA 握手:密钥材料加密发送
+
+### 完整握手流程
+
+先看 TLS 1.2 里的 RSA 密钥交换。
+
+浏览器先发 `ClientHello`。这里面会带上客户端支持的 TLS 版本、支持的密码套件、一个随机数 `Client Random`。
+
+服务器收到之后,回 `ServerHello`,选定 TLS 版本和密码套件,也给出一个随机数 `Server Random`,然后把自己的证书发给客户端。
+
+到这里,客户端拿到了服务器证书。它会验证证书链、域名、有效期、签名这些信息。证书验证通过后,客户端就从证书里取出服务器的 RSA 公钥。
+
+接下来是关键步骤:客户端生成一个新的随机值,也就是 `PreMasterSecret`。在 TLS 1.2 的 RSA 密钥交换里,这个值是 **48 字节**。客户端会用服务器证书里的 RSA 公钥加密 `PreMasterSecret`,再把加密结果放进 `Client Key Exchange` 发给服务器。
+
+服务器收到后,用自己的 RSA 私钥解密,拿到同一份 `PreMasterSecret`。
+
+这时,客户端和服务端手里都有三份材料:
+
+```text
+Client Random
+Server Random
+PreMasterSecret
+```
+
+双方再根据这三份材料派生出 `Master Secret`,后续的会话密钥也会从这里继续派生出来。真正传 HTTP 请求和响应时,用的是这些派生出来的对称密钥。
+
+用一句话压缩:
+
+**RSA 握手的会话密钥材料,是客户端生成后“包起来”寄给服务器的。**
+
+这里的“包起来”,靠的就是服务器 RSA 公钥。只有持有对应 RSA 私钥的服务器,才能拆开这个包。
+
+看起来挺合理。客户端生成秘密,服务器私钥解密,双方得到同一份材料,再结合两个随机数派生出后续会话密钥。
+
+但问题也在这里。
+
+### 没有前向安全:长期私钥太值钱
+
+假设攻击者今天抓到了一段 HTTPS 流量,但当时没有服务器私钥,所以看不懂里面的内容。这时他可以先把流量保存下来。
+
+一年后,如果服务器 RSA 私钥泄漏了,会发生什么?
+
+在 RSA 密钥交换里,客户端当年发出的 `PreMasterSecret` 是用服务器 RSA 公钥加密的。如果攻击者完整捕获了握手阶段的明文随机数,也就是 `Client Random`、`Server Random`,同时保存了加密后的 `PreMasterSecret`,再结合后来泄漏的服务器私钥,就可能解开当时的 `PreMasterSecret`,继续派生出那次连接用过的会话密钥。
+
+旧数据就有机会被翻出来。
+
+这里要注意条件:不是“只要私钥泄漏,所有历史流量必然能解”。攻击者至少得拿到足够完整的握手数据和应用数据。如果只有单向片段,或者握手日志不完整,即使有私钥,也未必能把那次会话还原出来。
+
+但从安全设计上看,这个风险已经足够麻烦。长期私钥一旦变成打开历史流量的总钥匙,它的影响就不再只覆盖未来连接,也会波及过去已经发生过的通信。
+
+这里批评的不是 RSA 算法本身“不能用”。RSA 仍然可以用于签名认证,也可以出现在证书体系里。问题出在“用长期不变的服务器私钥去解密历史握手里的密钥材料”。
+
+服务器私钥一旦泄漏,代价太大。
+
+
+
+### 另一个历史包袱:填充预言机攻击
+
+RSA 密钥交换还有一个工程层面的麻烦:`PreMasterSecret` 不是直接裸加密,而是按 RSAES-PKCS1-v1_5 这类格式封装后再加密。
+
+这个细节曾经引出过 Bleichenbacher 这类填充预言机攻击。
+
+它的大致思路是:攻击者不一定要马上拿到服务器私钥,而是反复构造不同的密文发给服务器,观察服务器对“填充错误、版本错误、长度错误”的处理差异。如果服务端在错误码、响应时间、日志行为、连接关闭方式上露出差别,攻击者就可能一点点逼近明文。
+
+这类攻击麻烦的地方在于,它不是单纯的数学问题,而是实现问题。
+
+TLS 1.2 对这类情况做过防御要求:服务端即使解密失败,也不要把具体失败原因暴露出去,而是继续用随机值走完整个流程,避免攻击者通过差异行为判断密文是否接近正确格式。
+
+可规范要求不等于实现可靠。2017 年的 ROBOT 攻击再次说明,一些服务端仍然可能因为细小的行为差异暴露出 RSA 解密 oracle。错误码、耗时、日志、分支路径,只要有一处表现不一致,都可能变成侧信道。
+
+所以,静态 RSA 密钥交换被淘汰,不只是因为它没有前向安全,也因为它把太多风险压到了实现细节上。
+
+### 能否被降级回 RSA?
+
+这里还要补一个容易误解的点。
+
+TLS 1.2 里,客户端会在 `ClientHello` 里带上自己支持的密码套件列表,服务端从里面选一个双方都支持的套件。理论上,如果服务端仍然开放 `TLS_RSA_*` 这类静态 RSA 密钥交换套件,老客户端就可能继续用 RSA 握手。
+
+但这不等于“中间人随便把 ClientHello 里的 ECDHE 删掉,就能让连接悄悄降级到 RSA”。握手最后的 `Finished` 会校验握手 transcript,简单篡改 `ClientHello` 通常会导致校验失败,连接建立不起来。
+
+历史上确实发生过降级相关攻击,比如 FREAK 和 Logjam。它们利用的是当时一些客户端、服务端仍然支持出口级弱密码套件,再结合实现和配置问题,把连接压到更弱的 RSA_EXPORT 或 DHE_EXPORT 路径上,而不是“随便删掉 ECDHE 就能静默成功”。TLS 1.3 在 `ServerHello.random` 里加入降级保护值,也是在提醒我们:协议本身一直在补这类历史攻击面。
+
+真正需要关注的是服务端配置本身:如果已经不需要兼容很老的客户端,就应该关闭静态 RSA 密钥交换套件,只保留支持前向安全的套件。否则,环境里仍然可能存在客户端或错误配置走到 RSA 握手。
+
+这也是排查 TLS 配置时要看密码套件实际协商结果的原因。只看“服务器支持 ECDHE”不够,还要看它是否同时保留了 `TLS_RSA_*` 这类旧套件。
+
+## ECDHE 握手:密钥材料双方协商
+
+### DH 的核心思路
+
+ECDHE 里的 `DHE` 来自 Diffie-Hellman Ephemeral,意思是临时 Diffie-Hellman。前面的 `EC` 是 Elliptic Curve,表示基于椭圆曲线。
+
+别被名字吓住。先不看椭圆曲线,先看 DH 想解决什么问题。
+
+DH 的目标很有意思:通信双方不直接传输共享秘密,却能各自算出同一个共享秘密。
+
+可以粗略理解成这样:
+
+客户端生成一个临时私钥,只留在本地,再算出一个临时公钥发给服务器。服务器也生成一个临时私钥,只留在本地,再算出一个临时公钥发给客户端。
+
+双方交换的都是公钥。攻击者在网络里能看到这些公钥,但看不到双方各自的临时私钥。
+
+接着,客户端用“自己的临时私钥 + 服务器临时公钥”算出共享秘密;服务器用“自己的临时私钥 + 客户端临时公钥”也算出同一个共享秘密。
+
+共享秘密没有在网络上传输过。
+
+ECDHE 只是把这个过程放到椭圆曲线体系里做。椭圆曲线的数学理论更抽象,但在同等安全强度下,它通常能用更短的密钥达到相近的安全级别,运算和传输成本也比传统有限域 DHE 更低。对理解 TLS 握手来说,先记住一句话就够了:
+
+**ECDHE 的会话密钥材料不是某一方生成后发给另一方,而是双方通过临时密钥协商出来的。**
+
+### 完整握手流程
+
+再看 TLS 1.2 里常见的 `ECDHE_RSA` 握手。
+
+客户端还是先发 `ClientHello`,里面有 TLS 版本、支持的密码套件、`Client Random`。服务器回 `ServerHello`,选择一个密码套件,比如:
+
+```text
+TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+```
+
+这个密码套件名要拆开看,不能看到 RSA 就以为它还在用 RSA 加密会话密钥。
+
+- `ECDHE` 表示密钥交换方式。
+- `RSA` 表示认证签名方式。
+- `AES_256_GCM` 表示后续记录数据使用 AES,密钥长度 256 位,模式是 GCM。
+- `SHA384` 指定 TLS 1.2 PRF 和 `Finished` 消息使用的哈希算法。
+
+GCM 本身已经提供记录层的完整性保护,所以这里的 `SHA384` 不再表示记录层 MAC,而是主要参与握手阶段的密钥派生和验证。
+
+服务端接着发证书。以 `ECDHE_RSA` 为例,证书里的 RSA 公钥主要用于验证服务端签名,而不是让客户端拿它加密 `PreMasterSecret`。
+
+然后,ECDHE 和 RSA 握手开始分叉。
+
+在 ECDHE 握手里,服务端会发送 `Server Key Exchange`。这个消息里会包含服务端选择的椭圆曲线参数,以及服务端临时 ECDHE 公钥。
+
+**问题来了:客户端怎么知道这份临时 ECDHE 公钥没有被中间人换掉?**
+
+**答案是签名。**
+
+服务端会用证书对应的私钥,对握手参数做签名。客户端收到后,用证书里的公钥验证签名。如果签名验证通过,客户端就能确认:这份临时 ECDHE 公钥确实来自持有证书私钥的服务器,不是路上被人替换的。
+
+随后客户端也生成自己的临时 ECDHE 私钥和公钥,把客户端临时公钥通过 `Client Key Exchange` 发给服务器。
+
+到这一步,双方都有了计算共享秘密需要的材料。
+
+客户端手里有:
+
+```text
+客户端临时私钥
+服务端临时公钥
+Client Random
+Server Random
+```
+
+服务端手里有:
+
+```text
+服务端临时私钥
+客户端临时公钥
+Client Random
+Server Random
+```
+
+两边各自计算出同一个共享秘密,再派生出后续使用的会话密钥。
+
+这里再强调一次:
+
+**ECDHE_RSA 里的 RSA,不是用来加密传输会话密钥的。它负责证明“这份 ECDHE 临时参数确实是服务器发的”。**
+
+这也是很多人看到密码套件名字后最容易误会的地方。
+
+
+
+### 密码套件名怎么读
+
+TLS 1.2 的密码套件名字通常可以按这条线拆:
+
+```text
+TLS_密钥交换算法_认证算法_WITH_对称加密算法_哈希算法
+```
+
+例如:
+
+```text
+TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+```
+
+可以拆成:
+
+```text
+ECDHE:密钥交换
+RSA:身份认证,也就是服务端签名
+AES_128_GCM:后续记录层加密算法
+SHA256:TLS 1.2 PRF 和 Finished 消息使用的哈希算法;如果是 GCM 套件,它不再充当记录层 MAC
+```
+
+再看另一个:
+
+```text
+TLS_RSA_WITH_AES_128_GCM_SHA256
+```
+
+这里的 `RSA` 出现在 `WITH` 前面,而且没有 `ECDHE`,表示密钥交换和身份认证都和 RSA 绑定。这类就是典型的静态 RSA 密钥交换套件。
+
+到了 TLS 1.3,密码套件命名变了,比如:
+
+```text
+TLS_AES_128_GCM_SHA256
+```
+
+你会发现,它不再把密钥交换和认证方式写进密码套件名里。TLS 1.3 把这些信息拆到其他扩展和握手消息中,密码套件名主要描述记录层 AEAD 算法和 HKDF 使用的哈希算法。
+
+所以,看到 TLS 1.3 的 `TLS_AES_128_GCM_SHA256`,不要误以为它“没有密钥交换”。密钥交换还在,只是不用 TLS 1.2 那套命名方式写出来了。
+
+
+
+## 前向安全与性能代价
+
+### ECDHE 为什么有前向安全
+
+关键在 `E`,也就是 `Ephemeral`,临时。
+
+ECDHE 握手里的私钥不是服务器证书那把长期私钥,而是握手过程中使用的临时私钥。连接结束后,正常情况下不应该再依赖这份临时材料。
+
+这带来的结果是:攻击者今天抓包,未来某天拿到了服务器证书私钥,也不能仅靠这把长期私钥还原过去每次握手里的临时共享秘密。因为当时真正参与密钥协商的是那次握手里的 ECDHE 临时私钥,而不是证书私钥。
+
+证书私钥在这里更像“签字笔”,不是“保险柜钥匙”。
+
+RSA 密钥交换里,服务器私钥可以直接打开客户端发来的 `PreMasterSecret`;ECDHE 里,服务器私钥只是给临时参数签名,证明身份。它不直接参与每次连接共享秘密的计算。
+
+这个角色变化,决定了两者在历史流量保护上的差异。
+
+
+
+不过,前向安全不是免死金牌。
+
+如果服务端随机数质量很差,临时私钥被日志记录下来,或者实现里出现内存泄漏,ECDHE 也救不了你。工程实现里,为了降低握手成本,部分实现还可能短时间复用临时 DH/ECDH 私密材料:有限域 DH 场景常说“指数复用”,ECDH 场景更常说“临时私钥/标量复用”。如果复用时间过长,前向安全的粒度就会变粗。
+
+还有一类风险来自参数校验。比如服务端没有正确校验客户端发来的椭圆曲线点是否在合法曲线上,就可能给无效曲线攻击留下空间。正常开发者不一定会直接写这层代码,但它提醒我们:密码学协议不只是“选对算法”就结束了,TLS 库实现和配置同样重要。
+
+### 会话恢复的影响
+
+还有一个容易被忽略的点:**会话恢复。**
+
+完整 ECDHE 握手要做临时密钥协商,成本不低。为了减少握手开销,TLS 支持会话恢复。客户端下次访问同一个站点时,可以尝试复用之前协商过的会话状态,避免每次都完整走一遍握手。
+
+问题在于,会话恢复也有自己的安全边界。
+
+以 TLS 1.2 的会话票据为例,服务端会用一把票据加密密钥保护会话状态,客户端后续带着票据回来,服务端解开票据后恢复会话。如果这把票据加密密钥长期不轮换,一旦它泄漏,攻击者就可能解开过去收集到的票据,并进一步还原相关恢复会话的密钥材料。
+
+这时,前向安全的窗口就不再是“一次连接”,而会被拉长到“票据加密密钥的生命周期”。
+
+所以线上配置不能只看“是否启用了 ECDHE”。会话票据密钥怎么生成、怎么轮换、是否在多台机器间共享、泄漏后影响多大,也要算进去。
+
+### 性能不是免费的
+
+ECDHE 带来了前向安全,但它也有成本。
+
+RSA 密钥交换的主路径,是服务端用长期 RSA 私钥解开客户端发来的 `PreMasterSecret`。ECDHE_RSA 则需要完成临时 ECDH 协商,还要对服务端临时参数做签名。
+
+对高并发服务来说,TLS 握手会消耗 CPU,尤其是短连接多、会话恢复命中率低的时候。
+
+这里不能简单写成“ECDHE 一定比 RSA 慢”。实际开销取决于 RSA 密钥长度、椭圆曲线选择、签名算法、TLS 库实现、CPU 指令集、会话恢复命中率等因素。比如 X25519、P-256、RSA 2048、RSA 3072 在不同 CPU 和不同 TLS 库上的表现都不一样。
+
+如果真要判断成本,最靠谱的方法不是引用别人的固定数字,而是在目标机器上压测。至少要区分三件事:
+
+```text
+1. 单次密码学操作耗时
+2. 完整 TLS 握手耗时
+3. 业务请求端到端耗时
+```
+
+第一项可以用 `openssl speed` 粗看数量级,比如测试 RSA、ECDH、X25519 的运算能力;第二项要看 TLS 库和服务端配置;第三项还会受网络、连接复用、应用逻辑影响。
+
+所以线上不会只靠“换成 ECDHE”解决所有问题。更常见的做法是配合 TLS 1.3、会话恢复、合理的证书算法和曲线选择,必要时再用硬件加速。
+
+安全性和性能不是二选一,但也不能假装没有成本。
+
+## TLS 1.3 的变化
+
+如果只看 TLS 1.2,RSA 和 ECDHE 可以作为两种密钥交换方式来对比。
+
+但到了 TLS 1.3,静态 RSA 密钥交换已经被移除,握手结构也改了。
+
+TLS 1.2 完整握手通常需要 2 个 RTT。客户端先发 `ClientHello`,服务端回 `ServerHello`、证书和相关握手消息,客户端再发密钥交换和 `Finished`,服务端最后回 `Finished`。
+
+TLS 1.3 则把密钥交换参数提前放进 `ClientHello` 的 `key_share`。服务端第一轮响应就能返回自己的 `key_share`,完整握手通常压到 1 个 RTT。
+
+2 RTT 变 1 RTT 能省多少毫秒,取决于网络环境。同机房可能只是几毫秒;跨地域、移动网络、高丢包场景下,少一个 RTT 才更容易被感知。
+
+不过,TLS 1.3 也不是任何情况下都稳稳 1 RTT。如果客户端带的 `key_share` 和服务端支持的曲线不匹配,服务端会返回 `HelloRetryRequest`,要求客户端换一组参数再来一次。这时握手可能重新接近 2 RTT。
+
+所以生产环境里,客户端和服务端对常见密钥协商组的支持要尽量对齐,比如 `X25519`、`secp256r1` 这类常见选择。否则 TLS 1.3 的 1 RTT 优势可能打折。
+
+
+
+至于后量子混合密钥交换、0-RTT、PSK-only、mTLS,这些都属于另一条线,本文不展开。
+
+## RSA vs ECDHE 核心差异速查
+
+放到一起看,差异就很清楚了。
+
+| 对比项 | RSA 密钥交换 | ECDHE 密钥交换 |
+| ------------------ | ------------------------------------------------------- | ------------------------------------------------------ |
+| 常见版本背景 | TLS 1.2 及更早版本可见 | TLS 1.2 常见,TLS 1.3 延续临时密钥协商方向 |
+| 会话密钥材料怎么来 | 客户端生成 `PreMasterSecret`,用服务器 RSA 公钥加密发送 | 双方各自生成临时密钥对,通过 ECDHE 算出共享秘密 |
+| 服务器私钥的作用 | 解密客户端发来的 `PreMasterSecret` | 对临时 ECDHE 参数签名,证明参数来自真实服务端 |
+| 网络上传了什么 | 加密后的 `PreMasterSecret` | 双方临时公钥和签名后的参数 |
+| 是否支持前向安全 | 不支持 | 支持,前提是临时密钥正确生成、使用后不再保留 |
+| 私钥泄漏后的影响 | 在握手数据完整捕获的情况下,历史流量可能被解密 | 仅靠证书私钥,通常无法解开历史流量 |
+| 典型问题 | 长期私钥价值过高,存在 PKCS#1 v1.5 填充预言机历史包袱 | 握手有额外计算成本,参数校验和临时密钥管理依赖实现质量 |
+| TLS 1.3 情况 | 静态 RSA 密钥交换已移除 | 临时密钥协商成为主线 |
+
+
+
+如果你要在面试里快速讲,可以这样说:
+
+**RSA 握手是“客户端生成秘密,用服务器公钥加密发过去”;ECDHE 握手是“双方交换临时公钥,各自算出同一个秘密”。RSA 的服务器私钥能解历史握手材料,所以没有前向安全;ECDHE 的证书私钥只做签名认证,不直接解会话秘密,所以更适合现代 HTTPS。**
+
+这段就够用了。
+
+### 常见误读:ECDHE_RSA 不是两种算法都加密
+
+再单独说一下 `ECDHE_RSA`,因为这个名字太容易让人误读。
+
+很多人看到:
+
+```text
+TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+```
+
+第一反应是:是不是先做一轮 ECDHE 运算,再做一轮 RSA 加密?
+
+不是。
+
+在这个密码套件里:
+
+密钥交换用 ECDHE;
+身份认证用 RSA 签名;
+后续数据加密用 AES-256-GCM;
+相关哈希使用 SHA384。
+
+这也解释了为什么“HTTPS 还在用 RSA”这句话要小心说。
+
+用 RSA 做证书签名,和用 RSA 做密钥交换,是两件事。
+
+前者在现代 HTTPS 里仍然常见,后者已经不适合作为现代 TLS 的主线。
+
+### RSA 在现代 HTTPS 里的实际角色
+
+学习 HTTPS 握手时,很多入门资料喜欢用一句话概括:
+
+非对称加密交换对称密钥。
+
+这句话在入门阶段有帮助,但不够准确。它更像是在描述早期 RSA 密钥交换的思路。
+
+到了 ECDHE,密钥不是简单“加密后传输”,而是双方协商出来的。到了 TLS 1.3,密钥交换、身份认证、记录层加密的边界更清楚:临时密钥协商负责生成共享秘密,证书负责身份认证,对称加密负责保护后续应用数据。
+
+更准确的说法应该是:
+
+HTTPS 的业务数据通常用对称密钥加密;TLS 握手负责协商这份密钥并验证身份。RSA 可以参与身份认证,也曾经可以参与密钥交换;ECDHE 负责临时密钥协商,能避免历史会话因为未来证书私钥泄漏而直接暴露。
+
+如果把这几件事混在一起,就很容易得出错误结论:看到 RSA 就以为它在加密会话密钥,看到 ECDHE_RSA 就以为两种算法都在做加密。
+
+事实不是这样。
+
+## 用一次完整请求串起来
+
+浏览器访问一个 HTTPS 网站时,TCP 连接先建立起来。接着 TLS 握手开始。
+
+如果是 TLS 1.2 的 RSA 密钥交换,客户端验证证书后,生成 48 字节的 `PreMasterSecret`,用服务器证书里的 RSA 公钥加密发给服务器。服务器用 RSA 私钥解密,双方再结合两个随机数派生会话密钥。
+
+如果是 TLS 1.2 的 ECDHE_RSA,服务器发证书后,还会发 `Server Key Exchange`,里面带着临时 ECDHE 公钥和签名。客户端验证签名后,也生成自己的临时 ECDHE 公钥发回去。双方不传输最终共享秘密,而是各自算出同一个共享秘密,再派生会话密钥。
+
+这两个流程看起来只差了几个握手消息,安全性质却差很多。
+
+RSA 密钥交换的问题是历史包袱太重:长期私钥一旦泄漏,过去保存下来的流量也可能遭殃;再加上 PKCS#1 v1.5 填充预言机这类实现风险,它已经不适合作为现代 TLS 密钥交换方案。
+
+ECDHE 把每次连接的密钥协商换成临时过程,让服务器长期私钥不再成为打开历史流量的钥匙。它也有计算成本,也依赖正确实现和配置,但方向更符合现代 HTTPS 的安全要求。
+
+这篇文章只聚焦一个问题:**RSA 密钥交换和 ECDHE 密钥交换到底差在哪**。如果继续往下讲,还可以展开 TLS 1.3 的 0-RTT、PSK、会话票据轮换、mTLS、证书透明、后量子迁移,这些都值得单独写。
+
+所以,面试里问“RSA 和 ECDHE 握手有什么区别”,不要只回答“一个不支持前向安全,一个支持前向安全”。
+
+真正要讲的是:
+
+**RSA 是把秘密加密送过去;ECDHE 是双方临时协商出来。**
+
+把这句话讲透,后面的 `PreMasterSecret`、`Server Key Exchange`、前向安全、TLS 1.3 为什么移除静态 RSA,就都能顺着讲下去了。
+
+## 面试怎么回答:HTTPS 握手里的 RSA 和 ECDHE,到底差在哪?
+
+RSA 和 ECDHE 的核心区别在于:**会话密钥材料是“传过去的”,还是“协商出来的”**。
+
+在 TLS 1.2 的静态 RSA 握手里,客户端生成 `PreMasterSecret`,用服务器证书里的 RSA 公钥加密后发给服务端,服务端再用 RSA 私钥解密。问题是,如果攻击者保存了当年的握手流量,后来服务器私钥又泄漏,就可能回头解出历史会话密钥,所以它没有前向安全。
+
+ECDHE 不直接传输共享秘密。客户端和服务端各自生成临时密钥对,交换临时公钥后,双方本地算出同一个共享秘密。服务器证书私钥主要用于签名认证,证明临时参数没被中间人替换,而不是用来解密会话密钥。
+
+所以一句话总结:**RSA 是客户端把秘密加密送过去;ECDHE 是双方用临时密钥协商出秘密。ECDHE 支持前向安全,也因此成为现代 HTTPS 的主流方向。**
diff --git a/docs/cs-basics/network/images/arp/2008410143049281.png b/docs/cs-basics/network/images/arp/2008410143049281.png
deleted file mode 100644
index 759fb441f6c..00000000000
Binary files a/docs/cs-basics/network/images/arp/2008410143049281.png and /dev/null differ
diff --git a/docs/cs-basics/network/maximum-number-of-tcp-connections-per-host.md b/docs/cs-basics/network/maximum-number-of-tcp-connections-per-host.md
new file mode 100644
index 00000000000..183450d9ca4
--- /dev/null
+++ b/docs/cs-basics/network/maximum-number-of-tcp-connections-per-host.md
@@ -0,0 +1,160 @@
+---
+title: 一台主机上只能保持最多 65535 个 TCP 连接吗?
+description: 从 TCP 四元组、临时端口、文件描述符、内存、TIME_WAIT 与 NAT 等角度,解释一台主机能保持多少 TCP 连接。
+category: 计算机基础
+tag:
+ - 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP连接数,65535,TCP四元组,TIME_WAIT,临时端口,文件描述符,NAT
+---
+
+一台主机最多只能保持 65535 个 TCP 连接吗?小 G 先给结论:**不是**。
+
+`65535` 这个数字来自端口号范围。TCP 首部里的源端口和目的端口字段都是 16 位,可以表示 `0~65535`,一共 2^16 = 65536 个取值。**65535 是最大端口号,不是连接数上限。**
+
+但 TCP 连接数和端口号数不是一回事。要搞清楚这个问题,得从 TCP 连接是怎么被标识的开始讲。
+
+## TCP 连接靠四元组来区分
+
+TCP 连接不是靠“本地端口”唯一标识,而是靠四元组标识:
+
+```text
+源 IP、源端口、目的 IP、目的端口
+```
+
+只要四元组不同,内核就可以把它们识别为不同连接。
+
+为了避免混淆,下面统一用**客户端发起连接时的视角**来写四元组:`(客户端 IP, 客户端端口, 服务端 IP, 服务端端口)`。
+
+假设服务器 IP 是 `192.168.1.100`,监听端口 `8080`:
+
+
+
+- 客户端 A(`10.0.0.1:50000`)连过来 → 四元组 `(10.0.0.1, 50000, 192.168.1.100, 8080)`
+- 客户端 A(`10.0.0.1:50001`)再连过来 → 四元组 `(10.0.0.1, 50001, 192.168.1.100, 8080)`
+- 客户端 B(`10.0.0.2:50000`)连过来 → 四元组 `(10.0.0.2, 50000, 192.168.1.100, 8080)`
+
+三条连接,服务端的 IP 和端口都没变,但因为客户端 IP 或端口不同,四元组各不相同,所以是三条独立的连接。
+
+这里有个容易混淆的点:服务端 `8080` 端口只有**一个监听 socket**,但每 `accept()` 一次,内核就会生成一个新的**已连接 socket**,用四元组来区分。所以多个连接共享同一个服务端端口,完全不冲突。
+
+## 为什么服务端可以超过 65535?
+
+假设 Web 服务监听 `192.168.1.100:443`,服务端 IP 和端口固定,但客户端 IP 和端口会变化。比如 `(10.0.0.1, 50001, 192.168.1.100, 443)` 和 `(10.0.0.2, 50001, 192.168.1.100, 443)` 的服务端端口都是 443,但四元组不同,所以是两条不同 TCP 连接。
+
+纯从 IPv4 四元组组合看,固定服务端 IP 和端口后,客户端 IP 理论上有 `2^32` 种可能,客户端端口有 `2^16` 种可能,理论组合数非常大。
+
+真实上限来自资源和配置。
+
+## 真正的限制是什么?
+
+**1、文件描述符(File Descriptor,FD)和内存。**
+
+在 Linux 里,socket 也是文件。对应用进程来说,`accept()` 后的每条已建立连接通常对应一个 socket FD;**监听 socket 本身也占一个 FD**。还没被 `accept()` 的连接会先停留在内核队列里,不应简单都算成应用已持有的 FD。
+
+进程可打开文件数不够时,常见报错是 `Too many open files`。
+
+每条 TCP 连接都需要内核维护 socket、TCP 控制块、发送缓冲区、接收缓冲区等数据。连接空闲时开销较小,一旦有数据收发,缓冲区和应用对象也会继续占内存。
+
+不建议死记“一个连接占多少 KB”。这个值会受内核版本、socket 选项、缓冲区大小和业务收发情况影响。
+
+**2、握手队列和 accept 速度。**
+
+Linux 实际上维护两个队列:
+
+- **SYN 队列(半连接队列)**:收到 SYN、发出 SYN-ACK、尚未完成三次握手的连接,受 `tcp_max_syn_backlog` 限制,实际大小还会结合 `somaxconn` 和 `listen()` backlog 计算。
+- **accept 队列(全连接队列)**:已完成握手、等待应用 `accept()` 的连接,上限为 `min(listen(fd, backlog), net.core.somaxconn)`。
+
+它们影响的是**连接建立阶段的排队和丢弃**,不是 ESTABLISHED 连接总数的简单上限。
+
+半连接队列溢出时,Linux 可以启用 SYN Cookie 机制:服务端把必要信息编码进 SYN-ACK 的序列号,不在本地保留完整的半连接状态,收到合法 ACK 后再重建连接信息。SYN Cookie 是防护手段,不是扩容手段。
+
+全连接队列溢出时,行为取决于 `tcp_abort_on_overflow`:默认值 `0` 时,服务端会丢弃客户端发来的 ACK,让客户端重传,服务端有机会重传 SYN-ACK;设为 `1` 时,直接回复 RST,快速失败。生产环境通常保持默认值 `0`,避免误拒正常连接。排查全连接队列溢出可以用 `ss -ltn`:如果 Recv-Q 长时间接近 Send-Q,说明 accept 不够及时,要检查应用线程池是否卡住或 backlog 配置是否过小。
+
+**3、CPU、网卡和业务处理能力。**
+
+空闲长连接主要考验内存、FD 上限、内核连接表和连接保活策略;活跃连接还会带来系统调用、加解密、协议解析、线程调度和网卡中断等压力。
+
+## 客户端为什么更容易撞到端口限制?
+
+
+
+服务端不是 65535 上限,但客户端访问同一个目标时,临时端口可能先耗尽。
+
+例如客户端固定为 `192.168.1.10`,不断连接 `10.0.0.1:443`。这时目的 IP、目的端口、源 IP 都固定,只剩源端口可变。源端口用完后,就无法再创建新四元组。
+
+Linux 自动分配临时端口范围可以这样看:
+
+```bash
+sysctl net.ipv4.ip_local_port_range
+```
+
+Mac 下可以这样查看:
+
+
+
+很多 Linux 环境默认临时端口范围是 `32768 60999`,大约 2.8 万个端口;实际值以 `sysctl net.ipv4.ip_local_port_range` 输出为准,且不是全部 `0~65535` 都会自动拿来做临时端口。
+
+看到 `Cannot assign requested address` / `EADDRNOTAVAIL`、大量 `connect` 失败,且目标 `IP:Port` 很集中时,要怀疑临时端口耗尽或 `TIME_WAIT` 堆积。
+
+## NAT 网关这层也可能先顶不住
+
+还有一种情况容易被忽略:很多内网机器并不是直接访问公网,而是先经过 NAT 网关。
+
+NAT 做的事情是把内网地址转换成公网地址。比如内网机器 `192.168.1.10:50000` 访问外部服务时,NAT 可能会改成 `203.0.113.1:40000`,并在本地记录这条映射。响应包回来后,再根据映射关系转发回原来的内网机器。
+
+如果大量内网机器共享同一个公网 IP,并集中访问**同一个外部 `IP:Port`**,NAT 侧可用的公网源端口数量就会成为限制因素。如果目标分散,端口复用空间会更大。端口不够只是其中一类问题,NAT 设备的连接跟踪表、CPU、内存也可能先到瓶颈。
+
+所以排查连接数问题时,不要只盯着客户端和服务端,链路中间的 NAT 网关也要看。
+
+常见的 NAT 侧排查指标包括:NAT 连接跟踪表使用率、SNAT 端口使用率、单公网 IP 到单目标的连接数,以及 NAT 设备的 CPU、内存、丢包和连接创建速率。如果 NAT 确实成了瓶颈,可以考虑增加公网 IP、拆分出口或做连接复用。
+
+## TIME_WAIT 会怎样影响连接数?
+
+
+
+典型情况下,**主动关闭连接的一方会进入 `TIME_WAIT`**——因为它需要在发送最后一个 ACK 后等待一段时间,防止最后 ACK 丢失以及旧报文影响后续连接。(同时关闭场景下,双方都会进入 TIME_WAIT,不过日常碰到的绝大多数是前者。)
+
+问题在于,`TIME_WAIT` 会让对应连接在一段时间内不能被随意复用。对客户端高频短连接同一目标来说,可用临时端口会被大量 `TIME_WAIT` 消耗,从而更容易撞到端口上限。
+
+这也是为什么高并发调用**优先建议使用连接池和 HTTP keep-alive**,从源头减少短连接创建。
+
+说到连接复用,很多人分不清 TCP Keepalive 和 HTTP Keep-Alive,其实它们解决的问题完全不同。
+
+简单说:HTTP Keep-Alive 管的是“一条连接最多用多久、服务多少次请求”,TCP Keepalive 管的是“如果长时间没数据,检查一下对方是不是已经消失了”。两者互不干扰,也不能互相替代。详细介绍可以看这篇文章:[TCP Keepalive 和 HTTP Keep-Alive 有什么区别?](./tcp-keepalive-vs-http-keepalive.md)。
+
+至于内核参数,别一看到 `TIME_WAIT` 多就急着改。
+
+`tcp_tw_reuse` 要结合内核版本、业务场景和真实的端口耗尽证据来看,不适合当成万能优化项。`tcp_tw_recycle` 更不用碰了,Linux 4.12 之后已经被移除。
+
+也别想着清理 TIME_WAIT。它不是脏东西,而是 TCP 协议里的正常机制。
+
+看到 `TIME_WAIT` 数量很多,第一反应应该是回到业务链路看问题:是不是一直在创建短连接?连接池有没有生效?HTTP keep-alive 有没有打开?客户端是不是每次请求完都主动断开?
+
+生产环境里很常见的一个坑,就是**连接池没配好,最后把临时端口耗光了**。
+
+比如:
+
+- HTTP 客户端没开 keep-alive,也没用连接池,每次请求都新建连接,请求完就关,`TIME_WAIT` 很快堆起来。
+- 连接池最大连接数、每个目标地址的连接数配置太小,导致连接一直被创建和销毁。
+- DNS 最后解析到单个 IP,请求目标太集中,四元组里主要只剩源端口在变,更容易把端口打满。
+
+排查这类问题,优先修连接复用。确认连接池、keep-alive、超时和关闭策略都没问题之后,再考虑扩大临时端口范围,或者增加源 IP。不要一上来就改内核参数。
+
+
+
+排查时可以用 `ss -ant` 统计各 TCP 状态数量,`ss -ant state time-wait | awk 'NR>1 {print $5}' | sort | uniq -c | sort -nr | head` 查看 TIME_WAIT 集中在哪些目标,`ss -ltn` 查看监听 socket 的 accept queue 堆积情况。看到 TIME_WAIT 集中在某个远端服务,检查短连接和连接池;看到 CLOSE_WAIT 集中在某个本地进程,优先查应用代码有没有正确关闭连接。
+
+## 回到问题
+
+一台主机最多只能保持 65535 个 TCP 连接吗?
+
+答案是:不能这么理解。
+
+`65535` 对应的是端口号范围,不是 TCP 连接数上限。TCP 连接靠四元组区分:源 IP、源端口、目的 IP、目的端口。服务端监听同一个端口时,只要客户端 IP 或客户端端口不同,连接就可以继续增加。
+
+不过,理论上能区分出来,不代表机器一定扛得住。实际连接数通常会被文件描述符、内存、CPU、网卡、应用处理能力、握手队列等资源限制住。客户端如果频繁短连接访问同一个目标,还会碰到临时端口和 `TIME_WAIT` 的压力;如果中间经过 NAT,还要看 NAT 网关能不能撑住。
+
+小 G 这里再压缩成一句话:**服务端连接数主要看机器资源,客户端连同一个目标主要看临时端口,中间有 NAT 时还要看 NAT 网关。`65535` 只是端口号上限,不是所有 TCP 连接的上限。**
diff --git a/docs/cs-basics/network/nat.md b/docs/cs-basics/network/nat.md
index 4567719b81e..adfabc04345 100644
--- a/docs/cs-basics/network/nat.md
+++ b/docs/cs-basics/network/nat.md
@@ -1,10 +1,26 @@
---
title: NAT 协议详解(网络层)
+description: 解析 NAT 的地址转换与端口映射机制,结合 LAN/WAN 通信与转换表,理解家庭与企业网络的实践细节。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: NAT,地址转换,端口映射,LAN,WAN,连接跟踪,DHCP
---
+很多设备在家用网络、公司内网里使用的都是私有 IP 地址,比如 `192.168.x.x`、`10.x.x.x`。这些地址不能直接在公网中路由,但内网设备依然可以访问互联网。
+
+这背后通常就有 NAT 在工作。NAT 会在内网地址和公网地址之间做转换,让多个内网设备共享一个或少量公网 IP 对外通信。
+
+这篇文章主要回答几个问题:
+
+1. NAT 主要解决什么问题?
+2. NAT 转换表是如何记录内外网地址和端口映射的?
+3. 内网主机访问公网时,源 IP 和端口会发生什么变化?
+4. NAT 会带来哪些限制,比如外部主动访问内网主机为什么更麻烦?
+
## 应用场景
**NAT 协议(Network Address Translation)** 的应用场景如同它的名称——网络地址转换,应用于内部网到外部网的地址转换过程中。具体地说,在一个小的子网(局域网,Local Area Network,LAN)内,各主机使用的是同一个 LAN 下的 IP 地址,但在该 LAN 以外,在广域网(Wide Area Network,WAN)中,需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。
@@ -15,26 +31,26 @@ SOHO 子网的“代理人”,也就是和外界的窗口,通常由路由器
## 细节
-
+
-假设当前场景如上图。中间是一个路由器,它的右侧组织了一个 LAN,网络号为`10.0.0/24`。LAN 侧接口的 IP 地址为`10.0.0.4`,并且该子网内有至少三台主机,分别是`10.0.0.1`,`10.0.0.2`和`10.0.0.3`。路由器的左侧连接的是 WAN,WAN 侧接口的 IP 地址为`138.76.29.7`。
+假设当前场景如上图。中间是一个路由器,它的右侧组织了一个 LAN,网络号为 `10.0.0/24`。LAN 侧接口的 IP 地址为 `10.0.0.4`,并且该子网内有至少三台主机,分别是 `10.0.0.1`、`10.0.0.2` 和 `10.0.0.3`。路由器的左侧连接的是 WAN,WAN 侧接口的 IP 地址为 `138.76.29.7`。
首先,针对以上信息,我们有如下事实需要说明:
-1. 路由器的右侧子网的网络号为`10.0.0/24`,主机号为`10.0.0/8`,三台主机地址,以及路由器的 LAN 侧接口地址,均由 DHCP 协议规定。而且,该 DHCP 运行在路由器内部(路由器自维护一个小 DHCP 服务器),从而为子网内提供 DHCP 服务。
+1. 路由器右侧子网的网络地址为 `10.0.0.0/24`(网络前缀 24 位,主机号占 8 位),三台主机地址以及路由器的 LAN 侧接口地址,均由 DHCP 协议规定。而且,该 DHCP 运行在路由器内部(路由器自维护一个小 DHCP 服务器),从而为子网内提供 DHCP 服务。
2. 路由器的 WAN 侧接口地址同样由 DHCP 协议规定,但该地址是路由器从 ISP(网络服务提供商)处获得,也就是该 DHCP 通常运行在路由器所在区域的 DHCP 服务器上。
现在,路由器内部还运行着 NAT 协议,从而为 LAN-WAN 间通信提供地址转换服务。为此,一个很重要的结构是 **NAT 转换表**。为了说明 NAT 的运行细节,假设有以下请求发生:
-1. 主机`10.0.0.1`向 IP 地址为`128.119.40.186`的 Web 服务器(端口 80)发送了 HTTP 请求(如请求页面)。此时,主机`10.0.0.1`将随机指派一个端口,如`3345`,作为本次请求的源端口号,将该请求发送到路由器中(目的地址将是`128.119.40.186`,但会先到达`10.0.0.4`)。
-2. `10.0.0.4`即路由器的 LAN 接口收到`10.0.0.1`的请求。路由器将为该请求指派一个新的源端口号,如`5001`,并将请求报文发送给 WAN 接口`138.76.29.7`。同时,在 NAT 转换表中记录一条转换记录**138.76.29.7:5001——10.0.0.1:3345**。
-3. 请求报文到达 WAN 接口,继续向目的主机`128.119.40.186`发送。
+1. 主机 `10.0.0.1` 向 IP 地址为 `128.119.40.186` 的 Web 服务器(端口 80)发送了 HTTP 请求(如请求页面)。此时,主机 `10.0.0.1` 将随机指派一个端口,如 `3345`,作为本次请求的源端口号,将该请求发送到路由器中(目的地址将是 `128.119.40.186`,但会先到达 `10.0.0.4`)。
+2. `10.0.0.4` 即路由器的 LAN 接口收到 `10.0.0.1` 的请求。路由器将为该请求指派一个新的源端口号,如 `5001`,并将请求报文发送给 WAN 接口 `138.76.29.7`。同时,在 NAT 转换表中记录一条转换记录 **138.76.29.7:5001——10.0.0.1:3345**。
+3. 请求报文到达 WAN 接口,继续向目的主机 `128.119.40.186` 发送。
之后,将会有如下响应发生:
-1. 主机`128.119.40.186`收到请求,构造响应报文,并将其发送给目的地`138.76.29.7:5001`。
-2. 响应报文到达路由器的 WAN 接口。路由器查询 NAT 转换表,发现`138.76.29.7:5001`在转换表中有记录,从而将其目的地址和目的端口转换成为`10.0.0.1:3345`,再发送到`10.0.0.4`上。
-3. 被转换的响应报文到达路由器的 LAN 接口,继而被转发至目的地`10.0.0.1`。
+1. 主机 `128.119.40.186` 收到请求,构造响应报文,并将其发送给目的地 `138.76.29.7:5001`。
+2. 响应报文到达路由器的 WAN 接口。路由器查询 NAT 转换表,发现 `138.76.29.7:5001` 在转换表中有记录,从而将其目的地址和目的端口转换成为 `10.0.0.1:3345`,再发送到 `10.0.0.4` 上。
+3. 被转换的响应报文到达路由器的 LAN 接口,继而被转发至目的地 `10.0.0.1`。

@@ -45,7 +61,7 @@ SOHO 子网的“代理人”,也就是和外界的窗口,通常由路由器
针对以上过程,有以下几个重点需要强调:
1. 当请求报文到达路由器,并被指定了新端口号时,由于端口号有 16 位,因此,通常来说,一个路由器管理的 LAN 中的最大主机数 $≈65500$($2^{16}$ 的地址空间),但通常 SOHO 子网内不会有如此多的主机数量。
-2. 对于目的服务器来说,从来不知道“到底是哪个主机给我发送的请求”,它只知道是来自`138.76.29.7:5001`的路由器转发的请求。因此,可以说,**路由器在 WAN 和 LAN 之间起到了屏蔽作用**,所有内部主机发送到外部的报文,都具有同一个 IP 地址(不同的端口号),所有外部发送到内部的报文,也都只有一个目的地(不同端口号),是经过了 NAT 转换后,外部报文才得以正确地送达内部主机。
+2. 对于目的服务器来说,从来不知道“到底是哪个主机给我发送的请求”,它只知道是来自 `138.76.29.7:5001` 的路由器转发的请求。因此,可以说,**路由器在 WAN 和 LAN 之间起到了屏蔽作用**,所有内部主机发送到外部的报文,都具有同一个 IP 地址(不同的端口号),所有外部发送到内部的报文,也都只有一个目的地(不同端口号),是经过了 NAT 转换后,外部报文才得以正确地送达内部主机。
3. 在报文穿过路由器,发生 NAT 转换时,如果 LAN 主机 IP 已经在 NAT 转换表中注册过了,则不需要路由器新指派端口,而是直接按照转换记录穿过路由器。同理,外部报文发送至内部时也如此。
总结 NAT 协议的特点,有以下几点:
diff --git a/docs/cs-basics/network/network-attack-means.md b/docs/cs-basics/network/network-attack-means.md
index 748999d6eba..f850e36dbb3 100644
--- a/docs/cs-basics/network/network-attack-means.md
+++ b/docs/cs-basics/network/network-attack-means.md
@@ -1,69 +1,83 @@
---
-title: 网络攻击常见手段总结
+title: 网络攻击常见手段总结(安全)
+description: 总结常见 TCP/IP 攻击与防护思路,覆盖 DDoS、IP/ARP 欺骗、中间人等手段,强调工程防护实践。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: 网络攻击,DDoS,IP 欺骗,ARP 欺骗,中间人攻击,扫描,防护
---
> 本文整理完善自[TCP/IP 常见攻击手段 - 暖蓝笔记 - 2021](https://mp.weixin.qq.com/s/AZwWrOlLxRSSi-ywBgZ0fA)这篇文章。
-这篇文章的内容主要是介绍 TCP/IP 常见攻击手段,尤其是 DDoS 攻击,也会补充一些其他的常见网络攻击手段。
+TCP/IP 协议栈追求互联互通,但很多机制在设计之初并没有把今天的攻击规模和对抗强度都考虑进去。
+
+IP 欺骗、SYN Flood、DDoS、ARP 欺骗、DNS 劫持这些攻击,表面上各不相同,本质上都在利用网络协议里的信任假设、资源消耗点或解析链路。
+
+这篇文章主要回答几个问题:
+
+1. TCP/IP 常见攻击手段分别利用了什么机制?
+2. IP 欺骗、SYN Flood、DDoS 等攻击大致是怎么发生的?
+3. 常见网络攻击会造成哪些影响?
+4. 面对这些攻击,通常有哪些基础防御思路?
## IP 欺骗
-### IP 是什么?
+### IP 是什么?
在网络中,所有的设备都会分配一个地址。这个地址就仿佛小蓝的家地址「**多少号多少室**」,这个号就是分配给整个子网的,「**室**」对应的号码即分配给子网中计算机的,这就是网络中的地址。「号」对应的号码为网络号,「**室**」对应的号码为主机号,这个地址的整体就是 **IP 地址**。
### 通过 IP 地址我们能知道什么?
-通过 IP 地址,我们就可以知道判断访问对象服务器的位置,从而将消息发送到服务器。一般发送者发出的消息首先经过子网的集线器,转发到最近的路由器,然后根据路由位置访问下一个路由器的位置,直到终点
+通过 IP 地址,我们就可以判断访问对象服务器的位置,从而将消息发送到服务器。一般发送者发出的消息首先经过子网的集线器,转发到最近的路由器,然后根据路由位置访问下一个路由器的位置,直到终点。
-**IP 头部格式** :
+**IP 头部格式**:
-
+
### IP 欺骗技术是什么?
骗呗,拐骗,诱骗!
-IP 欺骗技术就是**伪造**某台主机的 IP 地址的技术。通过 IP 地址的伪装使得某台主机能够**伪装**另外的一台主机,而这台主机往往具有某种特权或者被另外的主机所信任。
+IP 欺骗技术就是伪造某台主机的 IP 地址的技术。通过 IP 地址的伪装使得某台主机能够伪装另外的一台主机,而这台主机往往具有某种特权或者被另外的主机所信任。
假设现在有一个合法用户 **(1.1.1.1)** 已经同服务器建立正常的连接,攻击者构造攻击的 TCP 数据,伪装自己的 IP 为 **1.1.1.1**,并向服务器发送一个带有 RST 位的 TCP 数据段。服务器接收到这样的数据后,认为从 **1.1.1.1** 发送的连接有错误,就会清空缓冲区中建立好的连接。
这时,如果合法用户 **1.1.1.1** 再发送合法数据,服务器就已经没有这样的连接了,该用户就必须从新开始建立连接。攻击时,伪造大量的 IP 地址,向目标发送 RST 数据,使服务器不对合法用户服务。虽然 IP 地址欺骗攻击有着相当难度,但我们应该清醒地意识到,这种攻击非常广泛,入侵往往从这种攻击开始。
-
+
### 如何缓解 IP 欺骗?
虽然无法预防 IP 欺骗,但可以采取措施来阻止伪造数据包渗透网络。**入口过滤** 是防范欺骗的一种极为常见的防御措施,如 BCP38(通用最佳实践文档)所示。入口过滤是一种数据包过滤形式,通常在[网络边缘](https://www.cloudflare.com/learning/serverless/glossary/what-is-edge-computing/)设备上实施,用于检查传入的 IP 数据包并确定其源标头。如果这些数据包的源标头与其来源不匹配或者看上去很可疑,则拒绝这些数据包。一些网络还实施出口过滤,检查退出网络的 IP 数据包,确保这些数据包具有合法源标头,以防止网络内部用户使用 IP 欺骗技术发起出站恶意攻击。
-## SYN Flood(洪水)
+## SYN Flood(洪水)
### SYN Flood 是什么?
-SYN Flood 是互联网上最原始、最经典的 DDoS(Distributed Denial of Service,分布式拒绝服务)攻击之一,旨在耗尽可用服务器资源,致使服务器无法传输合法流量
+SYN Flood 是互联网上最原始、最经典的 DDoS(Distributed Denial of Service,分布式拒绝服务)攻击之一,旨在耗尽可用服务器资源,致使服务器无法传输合法流量。
SYN Flood 利用了 TCP 协议的三次握手机制,攻击者通常利用工具或者控制僵尸主机向服务器发送海量的变源 IP 地址或变源端口的 TCP SYN 报文,服务器响应了这些报文后就会生成大量的半连接,当系统资源被耗尽后,服务器将无法提供正常的服务。
-增加服务器性能,提供更多的连接能力对于 SYN Flood 的海量报文来说杯水车薪,防御 SYN Flood 的关键在于判断哪些连接请求来自于真实源,屏蔽非真实源的请求以保障正常的业务请求能得到服务。
+增加服务器性能、提供更多的连接能力对于 SYN Flood 的海量报文来说杯水车薪。防御 SYN Flood 的关键在于判断哪些连接请求来自于真实源,屏蔽非真实源的请求以保障正常的业务请求能得到服务。
-
+
### TCP SYN Flood 攻击原理是什么?
**TCP SYN Flood** 攻击利用的是 **TCP** 的三次握手(**SYN -> SYN/ACK -> ACK**),假设连接发起方是 A,连接接受方是 B,即 B 在某个端口(**Port**)上监听 A 发出的连接请求,过程如下图所示,左边是 A,右边是 B。
-
+
-A 首先发送 **SYN**(Synchronization)消息给 B,要求 B 做好接收数据的准备;B 收到后反馈 **SYN-ACK**(Synchronization-Acknowledgement) 消息给 A,这个消息的目的有两个:
+A 首先发送 **SYN**(Synchronization)消息给 B,要求 B 做好接收数据的准备;B 收到后反馈 **SYN-ACK**(Synchronization-Acknowledgement)消息给 A,这个消息的目的有两个:
- 向 A 确认已做好接收数据的准备,
-- 同时要求 A 也做好接收数据的准备,此时 B 已向 A 确认好接收状态,并等待 A 的确认,连接处于**半开状态(Half-Open)**,顾名思义只开了一半;A 收到后再次发送 **ACK** (Acknowledgement) 消息给 B,向 B 确认也做好了接收数据的准备,至此三次握手完成,「**连接**」就建立了,
+- 同时要求 A 也做好接收数据的准备,此时 B 已向 A 确认好接收状态,并等待 A 的确认,连接处于**半开状态(Half-Open)**,顾名思义只开了一半;A 收到后再次发送 **ACK**(Acknowledgement)消息给 B,向 B 确认也做好了接收数据的准备,至此三次握手完成,「**连接**」就建立了,
-大家注意到没有,最关键的一点在于双方是否都按对方的要求进入了**可以接收消息**的状态。而这个状态的确认主要是双方将要使用的**消息序号(**SequenceNum),**TCP** 为保证消息按发送顺序抵达接收方的上层应用,需要用**消息序号**来标记消息的发送先后顺序的。
+大家注意到没有,最关键的一点在于双方是否都按对方的要求进入了**可以接收消息**的状态。而这个状态的确认主要是双方将要使用的**消息序号(**SequenceNum),**TCP** 为保证消息按发送顺序抵达接收方的上层应用,需要用**消息序号**来标记消息的发送先后顺序的。
-**TCP**是「**双工**」(Duplex)连接,同时支持双向通信,也就是双方同时可向对方发送消息,其中 **SYN** 和 **SYN-ACK** 消息开启了 A→B 的单向通信通道(B 获知了 A 的消息序号);**SYN-ACK** 和 **ACK** 消息开启了 B→A 单向通信通道(A 获知了 B 的消息序号)。
+**TCP**是「**双工**」(Duplex)连接,同时支持双向通信,也就是双方同时可向对方发送消息,其中 **SYN** 和 **SYN-ACK** 消息开启了 A→B 的单向通信通道(B 获知了 A 的消息序号);**SYN-ACK** 和 **ACK** 消息开启了 B→A 单向通信通道(A 获知了 B 的消息序号)。
上面讨论的是双方在诚实守信,正常情况下的通信。
@@ -71,16 +85,16 @@ A 首先发送 **SYN**(Synchronization)消息给 B,要求 B 做好接收
假设 B 通过某 **TCP** 端口提供服务,B 在收到 A 的 **SYN** 消息时,积极的反馈了 **SYN-ACK** 消息,使连接进入**半开状态**,因为 B 不确定自己发给 A 的 **SYN-ACK** 消息或 A 反馈的 ACK 消息是否会丢在半路,所以会给每个待完成的半开连接都设一个**Timer**,如果超过时间还没有收到 A 的 **ACK** 消息,则重新发送一次 **SYN-ACK** 消息给 A,直到重试超过一定次数时才会放弃。
-
+
B 为帮助 A 能顺利连接,需要**分配内核资源**维护半开连接,那么当 B 面临海量的连接 A 时,如上图所示,**SYN Flood** 攻击就形成了。攻击方 A 可以控制肉鸡向 B 发送大量 SYN 消息但不响应 ACK 消息,或者干脆伪造 SYN 消息中的 **Source IP**,使 B 反馈的 **SYN-ACK** 消息石沉大海,导致 B 被大量注定不能完成的半开连接占据,直到资源耗尽,停止响应正常的连接请求。
### SYN Flood 的常见形式有哪些?
-**恶意用户可通过三种不同方式发起 SYN Flood 攻击**:
+恶意用户可通过三种不同方式发起 SYN Flood 攻击:
1. **直接攻击:** 不伪造 IP 地址的 SYN 洪水攻击称为直接攻击。在此类攻击中,攻击者完全不屏蔽其 IP 地址。由于攻击者使用具有真实 IP 地址的单一源设备发起攻击,因此很容易发现并清理攻击者。为使目标机器呈现半开状态,黑客将阻止个人机器对服务器的 SYN-ACK 数据包做出响应。为此,通常采用以下两种方式实现:部署防火墙规则,阻止除 SYN 数据包以外的各类传出数据包;或者,对传入的所有 SYN-ACK 数据包进行过滤,防止其到达恶意用户机器。实际上,这种方法很少使用(即便使用过也不多见),因为此类攻击相当容易缓解 – 只需阻止每个恶意系统的 IP 地址。哪怕攻击者使用僵尸网络(如 [Mirai 僵尸网络](https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/)),通常也不会刻意屏蔽受感染设备的 IP。
-2. **欺骗攻击:** 恶意用户还可以伪造其发送的各个 SYN 数据包的 IP 地址,以便阻止缓解措施并加大身份暴露难度。虽然数据包可能经过伪装,但还是可以通过这些数据包追根溯源。此类检测工作很难开展,但并非不可实现;特别是,如果 Internet 服务提供商 (ISP) 愿意提供帮助,则更容易实现。
+2. **欺骗攻击:** 恶意用户还可以伪造其发送的各个 SYN 数据包的 IP 地址,以便阻止缓解措施并加大身份暴露难度。虽然数据包可能经过伪装,但还是可以通过这些数据包追根溯源。此类检测工作很难开展,但并非不可实现;特别是,如果 Internet 服务提供商(ISP)愿意提供帮助,则更容易实现。
3. **分布式攻击(DDoS):** 如果使用僵尸网络发起攻击,则追溯攻击源头的可能性很低。随着混淆级别的攀升,攻击者可能还会命令每台分布式设备伪造其发送数据包的 IP 地址。哪怕攻击者使用僵尸网络(如 Mirai 僵尸网络),通常也不会刻意屏蔽受感染设备的 IP。
### 如何缓解 SYN Flood?
@@ -97,7 +111,7 @@ B 为帮助 A 能顺利连接,需要**分配内核资源**维护半开连接
此策略要求服务器创建 Cookie。为避免在填充积压工作时断开连接,服务器使用 SYN-ACK 数据包响应每一项连接请求,而后从积压工作中删除 SYN 请求,同时从内存中删除请求,保证端口保持打开状态并做好重新建立连接的准备。如果连接是合法请求并且已将最后一个 ACK 数据包从客户端机器发回服务器,服务器将重建(存在一些限制)SYN 积压工作队列条目。虽然这项缓解措施势必会丢失一些 TCP 连接信息,但好过因此导致对合法用户发起拒绝服务攻击。
-## UDP Flood(洪水)
+## UDP Flood(洪水)
### UDP Flood 是什么?
@@ -118,19 +132,19 @@ B 为帮助 A 能顺利连接,需要**分配内核资源**维护半开连接
由于目标服务器利用资源检查并响应每个接收到的 **UDP** 数据包的结果,当接收到大量 **UDP** 数据包时,目标的资源可能会迅速耗尽,导致对正常流量的拒绝服务。
-
+
-### 如何缓解 UDP Flooding?
+### 如何缓解 UDP Flood?
大多数操作系统部分限制了 **ICMP** 报文的响应速率,以中断需要 ICMP 响应的 **DDoS** 攻击。这种缓解的一个缺点是在攻击过程中,合法的数据包也可能被过滤。如果 **UDP Flood** 的容量足够高以使目标服务器的防火墙的状态表饱和,则在服务器级别发生的任何缓解都将不足以应对目标设备上游的瓶颈。
-## HTTP Flood(洪水)
+## HTTP Flood(洪水)
### HTTP Flood 是什么?
HTTP Flood 是一种大规模的 DDoS(Distributed Denial of Service,分布式拒绝服务)攻击,旨在利用 HTTP 请求使目标服务器不堪重负。目标因请求而达到饱和,且无法响应正常流量后,将出现拒绝服务,拒绝来自实际用户的其他请求。
-
+
### HTTP Flood 的攻击原理是什么?
@@ -147,29 +161,29 @@ HTTP 洪水攻击有两种:
如前所述,缓解第 7 层攻击非常复杂,而且通常要从多方面进行。一种方法是对发出请求的设备实施质询,以测试它是否是机器人,这与在线创建帐户时常用的 CAPTCHA 测试非常相似。通过提出 JavaScript 计算挑战之类的要求,可以缓解许多攻击。
-其他阻止 HTTP 洪水攻击的途径包括使用 Web 应用程序防火墙 (WAF)、管理 IP 信誉数据库以跟踪和有选择地阻止恶意流量,以及由工程师进行动态分析。Cloudflare 具有超过 2000 万个互联网设备的规模优势,能够分析来自各种来源的流量并通过快速更新的 WAF 规则和其他防护策略来缓解潜在的攻击,从而消除应用程序层 DDoS 流量。
+其他阻止 HTTP 洪水攻击的途径包括使用 Web 应用程序防火墙(WAF)、管理 IP 信誉数据库以跟踪和有选择地阻止恶意流量,以及由工程师进行动态分析。Cloudflare 具有超过 2000 万个互联网设备的规模优势,能够分析来自各种来源的流量并通过快速更新的 WAF 规则和其他防护策略来缓解潜在的攻击,从而消除应用程序层 DDoS 流量。
-## DNS Flood(洪水)
+## DNS Flood(洪水)
### DNS Flood 是什么?
-域名系统(DNS)服务器是互联网的“电话簿“;互联网设备通过这些服务器来查找特定 Web 服务器以便访问互联网内容。DNS Flood 攻击是一种分布式拒绝服务(DDoS)攻击,攻击者用大量流量淹没某个域的 DNS 服务器,以尝试中断该域的 DNS 解析。如果用户无法找到电话簿,就无法查找到用于调用特定资源的地址。通过中断 DNS 解析,DNS Flood 攻击将破坏网站、API 或 Web 应用程序响应合法流量的能力。很难将 DNS Flood 攻击与正常的大流量区分开来,因为这些大规模流量往往来自多个唯一地址,查询该域的真实记录,模仿合法流量。
+域名系统(DNS)服务器是互联网的“电话簿”;互联网设备通过这些服务器来查找特定 Web 服务器以便访问互联网内容。DNS Flood 攻击是一种分布式拒绝服务(DDoS)攻击,攻击者用大量流量淹没某个域的 DNS 服务器,以尝试中断该域的 DNS 解析。如果用户无法找到电话簿,就无法查找到用于调用特定资源的地址。通过中断 DNS 解析,DNS Flood 攻击将破坏网站、API 或 Web 应用程序响应合法流量的能力。很难将 DNS Flood 攻击与正常的大流量区分开来,因为这些大规模流量往往来自多个唯一地址,查询该域的真实记录,模仿合法流量。
### DNS Flood 的攻击原理是什么?
-
+
域名系统的功能是将易于记忆的名称(例如 example.com)转换成难以记住的网站服务器地址(例如 192.168.0.1),因此成功攻击 DNS 基础设施将导致大多数人无法使用互联网。DNS Flood 攻击是一种相对较新的基于 DNS 的攻击,这种攻击是在高带宽[物联网(IoT)](https://www.cloudflare.com/learning/ddos/glossary/internet-of-things-iot/)[僵尸网络](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-botnet/)(如 [Mirai](https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/))兴起后激增的。DNS Flood 攻击使用 IP 摄像头、DVR 盒和其他 IoT 设备的高带宽连接直接淹没主要提供商的 DNS 服务器。来自 IoT 设备的大量请求淹没 DNS 提供商的服务,阻止合法用户访问提供商的 DNS 服务器。
DNS Flood 攻击不同于 [DNS 放大攻击](https://www.cloudflare.com/zh-cn/learning/ddos/dns-amplification-ddos-attack/)。与 DNS Flood 攻击不同,DNS 放大攻击反射并放大不安全 DNS 服务器的流量,以便隐藏攻击的源头并提高攻击的有效性。DNS 放大攻击使用连接带宽较小的设备向不安全的 DNS 服务器发送无数请求。这些设备对非常大的 DNS 记录发出小型请求,但在发出请求时,攻击者伪造返回地址为目标受害者。这种放大效果让攻击者能借助有限的攻击资源来破坏较大的目标。
-### 如何防护 DNS Flood?
+### 如何防护 DNS Flood?
DNS Flood 对传统上基于放大的攻击方法做出了改变。借助轻易获得的高带宽僵尸网络,攻击者现能针对大型组织发动攻击。除非被破坏的 IoT 设备得以更新或替换,否则抵御这些攻击的唯一方法是使用一个超大型、高度分布式的 DNS 系统,以便实时监测、吸收和阻止攻击流量。
## TCP 重置攻击
-在 **TCP** 重置攻击中,攻击者通过向通信的一方或双方发送伪造的消息,告诉它们立即断开连接,从而使通信双方连接中断。正常情况下,如果客户端收发现到达的报文段对于相关连接而言是不正确的,**TCP** 就会发送一个重置报文段,从而导致 **TCP** 连接的快速拆卸。
+在 **TCP** 重置攻击中,攻击者通过向通信的一方或双方发送伪造的消息,告诉它们立即断开连接,从而使通信双方连接中断。正常情况下,如果客户端发现到达的报文段对于相关连接而言是不正确的,**TCP** 就会发送一个重置报文段,从而导致 **TCP** 连接的快速拆卸。
**TCP** 重置攻击利用这一机制,通过向通信方发送伪造的重置报文段,欺骗通信双方提前关闭 TCP 连接。如果伪造的重置报文段完全逼真,接收者就会认为它有效,并关闭 **TCP** 连接,防止连接被用来进一步交换信息。服务端可以创建一个新的 **TCP** 连接来恢复通信,但仍然可能会被攻击者重置连接。万幸的是,攻击者需要一定的时间来组装和发送伪造的报文,所以一般情况下这种攻击只对长连接有杀伤力,对于短连接而言,你还没攻击呢,人家已经完成了信息交换。
@@ -184,7 +198,7 @@ DNS Flood 对传统上基于放大的攻击方法做出了改变。借助轻易
- 嗅探通信双方的交换信息。
- 截获一个 `ACK` 标志位置位 1 的报文段,并读取其 `ACK` 号。
- 伪造一个 TCP 重置报文段(`RST` 标志位置为 1),其序列号等于上面截获的报文的 `ACK` 号。这只是理想情况下的方案,假设信息交换的速度不是很快。大多数情况下为了增加成功率,可以连续发送序列号不同的重置报文。
-- 将伪造的重置报文发送给通信的一方或双方,时其中断连接。
+- 将伪造的重置报文发送给通信的一方或双方,使其中断连接。
为了实验简单,我们可以使用本地计算机通过 `localhost` 与自己通信,然后对自己进行 TCP 重置攻击。需要以下几个步骤:
@@ -210,26 +224,26 @@ nc 127.0.0.1 8000
该命令会尝试与上面的服务建立连接,在其中一个窗口输入一些字符,就会通过 TCP 连接发送给另一个窗口并打印出来。
-
+
> 嗅探流量
编写一个攻击程序,使用 Python 网络库 `scapy` 来读取两个终端窗口之间交换的数据,并将其打印到终端上。代码比较长,下面为一部份,完整代码后台回复 TCP 攻击,代码的核心是调用 `scapy` 的嗅探方法:
-
+
这段代码告诉 `scapy` 在 `lo0` 网络接口上嗅探数据包,并记录所有 TCP 连接的详细信息。
-- **iface** : 告诉 scapy 在 `lo0`(localhost)网络接口上进行监听。
-- **lfilter** : 这是个过滤器,告诉 scapy 忽略所有不属于指定的 TCP 连接(通信双方皆为 `localhost`,且端口号为 `8000`)的数据包。
-- **prn** : scapy 通过这个函数来操作所有符合 `lfilter` 规则的数据包。上面的例子只是将数据包打印到终端,下文将会修改函数来伪造重置报文。
-- **count** : scapy 函数返回之前需要嗅探的数据包数量。
+- **iface**:告诉 scapy 在 `lo0`(localhost)网络接口上进行监听。
+- **lfilter**:这是个过滤器,告诉 scapy 忽略所有不属于指定的 TCP 连接(通信双方皆为 `localhost`,且端口号为 `8000`)的数据包。
+- **prn**:scapy 通过这个函数来操作所有符合 `lfilter` 规则的数据包。上面的例子只是将数据包打印到终端,下文将会修改函数来伪造重置报文。
+- **count**:scapy 函数返回之前需要嗅探的数据包数量。
> 发送伪造的重置报文
下面开始修改程序,发送伪造的 TCP 重置报文来进行 TCP 重置攻击。根据上面的解读,只需要修改 prn 函数就行了,让其检查数据包,提取必要参数,并利用这些参数来伪造 TCP 重置报文并发送。
-例如,假设该程序截获了一个从(`src_ip`, `src_port`)发往 (`dst_ip`, `dst_port`)的报文段,该报文段的 ACK 标志位已置为 1,ACK 号为 `100,000`。攻击程序接下来要做的是:
+例如,假设该程序截获了一个从(`src_ip`, `src_port`)发往(`dst_ip`, `dst_port`)的报文段,该报文段的 ACK 标志位已置为 1,ACK 号为 `100,000`。攻击程序接下来要做的是:
- 由于伪造的数据包是对截获的数据包的响应,所以伪造数据包的源 `IP/Port` 应该是截获数据包的目的 `IP/Port`,反之亦然。
- 将伪造数据包的 `RST` 标志位置为 1,以表示这是一个重置报文。
@@ -248,11 +262,11 @@ nc 127.0.0.1 8000
猪八戒要向小蓝表白,于是写了一封信给小蓝,结果第三者小黑拦截到了这封信,把这封信进行了篡改,于是乎在他们之间进行搞破坏行动。这个马文才就是中间人,实施的就是中间人攻击。好我们继续聊聊什么是中间人攻击。
-### 什么是中间人?
+### 什么是中间人?
-攻击中间人攻击英文名叫 Man-in-the-MiddleAttack,简称「MITM 攻击」。指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方 直接对话,但事实上整个会话都被攻击者完全控制。我们画一张图:
+中间人攻击英文名叫 Man-in-the-Middle Attack,简称「MITM 攻击」。指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。我们画一张图:
-
+
从这张图可以看到,中间人其实就是攻击者。通过这种原理,有很多实现的用途,比如说,你在手机上浏览不健康网站的时候,手机就会提示你,此网站可能含有病毒,是否继续访问还是做其他的操作等等。
@@ -262,53 +276,53 @@ nc 127.0.0.1 8000
在安全领域有句话:**我们没有办法杜绝网络犯罪,只好想办法提高网络犯罪的成本**。既然没法杜绝这种情况,那我们就想办法提高作案的成本,今天我们就简单了解下基本的网络安全知识,也是面试中的高频面试题了。
-为了避免双方说活不算数的情况,双方引入第三家机构,将合同原文给可信任的第三方机构,只要这个机构不监守自盗,合同就相对安全。
+为了避免双方说话不算数的情况,双方引入第三家机构,将合同原文给可信任的第三方机构,只要这个机构不监守自盗,合同就相对安全。
**如果第三方机构内部不严格或容易出现纰漏?**
-虽然我们将合同原文给第三方机构了,为了防止内部人员的更改,需要采取什么措施呢
+虽然我们将合同原文给第三方机构了,为了防止内部人员的更改,需要采取什么措施呢?
-一种可行的办法是引入 **摘要算法** 。即合同和摘要一起,为了简单的理解摘要。大家可以想象这个摘要为一个函数,这个函数对原文进行了加密,会产生一个唯一的散列值,一旦原文发生一点点变化,那么这个散列值将会变化。
+一种可行的办法是引入 **摘要算法**。即合同和摘要一起,为了简单的理解摘要。大家可以想象这个摘要为一个函数,这个函数对原文进行了加密,会产生一个唯一的散列值,一旦原文发生一点点变化,那么这个散列值将会变化。
#### 有哪些常用的摘要算法呢?
-目前比较常用的加密算法有消息摘要算法和安全散列算法(**SHA**)。**MD5** 是将任意长度的文章转化为一个 128 位的散列值,可是在 2004 年,**MD5** 被证实了容易发生碰撞,即两篇原文产生相同的摘要。这样的话相当于直接给黑客一个后门,轻松伪造摘要。
+目前比较常用的加密算法有消息摘要算法和安全散列算法(**SHA**)。**MD5** 是将任意长度的文章转化为一个 128 位的散列值,可是在 2004 年,**MD5** 被证实了容易发生碰撞,即两篇原文产生相同的摘要。这样的话相当于直接给黑客一个后门,轻松伪造摘要。
-所以在大部分的情况下都会选择 **SHA 算法** 。
+所以在大部分的情况下都会选择 **SHA 算法**。
**出现内鬼了怎么办?**
-看似很安全的场面了,理论上来说杜绝了篡改合同的做法。主要某个员工同时具有修改合同和摘要的权利,那搞事儿就是时间的问题了,毕竟没哪个系统可以完全的杜绝员工接触敏感信息,除非敏感信息都不存在。所以能不能考虑将合同和摘要分开存储呢
+看似很安全的场面了,理论上来说杜绝了篡改合同的做法。主要某个员工同时具有修改合同和摘要的权利,那搞事儿就是时间的问题了,毕竟没哪个系统可以完全的杜绝员工接触敏感信息,除非敏感信息都不存在。所以能不能考虑将合同和摘要分开存储呢?
**那如何确保员工不会修改合同呢?**
-这确实蛮难的,不过办法总比困难多。我们将合同放在双方手中,摘要放在第三方机构,篡改难度进一步加大
+这确实蛮难的,不过办法总比困难多。我们将合同放在双方手中,摘要放在第三方机构,篡改难度进一步加大。
**那么员工万一和某个用户串通好了呢?**
-看来放在第三方的机构还是不好使,同样存在不小风险。所以还需要寻找新的方案,这就出现了 **数字签名和证书**。
+看来放在第三方的机构还是不好使,同样存在不小风险。所以还需要寻找新的方案,这就出现了**数字签名和证书**。
#### 数字证书和签名有什么用?
同样的,举个例子。Sum 和 Mike 两个人签合同。Sum 首先用 **SHA** 算法计算合同的摘要,然后用自己私钥将摘要加密,得到数字签名。Sum 将合同原文、签名,以及公钥三者都交给 Mike
-
+
如果 Sum 想要证明合同是 Mike 的,那么就要使用 Mike 的公钥,将这个签名解密得到摘要 x,然后 Mike 计算原文的 sha 摘要 Y,随后对比 x 和 y,如果两者相等,就认为数据没有被篡改
在这样的过程中,Mike 是不能更改 Sum 的合同,因为要修改合同不仅仅要修改原文还要修改摘要,修改摘要需要提供 Mike 的私钥,私钥即 Sum 独有的密码,公钥即 Sum 公布给他人使用的密码
-总之,公钥加密的数据只能私钥可以解密。私钥加密的数据只有公钥可以解密,这就是 **非对称加密** 。
+总之,公钥加密的数据只能私钥可以解密。私钥加密的数据只有公钥可以解密,这就是 **非对称加密**。
隐私保护?不是吓唬大家,信息是透明的兄 die,不过尽量去维护个人的隐私吧,今天学习对称加密和非对称加密。
-大家先读读这个字"钥",是读"yao",我以前也是,其实读"yue"
+大家先读读这个字“钥”,是读"yao",我以前也是,其实读"yue"
#### 什么是对称加密?
-对称加密,顾名思义,加密方与解密方使用同一钥匙(秘钥)。具体一些就是,发送方通过使用相应的加密算法和秘钥,对将要发送的信息进行加密;对于接收方而言,使用解密算法和相同的秘钥解锁信息,从而有能力阅读信息。
+对称加密,顾名思义,加密方与解密方使用同一钥匙(秘钥)。具体一些就是,发送方通过使用相应的加密算法和秘钥,对将要发送的信息进行加密;对于接收方而言,使用解密算法和相同的秘钥解锁信息,从而有能力阅读信息。
-
+
#### 常见的对称加密算法有哪些?
@@ -316,42 +330,41 @@ nc 127.0.0.1 8000
DES 使用的密钥表面上是 64 位的,然而只有其中的 56 位被实际用于算法,其余 8 位可以被用于奇偶校验,并在算法中被丢弃。因此,**DES** 的有效密钥长度为 56 位,通常称 **DES** 的密钥长度为 56 位。假设秘钥为 56 位,采用暴力破 Jie 的方式,其秘钥个数为 2 的 56 次方,那么每纳秒执行一次解密所需要的时间差不多 1 年的样子。当然,没人这么干。**DES** 现在已经不是一种安全的加密方法,主要因为它使用的 56 位密钥过短。
-
+
**IDEA**
-国际数据加密算法(International Data Encryption Algorithm)。秘钥长度 128 位,优点没有专利的限制。
+国际数据加密算法(International Data Encryption Algorithm)。秘钥长度 128 位,优点没有专利的限制。
**AES**
-当 DES 被破解以后,没过多久推出了 **AES** 算法,提供了三种长度供选择,128 位、192 位和 256,为了保证性能不受太大的影响,选择 128 即可。
+当 DES 被破解以后,没过多久推出了 **AES** 算法,提供了三种长度供选择,128 位、192 位和 256 位,为了保证性能不受太大的影响,选择 128 即可。
**SM1 和 SM4**
-之前几种都是国外的,我们国内自行研究了国密 **SM1**和 **SM4**。其中 S 都属于国家标准,算法公开。优点就是国家的大力支持和认可
+之前几种都是国外的,我们国内自行研究了国密 **SM1** 和 **SM4**。其中 S 都属于国家标准,算法公开。优点就是国家的大力支持和认可。
**总结**:
-
+
#### 常见的非对称加密算法有哪些?
在对称加密中,发送方与接收方使用相同的秘钥。那么在非对称加密中则是发送方与接收方使用的不同的秘钥。其主要解决的问题是防止在秘钥协商的过程中发生泄漏。比如在对称加密中,小蓝将需要发送的消息加密,然后告诉你密码是 123balala,ok,对于其他人而言,很容易就能劫持到密码是 123balala。那么在非对称的情况下,小蓝告诉所有人密码是 123balala,对于中间人而言,拿到也没用,因为没有私钥。所以,非对称密钥其实主要解决了密钥分发的难题。如下图
-
+
-其实我们经常都在使用非对称加密,比如使用多台服务器搭建大数据平台 hadoop,为了方便多台机器设置免密登录,是不是就会涉及到秘钥分发。再比如搭建 docker 集群也会使用相关非对称加密算法。
+其实我们经常都在使用非对称加密,比如使用多台服务器搭建大数据平台 Hadoop,为了方便多台机器设置免密登录,是不是就会涉及到秘钥分发。再比如搭建 Docker 集群也会使用相关非对称加密算法。
常见的非对称加密算法:
-- RSA(RSA 加密算法,RSA Algorithm):优势是性能比较快,如果想要较高的加密难度,需要很长的秘钥。
-
-- ECC:基于椭圆曲线提出。是目前加密强度最高的非对称加密算法
-- SM2:同样基于椭圆曲线问题设计。最大优势就是国家认可和大力支持。
+- RSA(RSA 加密算法,RSA Algorithm):安全性基于大整数分解的计算难度,应用广泛,兼容性好。缺点是性能相对较慢,且密钥越长(如 2048/4096 位)安全性越高,但运算开销也随之增大。
+- ECC:基于椭圆曲线提出,是目前加密强度最高的非对称加密算法。
+- SM2:同样基于椭圆曲线问题设计,最大优势就是国家认可和大力支持。
总结:
-
+
#### 常见的散列算法有哪些?
@@ -367,31 +380,31 @@ MD5 可以用来生成一个 128 位的消息摘要,它是目前应用比较
**SM3**
-国密算法**SM3**。加密强度和 SHA-256 算法 相差不多。主要是受到了国家的支持。
+国密算法 **SM3**。加密强度和 SHA-256 算法相差不多。主要是受到了国家的支持。
**总结**:
-
+
-**大部分情况下使用对称加密,具有比较不错的安全性。如果需要分布式进行秘钥分发,考虑非对称。如果不需要可逆计算则散列算法。** 因为这段时间有这方面需求,就看了一些这方面的资料,入坑信息安全,就怕以后洗发水都不用买。谢谢大家查看!
+**大部分情况下使用对称加密,具有比较不错的安全性。如果需要分布式进行秘钥分发,考虑非对称。如果不需要可逆计算则使用散列算法。**
#### 第三方机构和证书机制有什么用?
-问题还有,此时如果 Sum 否认给过 Mike 的公钥和合同,不久 gg 了
+问题还有,此时如果 Sum 否认给过 Mike 的公钥和合同,不久就麻烦了。
-所以需要 Sum 过的话做过的事儿需要足够的信誉,这就引入了 **第三方机构和证书机制** 。
+所以需要 Sum 过的话做过的事儿需要足够的信誉,这就引入了**第三方机构和证书机制**。
-证书之所以会有信用,是因为证书的签发方拥有信用。所以如果 Sum 想让 Mike 承认自己的公钥,Sum 不会直接将公钥给 Mike ,而是提供由第三方机构,含有公钥的证书。如果 Mike 也信任这个机构,法律都认可,那 ik,信任关系成立
+证书之所以会有信用,是因为证书的签发方拥有信用。所以如果 Sum 想让 Mike 承认自己的公钥,Sum 不会直接将公钥给 Mike,而是提供由第三方机构签发的含有公钥的证书。如果 Mike 也信任这个机构,法律都认可,那信任关系成立。
-
+
如上图所示,Sum 将自己的申请提交给机构,产生证书的原文。机构用自己的私钥签名 Sum 的申请原文(先根据原文内容计算摘要,再用私钥加密),得到带有签名信息的证书。Mike 拿到带签名信息的证书,通过第三方机构的公钥进行解密,获得 Sum 证书的摘要、证书的原文。有了 Sum 证书的摘要和原文,Mike 就可以进行验签。验签通过,Mike 就可以确认 Sum 的证书的确是第三方机构签发的。
-用上面这样一个机制,合同的双方都无法否认合同。这个解决方案的核心在于需要第三方信用服务机构提供信用背书。这里产生了一个最基础的信任链,如果第三方机构的信任崩溃,比如被黑客攻破,那整条信任链条也就断裂了
+用上面这样一个机制,合同的双方都无法否认合同。这个解决方案的核心在于需要第三方信用服务机构提供信用背书。这里产生了一个最基础的信任链,如果第三方机构的信任崩溃,比如被黑客攻破,那整条信任链条也就断裂了。
-为了让这个信任条更加稳固,就需要环环相扣,打造更长的信任链,避免单点信任风险
+为了让这个信任条更加稳固,就需要环环相扣,打造更长的信任链,避免单点信任风险。
-
+
上图中,由信誉最好的根证书机构提供根证书,然后根证书机构去签发二级机构的证书;二级机构去签发三级机构的证书;最后有由三级机构去签发 Sum 证书。
@@ -399,15 +412,15 @@ MD5 可以用来生成一个 128 位的消息摘要,它是目前应用比较
如果要验证三级机构证书的合法性,就需要用二级机构的证书去解密三级机构证书的数字签名。
-如果要验证二级结构证书的合法性,就需要用根证书去解密。
+如果要验证二级机构证书的合法性,就需要用根证书去解密。
以上,就构成了一个相对长一些的信任链。如果其中一方想要作弊是非常困难的,除非链条中的所有机构同时联合起来,进行欺诈。
-### 中间人攻击如何避免?
+### 中间人攻击如何避免?
既然知道了中间人攻击的原理也知道了他的危险,现在我们看看如何避免。相信我们都遇到过下面这种状况:
-
+
出现这个界面的很多情况下,都是遇到了中间人攻击的现象,需要对安全证书进行及时地监测。而且大名鼎鼎的 github 网站,也曾遭遇过中间人攻击:
@@ -416,9 +429,9 @@ MD5 可以用来生成一个 128 位的消息摘要,它是目前应用比较
- 客户端不要轻易相信证书:因为这些证书极有可能是中间人。
- App 可以提前预埋证书在本地:意思是我们本地提前有一些证书,这样其他证书就不能再起作用了。
-## DDOS
+## DDoS
-通过上面的描述,总之即好多种攻击都是 **DDOS** 攻击,所以简单总结下这个攻击相关内容。
+通过上面的描述,前面好多种攻击都属于 DDoS 攻击,所以简单总结一下这个攻击的相关内容。
其实,像全球互联网各大公司,均遭受过大量的 **DDoS**。
@@ -442,7 +455,7 @@ DDos 全名 Distributed Denial of Service,翻译成中文就是**分布式拒
还是拿开的重庆火锅店举例,高防服务器就是我给重庆火锅店增加了两名保安,这两名保安可以让保护店铺不受流氓骚扰,并且还会定期在店铺周围巡逻防止流氓骚扰。
-高防服务器主要是指能独立硬防御 50Gbps 以上的服务器,能够帮助网站拒绝服务攻击,定期扫描网络主节点等,这东西是不错,就是贵~
+高防服务器主要是指能独立硬防御 50Gbps 以上的服务器,能够帮助网站拒绝服务攻击,定期扫描网络主节点等。
#### 黑名单
diff --git a/docs/cs-basics/network/osi-and-tcp-ip-model.md b/docs/cs-basics/network/osi-and-tcp-ip-model.md
index 34092a336b6..76863029821 100644
--- a/docs/cs-basics/network/osi-and-tcp-ip-model.md
+++ b/docs/cs-basics/network/osi-and-tcp-ip-model.md
@@ -1,15 +1,31 @@
---
-title: OSI 和 TCP/IP 网络分层模型详解(基础)
+title: OSI 七层模型与 TCP/IP 四层模型详解
+description: 详解 OSI 与 TCP/IP 的分层模型与职责划分,结合历史与实践对比两者差异与工程取舍。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: OSI 七层,TCP/IP 四层,分层模型,职责划分,协议栈,对比
---
+网络分层是学习计算机网络的第一张地图。没有这张地图,HTTP、TCP、IP、以太网、DNS 这些概念很容易堆在一起,分不清谁依赖谁、谁负责什么。
+
+常见的两套分层模型是 OSI 七层模型和 TCP/IP 四层模型。前者更适合建立概念框架,后者更贴近互联网实际落地。
+
+这篇文章主要回答几个问题:
+
+1. OSI 七层模型每一层分别做什么?
+2. TCP/IP 四层模型和 OSI 七层模型如何对应?
+3. 为什么 OSI 模型理论完整,但实际没有成为互联网主流实现?
+4. 学习具体网络协议时,为什么要先知道它位于哪一层?
+
## OSI 七层模型
**OSI 七层模型** 是国际标准化组织提出一个网络分层模型,其大体结构以及每一层提供的功能如下图所示:
-
+
每一层都专注做一件事情,并且每一层都需要使用下一层提供的功能比如传输层需要使用网络层提供的路由和寻址功能,这样传输层才知道把数据传输到哪里去。
@@ -19,7 +35,7 @@ tag:

-**既然 OSI 七层模型这么厉害,为什么干不过 TCP/IP 四 层模型呢?**
+**既然 OSI 七层模型这么厉害,为什么干不过 TCP/IP 四层模型呢?**
的确,OSI 七层模型当时一直被一些大公司甚至一些国家政府支持。这样的背景下,为什么会失败呢?我觉得主要有下面几方面原因:
@@ -32,11 +48,11 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
最后再分享一个关于 OSI 七层模型非常不错的总结图片!
-
+
## TCP/IP 四层模型
-**TCP/IP 四层模型** 是目前被广泛采用的一种模型,我们可以将 TCP / IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成:
+**TCP/IP 四层模型** 是目前被广泛采用的一种模型,我们可以将 TCP/IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成:
1. 应用层
2. 传输层
@@ -45,13 +61,13 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
需要注意的是,我们并不能将 TCP/IP 四层模型 和 OSI 七层模型完全精确地匹配起来,不过可以简单将两者对应起来,如下图所示:
-
+
### 应用层(Application layer)
**应用层位于传输层之上,主要提供两个终端设备上的应用程序之间信息交换的服务,它定义了信息交换的格式,消息会交给下一层传输层来传输。** 我们把应用层交互的数据单元称为报文。
-
+
应用层协议定义了网络通信规则,对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如支持 Web 应用的 HTTP 协议,支持电子邮件的 SMTP 协议等等。
@@ -62,11 +78,11 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
- **HTTP(Hypertext Transfer Protocol,超文本传输协议)**:基于 TCP 协议,是一种用于传输超文本和多媒体内容的协议,主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。当我们使用浏览器浏览网页的时候,我们网页就是通过 HTTP 请求进行加载的。
- **SMTP(Simple Mail Transfer Protocol,简单邮件发送协议)**:基于 TCP 协议,是一种用于发送电子邮件的协议。注意 ⚠️:SMTP 协议只负责邮件的发送,而不是接收。要从邮件服务器接收邮件,需要使用 POP3 或 IMAP 协议。
- **POP3/IMAP(邮件接收协议)**:基于 TCP 协议,两者都是负责邮件接收的协议。IMAP 协议是比 POP3 更新的协议,它在功能和性能上都更加强大。IMAP 支持邮件搜索、标记、分类、归档等高级功能,而且可以在多个设备之间同步邮件状态。几乎所有现代电子邮件客户端和服务器都支持 IMAP。
-- **FTP(File Transfer Protocol,文件传输协议)** : 基于 TCP 协议,是一种用于在计算机之间传输文件的协议,可以屏蔽操作系统和文件存储方式。注意 ⚠️:FTP 是一种不安全的协议,因为它在传输过程中不会对数据进行加密。建议在传输敏感数据时使用更安全的协议,如 SFTP。
+- **FTP(File Transfer Protocol,文件传输协议)**:基于 TCP 协议,是一种用于在计算机之间传输文件的协议,可以屏蔽操作系统和文件存储方式。注意 ⚠️:FTP 是一种不安全的协议,因为它在传输过程中不会对数据进行加密。建议在传输敏感数据时使用更安全的协议,如 SFTP。
- **Telnet(远程登陆协议)**:基于 TCP 协议,用于通过一个终端登陆到其他服务器。Telnet 协议的最大缺点之一是所有数据(包括用户名和密码)均以明文形式发送,这有潜在的安全风险。这就是为什么如今很少使用 Telnet,而是使用一种称为 SSH 的非常安全的网络传输协议的主要原因。
- **SSH(Secure Shell Protocol,安全的网络传输协议)**:基于 TCP 协议,通过加密和认证机制实现安全的访问和文件传输等业务
- **RTP(Real-time Transport Protocol,实时传输协议)**:通常基于 UDP 协议,但也支持 TCP 协议。它提供了端到端的实时传输数据的功能,但不包含资源预留存、不保证实时传输质量,这些功能由 WebRTC 实现。
-- **DNS(Domain Name System,域名管理系统)**: 基于 UDP 协议,用于解决域名和 IP 地址的映射问题。
+- **DNS(Domain Name System,域名管理系统)**:通常基于 UDP 协议(端口 53),用于解决域名和 IP 地址的映射问题。当响应数据过大或进行区域传送时会改用 TCP。
关于这些协议的详细介绍请看 [应用层常见协议总结(应用层)](./application-layer-protocol.md) 这篇文章。
@@ -78,7 +94,7 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础

-- **TCP(Transmission Control Protocol,传输控制协议 )**:提供 **面向连接** 的,**可靠** 的数据传输服务。
+- **TCP(Transmission Control Protocol,传输控制协议)**:提供 **面向连接** 的,**可靠** 的数据传输服务。
- **UDP(User Datagram Protocol,用户数据协议)**:提供 **无连接** 的,**尽最大努力** 的数据传输服务(不保证数据传输的可靠性),简单高效。
### 网络层(Network layer)
@@ -95,21 +111,21 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
**网络层常见协议**:
-
+
- **IP(Internet Protocol,网际协议)**:TCP/IP 协议中最重要的协议之一,主要作用是定义数据包的格式、对数据包进行路由和寻址,以便它们可以跨网络传播并到达正确的目的地。目前 IP 协议主要分为两种,一种是过去的 IPv4,另一种是较新的 IPv6,目前这两种协议都在使用,但后者已经被提议来取代前者。
- **ARP(Address Resolution Protocol,地址解析协议)**:ARP 协议解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但 IP 地址属于逻辑地址,而 MAC 地址才是物理地址,ARP 协议解决了 IP 地址转 MAC 地址的一些问题。
- **ICMP(Internet Control Message Protocol,互联网控制报文协议)**:一种用于传输网络状态和错误消息的协议,常用于网络诊断和故障排除。例如,Ping 工具就使用了 ICMP 协议来测试网络连通性。
- **NAT(Network Address Translation,网络地址转换协议)**:NAT 协议的应用场景如同它的名称——网络地址转换,应用于内部网到外部网的地址转换过程中。具体地说,在一个小的子网(局域网,LAN)内,各主机使用的是同一个 LAN 下的 IP 地址,但在该 LAN 以外,在广域网(WAN)中,需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。
-- **OSPF(Open Shortest Path First,开放式最短路径优先)** ):一种内部网关协议(Interior Gateway Protocol,IGP),也是广泛使用的一种动态路由协议,基于链路状态算法,考虑了链路的带宽、延迟等因素来选择最佳路径。
-- **RIP(Routing Information Protocol,路由信息协议)**:一种内部网关协议(Interior Gateway Protocol,IGP),也是一种动态路由协议,基于距离向量算法,使用固定的跳数作为度量标准,选择跳数最少的路径作为最佳路径。
+- **OSPF(Open Shortest Path First,开放式最短路径优先)**:一种内部网关协议(Interior Gateway Protocol,IGP),也是广泛使用的一种动态路由协议,基于链路状态算法,考虑了链路的带宽、延迟等因素来选择最佳路径。
+- **RIP(Routing Information Protocol,路由信息协议)**:一种内部网关协议(Interior Gateway Protocol,IGP),也是一种动态路由协议,基于距离向量算法,使用固定的跳数作为度量标准,选择跳数最少的路径作为最佳路径。
- **BGP(Border Gateway Protocol,边界网关协议)**:一种用来在路由选择域之间交换网络层可达性信息(Network Layer Reachability Information,NLRI)的路由选择协议,具有高度的灵活性和可扩展性。
### 网络接口层(Network interface layer)
我们可以把网络接口层看作是数据链路层和物理层的合体。
-1. 数据链路层(data link layer)通常简称为链路层( 两台主机之间的数据传输,总是在一段一段的链路上传送的)。**数据链路层的作用是将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。**
+1. 数据链路层(data link layer)通常简称为链路层(两台主机之间的数据传输,总是在一段一段的链路上传送的)。**数据链路层的作用是将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。**
2. **物理层的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异**
网络接口层重要功能和协议如下图所示:
@@ -122,7 +138,7 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础

-**应用层协议** :
+**应用层协议**:
- HTTP(Hypertext Transfer Protocol,超文本传输协议)
- SMTP(Simple Mail Transfer Protocol,简单邮件发送协议)
@@ -134,7 +150,7 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
- DNS(Domain Name System,域名管理系统)
- ……
-**传输层协议** :
+**传输层协议**:
- TCP 协议
- 报文段结构
@@ -145,18 +161,18 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
- 报文段结构
- RDT(可靠数据传输协议)
-**网络层协议** :
+**网络层协议**:
- IP(Internet Protocol,网际协议)
- ARP(Address Resolution Protocol,地址解析协议)
- ICMP 协议(控制报文协议,用于发送控制消息)
- NAT(Network Address Translation,网络地址转换协议)
- OSPF(Open Shortest Path First,开放式最短路径优先)
-- RIP(Routing Information Protocol,路由信息协议)
+- RIP(Routing Information Protocol,路由信息协议)
- BGP(Border Gateway Protocol,边界网关协议)
- ……
-**网络接口层** :
+**网络接口层**:
- 差错检测技术
- 多路访问协议(信道复用技术)
diff --git a/docs/cs-basics/network/other-network-questions.md b/docs/cs-basics/network/other-network-questions.md
index 2f4da42d1a0..b83e95eb9eb 100644
--- a/docs/cs-basics/network/other-network-questions.md
+++ b/docs/cs-basics/network/other-network-questions.md
@@ -1,13 +1,20 @@
---
-title: 计算机网络常见面试题总结(上)
+title: 计算机网络常见面试题总结(上)
+description: 最新计算机网络高频面试题总结(上):TCP/IP四层模型、HTTP全版本对比、TCP三次握手、DNS解析、WebSocket/SSE实时推送等,附图解+⭐️重点标注,一文搞定应用层&传输层&网络层核心考点,快速备战后端面试!
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: 计算机网络面试题,TCP/IP四层模型,HTTP面试,HTTPS vs HTTP,HTTP/1.1 vs HTTP/2,HTTP/3 QUIC,TCP三次握手,UDP区别,DNS解析,WebSocket vs SSE,GET vs POST,应用层协议,网络分层,队头阻塞,PING命令,ARP协议
---
-
+
-上篇主要是计算机网络基础和应用层相关的内容。
+计算机网络是后端面试和校招面试中绕不开的高频考点,尤其是 **TCP/IP 网络分层、HTTP、HTTPS、DNS、WebSocket、TCP 三次握手** 这些问题,几乎贯穿了“从输入 URL 到页面展示”“接口为什么变慢”“连接为什么失败”等真实开发场景。
+
+这篇《计算机网络常见面试题总结(上)》会先从网络分层模型讲起,再梳理应用层和 HTTP 相关的核心知识点,适合用来系统复习计算机网络基础,也适合作为 Java 后端、后端开发、计算机基础面试前的速查清单。
## 计算机网络基础
@@ -17,7 +24,7 @@ tag:
**OSI 七层模型** 是国际标准化组织提出的一个网络分层模型,其大体结构以及每一层提供的功能如下图所示:
-
+
每一层都专注做一件事情,并且每一层都需要使用下一层提供的功能比如传输层需要使用网络层提供的路由和寻址功能,这样传输层才知道把数据传输到哪里去。
@@ -27,9 +34,9 @@ tag:

-#### TCP/IP 四层模型是什么?每一层的作用是什么?
+#### ⭐️ TCP/IP 四层模型是什么?每一层的作用是什么?
-**TCP/IP 四层模型** 是目前被广泛采用的一种模型,我们可以将 TCP / IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成:
+**TCP/IP 四层模型** 是目前被广泛采用的一种模型,我们可以将 TCP/IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成:
1. 应用层
2. 传输层
@@ -38,9 +45,9 @@ tag:
需要注意的是,我们并不能将 TCP/IP 四层模型 和 OSI 七层模型完全精确地匹配起来,不过可以简单将两者对应起来,如下图所示:
-
+
-关于每一层作用的详细介绍,请看 [OSI 和 TCP/IP 网络分层模型详解(基础)](./osi-and-tcp-ip-model.md) 这篇文章。
+关于每一层作用的详细介绍,请看 [OSI 和 TCP/IP 网络分层模型详解(基础)](https://javaguide.cn/cs-basics/network/osi-and-tcp-ip-model.html) 这篇文章。
#### 为什么网络要分层?
@@ -64,18 +71,18 @@ tag:
### 常见网络协议
-#### 应用层有哪些常见的协议?
+#### ⭐️ 应用层有哪些常见的协议?

- **HTTP(Hypertext Transfer Protocol,超文本传输协议)**:基于 TCP 协议,是一种用于传输超文本和多媒体内容的协议,主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。当我们使用浏览器浏览网页的时候,我们网页就是通过 HTTP 请求进行加载的。
- **SMTP(Simple Mail Transfer Protocol,简单邮件发送协议)**:基于 TCP 协议,是一种用于发送电子邮件的协议。注意 ⚠️:SMTP 协议只负责邮件的发送,而不是接收。要从邮件服务器接收邮件,需要使用 POP3 或 IMAP 协议。
- **POP3/IMAP(邮件接收协议)**:基于 TCP 协议,两者都是负责邮件接收的协议。IMAP 协议是比 POP3 更新的协议,它在功能和性能上都更加强大。IMAP 支持邮件搜索、标记、分类、归档等高级功能,而且可以在多个设备之间同步邮件状态。几乎所有现代电子邮件客户端和服务器都支持 IMAP。
-- **FTP(File Transfer Protocol,文件传输协议)** : 基于 TCP 协议,是一种用于在计算机之间传输文件的协议,可以屏蔽操作系统和文件存储方式。注意 ⚠️:FTP 是一种不安全的协议,因为它在传输过程中不会对数据进行加密。建议在传输敏感数据时使用更安全的协议,如 SFTP。
+- **FTP(File Transfer Protocol,文件传输协议)**:基于 TCP 协议,是一种用于在计算机之间传输文件的协议,可以屏蔽操作系统和文件存储方式。注意 ⚠️:FTP 是一种不安全的协议,因为它在传输过程中不会对数据进行加密。建议在传输敏感数据时使用更安全的协议,如 SFTP。
- **Telnet(远程登陆协议)**:基于 TCP 协议,用于通过一个终端登陆到其他服务器。Telnet 协议的最大缺点之一是所有数据(包括用户名和密码)均以明文形式发送,这有潜在的安全风险。这就是为什么如今很少使用 Telnet,而是使用一种称为 SSH 的非常安全的网络传输协议的主要原因。
- **SSH(Secure Shell Protocol,安全的网络传输协议)**:基于 TCP 协议,通过加密和认证机制实现安全的访问和文件传输等业务
- **RTP(Real-time Transport Protocol,实时传输协议)**:通常基于 UDP 协议,但也支持 TCP 协议。它提供了端到端的实时传输数据的功能,但不包含资源预留存、不保证实时传输质量,这些功能由 WebRTC 实现。
-- **DNS(Domain Name System,域名管理系统)**: 基于 UDP 协议,用于解决域名和 IP 地址的映射问题。
+- **DNS(Domain Name System,域名管理系统)**:通常基于 UDP 协议(端口 53),用于解决域名和 IP 地址的映射问题。当响应数据过大或进行区域传送时会改用 TCP。
关于这些协议的详细介绍请看 [应用层常见协议总结(应用层)](./application-layer-protocol.md) 这篇文章。
@@ -83,32 +90,32 @@ tag:

-- **TCP(Transmission Control Protocol,传输控制协议 )**:提供 **面向连接** 的,**可靠** 的数据传输服务。
+- **TCP(Transmission Control Protocol,传输控制协议)**:提供 **面向连接** 的,**可靠** 的数据传输服务。
- **UDP(User Datagram Protocol,用户数据协议)**:提供 **无连接** 的,**尽最大努力** 的数据传输服务(不保证数据传输的可靠性),简单高效。
#### 网络层有哪些常见的协议?
-
+
- **IP(Internet Protocol,网际协议)**:TCP/IP 协议中最重要的协议之一,属于网络层的协议,主要作用是定义数据包的格式、对数据包进行路由和寻址,以便它们可以跨网络传播并到达正确的目的地。目前 IP 协议主要分为两种,一种是过去的 IPv4,另一种是较新的 IPv6,目前这两种协议都在使用,但后者已经被提议来取代前者。
- **ARP(Address Resolution Protocol,地址解析协议)**:ARP 协议解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但 IP 地址属于逻辑地址,而 MAC 地址才是物理地址,ARP 协议解决了 IP 地址转 MAC 地址的一些问题。
- **ICMP(Internet Control Message Protocol,互联网控制报文协议)**:一种用于传输网络状态和错误消息的协议,常用于网络诊断和故障排除。例如,Ping 工具就使用了 ICMP 协议来测试网络连通性。
- **NAT(Network Address Translation,网络地址转换协议)**:NAT 协议的应用场景如同它的名称——网络地址转换,应用于内部网到外部网的地址转换过程中。具体地说,在一个小的子网(局域网,LAN)内,各主机使用的是同一个 LAN 下的 IP 地址,但在该 LAN 以外,在广域网(WAN)中,需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。
- **OSPF(Open Shortest Path First,开放式最短路径优先)**:一种内部网关协议(Interior Gateway Protocol,IGP),也是广泛使用的一种动态路由协议,基于链路状态算法,考虑了链路的带宽、延迟等因素来选择最佳路径。
-- **RIP(Routing Information Protocol,路由信息协议)**:一种内部网关协议(Interior Gateway Protocol,IGP),也是一种动态路由协议,基于距离向量算法,使用固定的跳数作为度量标准,选择跳数最少的路径作为最佳路径。
+- **RIP(Routing Information Protocol,路由信息协议)**:一种内部网关协议(Interior Gateway Protocol,IGP),也是一种动态路由协议,基于距离向量算法,使用固定的跳数作为度量标准,选择跳数最少的路径作为最佳路径。
- **BGP(Border Gateway Protocol,边界网关协议)**:一种用来在路由选择域之间交换网络层可达性信息(Network Layer Reachability Information,NLRI)的路由选择协议,具有高度的灵活性和可扩展性。
## HTTP
-### 从输入 URL 到页面展示到底发生了什么?(非常重要)
+### ⭐️ 从输入 URL 到页面展示到底发生了什么?(非常重要)
> 类似的问题:打开一个网页,整个过程会使用哪些协议?
先来看一张图(来源于《图解 HTTP》):
-
+
-上图有一个错误需要注意:是 OSPF 不是 OPSF。 OSPF(Open Shortest Path First,ospf)开放最短路径优先协议, 是由 Internet 工程任务组开发的路由选择协议
+上图有一个错误需要注意:是 OSPF 不是 OPSF。OSPF(Open Shortest Path First,ospf)开放最短路径优先协议,是由 Internet 工程任务组开发的路由选择协议
总体来说分为以下几个步骤:
@@ -120,15 +127,15 @@ tag:
6. 浏览器收到 HTTP 响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式,同时根据 HTML 中的其他资源的 URL(如图片、CSS、JS 等),再次发起 HTTP 请求,获取这些资源的内容,直到网页完全加载显示。
7. 浏览器在不需要和服务器通信时,可以主动关闭 TCP 连接,或者等待服务器的关闭请求。
-详细介绍可以查看这篇文章:[访问网页的全过程(知识串联)](./the-whole-process-of-accessing-web-pages.md)(强烈推荐)。
+详细介绍可以查看这篇文章:[访问网页的全过程(知识串联)](https://javaguide.cn/cs-basics/network/the-whole-process-of-accessing-web-pages.html)(强烈推荐)。
-### HTTP 状态码有哪些?
+### ⭐️ HTTP 状态码有哪些?
HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被成功处理。

-关于 HTTP 状态码更详细的总结,可以看我写的这篇文章:[HTTP 常见状态码总结(应用层)](./http-status-codes.md)。
+关于 HTTP 状态码更详细的总结,可以看我写的这篇文章:[HTTP 常见状态码总结(应用层)](https://javaguide.cn/cs-basics/network/http-status-codes.html)。
### HTTP Header 中常见的字段有哪些?
@@ -146,10 +153,10 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
| Content-MD5 | 请求体的内容的二进制 MD5 散列值,以 Base64 编码的结果 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
| Content-Type | 请求体的多媒体类型(用于 POST 和 PUT 请求中) | Content-Type: application/x-www-form-urlencoded |
| Cookie | 之前由服务器通过 Set-Cookie(下文详述)发送的一个超文本传输协议 Cookie | Cookie: $Version=1; Skin=new; |
-| Date | 发送该消息的日期和时间(按照 RFC 7231 中定义的"超文本传输协议日期"格式来发送) | Date: Tue, 15 Nov 1994 08:12:31 GMT |
+| Date | 发送该消息的日期和时间(按照 RFC 7231 中定义的“超文本传输协议日期”格式来发送) | Date: Tue, 15 Nov 1994 08:12:31 GMT |
| Expect | 表明客户端要求服务器做出特定的行为 | Expect: 100-continue |
| From | 发起此请求的用户的邮件地址 | From: `user@example.com` |
-| Host | 服务器的域名(用于虚拟主机),以及服务器所监听的传输控制协议端口号。如果所请求的端口是对应的服务的标准端口,则端口号可被省略。 | Host: en.wikipedia.org |
+| Host | 服务器的域名(用于虚拟主机),以及服务器所监听的传输控制协议端口号。如果所请求的端口是对应的服务的标准端口,则端口号可被省略。 | Host: en.wikipedia.org |
| If-Match | 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要作用是用于像 PUT 这样的方法中,仅当从用户上次更新某个资源以来,该资源未被修改的情况下,才更新该资源。 | If-Match: "737060cd8c284d8af7ad3082f209582d" |
| If-Modified-Since | 允许服务器在请求的资源自指定的日期以来未被修改的情况下返回 `304 Not Modified` 状态码 | If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
| If-None-Match | 允许服务器在请求的资源的 ETag 未发生变化的情况下返回 `304 Not Modified` 状态码 | If-None-Match: "737060cd8c284d8af7ad3082f209582d" |
@@ -167,7 +174,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
| Via | 向服务器告知,这个请求是由哪些代理发出的。 | Via: 1.0 fred, 1.1 example.com (Apache/1.1) |
| Warning | 一个一般性的警告,告知,在实体内容体中可能存在错误。 | Warning: 199 Miscellaneous warning |
-### HTTP 和 HTTPS 有什么区别?(重要)
+### ⭐️ HTTP 和 HTTPS 有什么区别?(重要)

@@ -176,33 +183,66 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
- **安全性和资源消耗**:HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
- **SEO(搜索引擎优化)**:搜索引擎通常会更青睐使用 HTTPS 协议的网站,因为 HTTPS 能够提供更高的安全性和用户隐私保护。使用 HTTPS 协议的网站在搜索结果中可能会被优先显示,从而对 SEO 产生影响。
-关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](./http-vs-https.md) 。
+关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](https://javaguide.cn/cs-basics/network/http-vs-https.html)。
+
+### HTTPS 握手里的 RSA 和 ECDHE,到底差在哪?(应用层)
+
+RSA 和 ECDHE 的核心区别在于:**会话密钥材料是“传过去的”,还是“协商出来的”**。
+
+在 TLS 1.2 的静态 RSA 握手里,客户端生成 `PreMasterSecret`,用服务器证书里的 RSA 公钥加密后发给服务端,服务端再用 RSA 私钥解密。问题是,如果攻击者保存了当年的握手流量,后来服务器私钥又泄漏,就可能回头解出历史会话密钥,所以它没有前向安全。
+
+ECDHE 不直接传输共享秘密。客户端和服务端各自生成临时密钥对,交换临时公钥后,双方本地算出同一个共享秘密。服务器证书私钥主要用于签名认证,证明临时参数没被中间人替换,而不是用来解密会话密钥。
+
+所以一句话总结:**RSA 是客户端把秘密加密送过去;ECDHE 是双方用临时密钥协商出秘密。ECDHE 支持前向安全,也因此成为现代 HTTPS 的主流方向。**
+
+详细介绍:[HTTPS 握手里的 RSA 和 ECDHE,到底差在哪?(应用层)](./https-rsa-vs-ecdhe)
+
+### ⭐️ 有了 HTTP,为什么还要 RPC?
+
+HTTP 和 RPC 不是谁取代谁的关系,也不是谁更高级的问题。
+
+HTTP 能调服务,RPC 也能调服务。真正的区别在于,你是想把远程调用当成一次“资源访问”,还是当成一次“方法调用”。
+
+如果是对外接口,比如 Web、App、第三方系统接入,HTTP 通常更合适。它通用、好调试、接入成本低,别人拿 Postman、curl 就能测。
+如果是公司内部服务互调,尤其是服务多、调用链长、接口频繁调用,还要考虑服务发现、超时、重试、负载均衡、链路追踪这些问题,RPC 会更顺手一些。它不是单纯为了快,而是把内部服务调用里的很多麻烦事一起处理掉。
+
+所以,别再简单背“HTTP 对外,RPC 对内”了。
+
+这句话可以帮助入门,但真做项目时,还得看你的调用对象、团队基础设施、排查成本、性能要求和后续维护成本。
+
+系统规模不大,用 HTTP 已经跑得很稳,就别为了“看起来更微服务”强上 RPC。
+
+内部调用越来越复杂,HTTP SDK、网关、监控、重试这些东西越补越多,那就可以认真考虑 RPC。
+
+一句话:**HTTP 没那么弱,RPC 也没那么神。选哪个,主要看它能不能用更低成本解决你现在的问题。**
+
+详细介绍:[⭐️有了HTTP,为什么还要RPC?](./http-vs-rpc.md)
### HTTP/1.0 和 HTTP/1.1 有什么区别?

-- **连接方式** : HTTP/1.0 为短连接,HTTP/1.1 支持长连接。HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。
-- **状态响应码** : HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
-- **缓存机制** : 在 HTTP/1.0 中主要使用 Header 里的 If-Modified-Since,Expires 来做为缓存判断的标准,HTTP/1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。
+- **连接方式**:HTTP/1.0 为短连接,HTTP/1.1 支持长连接。HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。
+- **状态响应码**:HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
+- **缓存机制**:在 HTTP/1.0 中主要使用 Header 里的 If-Modified-Since,Expires 来做为缓存判断的标准,HTTP/1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。
- **带宽**:HTTP/1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP/1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
-- **Host 头(Host Header)处理** :HTTP/1.1 引入了 Host 头字段,允许在同一 IP 地址上托管多个域名,从而支持虚拟主机的功能。而 HTTP/1.0 没有 Host 头字段,无法实现虚拟主机。
+- **Host 头(Host Header)处理**:HTTP/1.1 引入了 Host 头字段,允许在同一 IP 地址上托管多个域名,从而支持虚拟主机的功能。而 HTTP/1.0 没有 Host 头字段,无法实现虚拟主机。
-关于 HTTP/1.0 和 HTTP/1.1 更详细的对比总结,可以看我写的这篇文章:[HTTP/1.0 vs HTTP/1.1(应用层)](./http1.0-vs-http1.1.md) 。
+关于 HTTP/1.0 和 HTTP/1.1 更详细的对比总结,可以看我写的这篇文章:[HTTP/1.0 vs HTTP/1.1(应用层)](https://javaguide.cn/cs-basics/network/http1.0-vs-http1.1.html)。
-### HTTP/1.1 和 HTTP/2.0 有什么区别?
+### ⭐️ HTTP/1.1 和 HTTP/2.0 有什么区别?

- **多路复用(Multiplexing)**:HTTP/2.0 在同一连接上可以同时传输多个请求和响应(可以看作是 HTTP/1.1 中长链接的升级版本),互不干扰。HTTP/1.1 则使用串行方式,每个请求和响应都需要独立的连接,而浏览器为了控制资源会有 6-8 个 TCP 连接的限制。这使得 HTTP/2.0 在处理多个请求时更加高效,减少了网络延迟和提高了性能。
- **二进制帧(Binary Frames)**:HTTP/2.0 使用二进制帧进行数据传输,而 HTTP/1.1 则使用文本格式的报文。二进制帧更加紧凑和高效,减少了传输的数据量和带宽消耗。
-- **队头阻塞**:HTTP/2 引入了多路复用技术,允许多个请求和响应在单个 TCP 连接上并行交错传输,解决了 HTTP/1.1 应用层的队头阻塞问题,但 HTTP/2 依然受到 TCP 层队头阻塞 的影响。
-- **头部压缩(Header Compression)**:HTTP/1.1 支持`Body`压缩,`Header`不支持压缩。HTTP/2.0 支持对`Header`压缩,使用了专门为`Header`压缩而设计的 HPACK 算法,减少了网络开销。
+- **队头阻塞**:HTTP/2 引入了多路复用技术,允许多个请求和响应在单个 TCP 连接上并行交错传输,解决了 HTTP/1.1 应用层的队头阻塞问题,但 HTTP/2 依然受到 TCP 层队头阻塞的影响。
+- **头部压缩(Header Compression)**:HTTP/1.1 支持 `Body` 压缩,`Header` 不支持压缩。HTTP/2.0 支持对 `Header` 压缩,使用了专门为 `Header` 压缩而设计的 HPACK 算法,减少了网络开销。
- **服务器推送(Server Push)**:HTTP/2.0 支持服务器推送,可以在客户端请求一个资源时,将其他相关资源一并推送给客户端,从而减少了客户端的请求次数和延迟。而 HTTP/1.1 需要客户端自己发送请求来获取相关资源。
HTTP/2.0 多路复用效果图(图源: [HTTP/2 For Web Developers](https://blog.cloudflare.com/http-2-for-web-developers/)):
-
+
可以看到,HTTP/2 的多路复用机制允许多个请求和响应共享一个 TCP 连接,从而避免了 HTTP/1.1 在应对并发请求时需要建立多个并行连接的情况,减少了重复连接建立和维护的额外开销。而在 HTTP/1.1 中,尽管支持持久连接,但为了缓解队头阻塞问题,浏览器通常会为同一域名建立多个并行连接。
@@ -210,7 +250,7 @@ HTTP/2.0 多路复用效果图(图源: [HTTP/2 For Web Developers](https://b

-- **传输协议**:HTTP/2.0 是基于 TCP 协议实现的,HTTP/3.0 新增了 QUIC(Quick UDP Internet Connections) 协议来实现可靠的传输,提供与 TLS/SSL 相当的安全性,具有较低的连接和传输延迟。你可以将 QUIC 看作是 UDP 的升级版本,在其基础上新增了很多功能比如加密、重传等等。HTTP/3.0 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。
+- **传输协议**:HTTP/2.0 是基于 TCP 协议实现的,HTTP/3.0 新增了 QUIC(Quick UDP Internet Connections)协议来实现可靠的传输,提供与 TLS/SSL 相当的安全性,具有较低的连接和传输延迟。你可以将 QUIC 看作是 UDP 的升级版本,在其基础上新增了很多功能比如加密、重传等等。HTTP/3.0 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。
- **连接建立**:HTTP/2.0 需要经过经典的 TCP 三次握手过程(由于安全的 HTTPS 连接建立还需要 TLS 握手,共需要大约 3 个 RTT)。由于 QUIC 协议的特性(TLS 1.3,TLS 1.3 除了支持 1 个 RTT 的握手,还支持 0 个 RTT 的握手)连接建立仅需 0-RTT 或者 1-RTT。这意味着 QUIC 在最佳情况下不需要任何的额外往返时间就可以建立新连接。
- **头部压缩**:HTTP/2.0 使用 HPACK 算法进行头部压缩,而 HTTP/3.0 使用更高效的 QPACK 头压缩算法。
- **队头阻塞**:HTTP/2.0 多请求复用一个 TCP 连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。由于 QUIC 协议的特性,HTTP/3.0 在一定程度上解决了队头阻塞(Head-of-Line blocking, 简写:HOL blocking)问题,一个连接建立多个不同的数据流,这些数据流之间独立互不影响,某个数据流发生丢包了,其数据流不受影响(本质上是多路复用+轮询)。
@@ -220,7 +260,7 @@ HTTP/2.0 多路复用效果图(图源: [HTTP/2 For Web Developers](https://b
HTTP/1.0、HTTP/2.0 和 HTTP/3.0 的协议栈比较:
-
+
下图是一个更详细的 HTTP/2.0 和 HTTP/3.0 对比图:
@@ -248,23 +288,23 @@ HTTP/1.1 队头阻塞的主要原因是无法多路复用:
最后,来一张表格总结补充一下:
-| **方面** | **HTTP/1.1 的队头阻塞** | **HTTP/2.0 的队头阻塞** |
-| -------------- | ---------------------------------------- | ---------------------------------------------------------------- |
-| **层级** | 应用层(HTTP 协议本身的限制) | 传输层(TCP 协议的限制) |
-| **根本原因** | 无法多路复用,请求和响应必须按顺序传输 | TCP 要求数据包按顺序交付,丢包时阻塞整个连接 |
-| **受影响范围** | 单个 HTTP 请求/响应会阻塞后续请求/响应。 | 单个 TCP 包丢失会影响所有 HTTP/2.0 流(依赖于同一个底层 TCP 连接) |
-| **缓解方法** | 开启多个并行的 TCP 连接 | 减少网络掉包或者使用基于 UDP 的 QUIC 协议 |
-| **影响场景** | 每次都会发生,尤其是大文件阻塞小文件时。 | 丢包率较高的网络环境下更容易发生。 |
+| **方面** | **HTTP/1.1 的队头阻塞** | **HTTP/2.0 的队头阻塞** |
+| -------------- | ---------------------------------------- | ------------------------------------------------------------------ |
+| **层级** | 应用层(HTTP 协议本身的限制) | 传输层(TCP 协议的限制) |
+| **根本原因** | 无法多路复用,请求和响应必须按顺序传输 | TCP 要求数据包按顺序交付,丢包时阻塞整个连接 |
+| **受影响范围** | 单个 HTTP 请求/响应会阻塞后续请求/响应。 | 单个 TCP 包丢失会影响所有 HTTP/2.0 流(依赖于同一个底层 TCP 连接) |
+| **缓解方法** | 开启多个并行的 TCP 连接 | 减少网络掉包或者使用基于 UDP 的 QUIC 协议 |
+| **影响场景** | 每次都会发生,尤其是大文件阻塞小文件时。 | 丢包率较高的网络环境下更容易发生。 |
-### HTTP 是不保存状态的协议, 如何保存用户状态?
+### ⭐️ HTTP 是不保存状态的协议,如何保存用户状态?
-HTTP 协议本身是 **无状态的 (stateless)** 。这意味着服务器默认情况下无法区分两个连续的请求是否来自同一个用户,或者同一个用户之前的操作是什么。这就像一个“健忘”的服务员,每次你跟他说话,他都不知道你是谁,也不知道你之前点过什么菜。
+HTTP 协议本身是 **无状态的(stateless)**。这意味着服务器默认情况下无法区分两个连续的请求是否来自同一个用户,或者同一个用户之前的操作是什么。这就像一个“健忘”的服务员,每次你跟他说话,他都不知道你是谁,也不知道你之前点过什么菜。
但在实际的 Web 应用中,比如网上购物、用户登录等场景,我们显然需要记住用户的状态(例如购物车里的商品、用户的登录信息)。为了解决这个问题,主要有以下几种常用机制:
-**方案一:Session (会话) 配合 Cookie (主流方式):**
+**方案一:Session(会话)配合 Cookie(主流方式):**
-
+
这可以说是最经典也是最常用的方法了。基本流程是这样的:
@@ -282,11 +322,11 @@ HTTP 协议本身是 **无状态的 (stateless)** 。这意味着服务器默认
Session 数据本身存储在服务器端。常见的存储方式有:
-- **服务器内存**:实现简单,访问速度快,但服务器重启数据会丢失,且不利于多服务器间的负载均衡。这种方式适合简单且用户量不大的业务场景。
-- **数据库 (如 MySQL, PostgreSQL)**:数据持久化,但读写性能相对较低,一般不会使用这种方式。
-- **分布式缓存 (如 Redis)**:性能高,支持分布式部署,是目前大规模应用中非常主流的方案。
+- **服务器内存**:实现简单,访问速度快,但服务器重启数据会丢失,且不利于多服务器间的负载均衡。这种方式适合简单且用户量不大的业务场景。
+- **数据库(如 MySQL, PostgreSQL)**:数据持久化,但读写性能相对较低,一般不会使用这种方式。
+- **分布式缓存(如 Redis)**:性能高,支持分布式部署,是目前大规模应用中非常主流的方案。
-**方案二:当 Cookie 被禁用时:URL 重写 (URL Rewriting)**
+**方案二:当 Cookie 被禁用时:URL 重写(URL Rewriting)**
如果用户的浏览器禁用了 Cookie,或者某些情况下不便使用 Cookie,还有一种备选方案是 URL 重写。这种方式会将 `SessionID` 直接附加到 URL 的末尾,作为参数传递。例如:。服务器端会解析 URL 中的 `sessionid` 参数来获取 `SessionID`,进而找到对应的 Session 数据。
@@ -294,20 +334,20 @@ Session 数据本身存储在服务器端。常见的存储方式有:
- URL 会变长且不美观;
- `SessionID` 暴露在 URL 中,安全性较低(容易被复制、分享或记录在日志中);
-- 对搜索引擎优化 (SEO) 可能不友好。
+- 对搜索引擎优化(SEO)可能不友好。
-**方案三:Token-based 认证 (如 JWT - JSON Web Tokens)**
+**方案三:Token-based 认证(如 JWT - JSON Web Tokens)**
这是一种越来越流行的无状态认证方式,尤其适用于前后端分离的架构和微服务。
-
+
-以 JWT 为例(普通 Token 方案也可以),简化后的步骤如下
+以 JWT 为例(普通 Token 方案也可以),简化后的步骤如下:
-1. 用户向服务器发送用户名、密码以及验证码用于登陆系统;
-2. 如果用户用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token,也就是 JWT;
-3. 客户端收到 Token 后自己保存起来(比如浏览器的 `localStorage` );
-4. 用户以后每次向后端发请求都在 Header 中带上这个 JWT ;
+1. 用户向服务器发送用户名、密码以及验证码用于登陆系统。
+2. 如果用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token,也就是 JWT。
+3. 客户端收到 Token 后自己保存起来(比如浏览器的 `localStorage`)。
+4. 用户以后每次向后端发请求都在 Header 中带上这个 JWT。
5. 服务端检查 JWT 并从中获取用户相关信息。
JWT 详细介绍可以查看这两篇文章:
@@ -317,22 +357,20 @@ JWT 详细介绍可以查看这两篇文章:
总结来说,虽然 HTTP 本身是无状态的,但通过 Cookie + Session、URL 重写或 Token 等机制,我们能够有效地在 Web 应用中跟踪和管理用户状态。其中,**Cookie + Session 是最传统也最广泛使用的方式,而 Token-based 认证则在现代 Web 应用中越来越受欢迎。**
-### URI 和 URL 的区别是什么?
+### URI 和 URL 的区别是什么?
-- URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。
-- URL(Uniform Resource Locator) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。
+- URI(Uniform Resource Identifier)是统一资源标志符,可以唯一标识一个资源。
+- URL(Uniform Resource Locator)是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。
URI 的作用像身份证号一样,URL 的作用更像家庭住址一样。URL 是一种具体的 URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。
### Cookie 和 Session 有什么区别?
-准确点来说,这个问题属于认证授权的范畴,你可以在 [认证授权基础概念详解](../../system-design/security/basis-of-authority-certification.md) 这篇文章中找到详细的答案。
-
-### GET 和 POST 的区别
+准确点来说,这个问题属于认证授权的范畴,你可以在 [认证授权基础概念详解](https://javaguide.cn/system-design/security/basis-of-authority-certification.html) 这篇文章中找到详细的答案。
-这个问题在知乎上被讨论的挺火热的,地址: 。
+### ⭐️ GET 和 POST 的区别
-
+这个问题在知乎上被讨论的挺火热的,地址:。
GET 和 POST 是 HTTP 协议中两种常用的请求方法,它们在不同的场景和目的下有不同的特点和用法。一般来说,可以从以下几个方面来区分二者(重点搞清两者在语义上的区别即可):
@@ -354,7 +392,7 @@ WebSocket 协议在 2008 年诞生,2011 年成为国际标准,几乎所有
WebSocket 协议本质上是应用层的协议,用于弥补 HTTP 协议在持久通信能力上的不足。客户端和服务器仅需一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
-
+
下面是 WebSocket 的常见应用场景:
@@ -365,14 +403,14 @@ WebSocket 协议本质上是应用层的协议,用于弥补 HTTP 协议在持
- 社交聊天
- ……
-### WebSocket 和 HTTP 有什么区别?
+### ⭐️ WebSocket 和 HTTP 有什么区别?
WebSocket 和 HTTP 两者都是基于 TCP 的应用层协议,都可以在网络中传输数据。
下面是二者的主要区别:
- WebSocket 是一种双向实时通信协议,而 HTTP 是一种单向通信协议。并且,HTTP 协议下的通信只能由客户端发起,服务器无法主动通知客户端。
-- WebSocket 使用 ws:// 或 wss://(使用 SSL/TLS 加密后的协议,类似于 HTTP 和 HTTPS 的关系) 作为协议前缀,HTTP 使用 http:// 或 https:// 作为协议前缀。
+- WebSocket 使用 ws:// 或 wss://(使用 SSL/TLS 加密后的协议,类似于 HTTP 和 HTTPS 的关系)作为协议前缀,HTTP 使用 http:// 或 https:// 作为协议前缀。
- WebSocket 可以支持扩展,用户可以扩展协议,实现部分自定义的子协议,如支持压缩、加密等。
- WebSocket 通信数据格式比较轻量,用于协议控制的数据包头部相对较小,网络开销小,而 HTTP 通信每次都要携带完整的头部,网络开销较大(HTTP/2.0 使用二进制帧进行数据传输,还支持头部压缩,减少了网络开销)。
@@ -381,13 +419,13 @@ WebSocket 和 HTTP 两者都是基于 TCP 的应用层协议,都可以在网
WebSocket 的工作过程可以分为以下几个步骤:
1. 客户端向服务器发送一个 HTTP 请求,请求头中包含 `Upgrade: websocket` 和 `Sec-WebSocket-Key` 等字段,表示要求升级协议为 WebSocket;
-2. 服务器收到这个请求后,会进行升级协议的操作,如果支持 WebSocket,它将回复一个 HTTP 101 状态码,响应头中包含 ,`Connection: Upgrade`和 `Sec-WebSocket-Accept: xxx` 等字段、表示成功升级到 WebSocket 协议。
+2. 服务器收到这个请求后,会进行升级协议的操作,如果支持 WebSocket,它将回复一个 HTTP 101 状态码,响应头中包含 `Connection: Upgrade` 和 `Sec-WebSocket-Accept: xxx` 等字段,表示成功升级到 WebSocket 协议。
3. 客户端和服务器之间建立了一个 WebSocket 连接,可以进行双向的数据传输。数据以帧(frames)的形式进行传送,WebSocket 的每条消息可能会被切分成多个数据帧(最小单位)。发送端会将消息切割成多个帧发送给接收端,接收端接收消息帧,并将关联的帧重新组装成完整的消息。
4. 客户端或服务器可以主动发送一个关闭帧,表示要断开连接。另一方收到后,也会回复一个关闭帧,然后双方关闭 TCP 连接。
另外,建立 WebSocket 连接之后,通过心跳机制来保持 WebSocket 连接的稳定性和活跃性。
-### WebSocket 与短轮询、长轮询的区别
+### ⭐️ WebSocket 与短轮询、长轮询的区别
这三种方式,都是为了解决“**客户端如何及时获取服务器最新数据,实现实时更新**”的问题。它们的实现方式和效率、实时性差异较大。
@@ -420,15 +458,15 @@ WebSocket 的工作过程可以分为以下几个步骤:
- **使用限制**:需要服务器和客户端都支持 WebSocket 协议。对连接管理有一定要求(如心跳保活、断线重连等)。
- **实现麻烦**:实现起来比短轮询和长轮询要更麻烦一些。
-
+
-### SSE 与 WebSocket 有什么区别?
+### ⭐️ SSE 与 WebSocket 有什么区别?
-SSE (Server-Sent Events) 和 WebSocket 都是用来实现服务器向浏览器实时推送消息的技术,让网页内容能自动更新,而不需要用户手动刷新。虽然目标相似,但它们在工作方式和适用场景上有几个关键区别:
+SSE(Server-Sent Events)和 WebSocket 都是用来实现服务器向浏览器实时推送消息的技术,让网页内容能自动更新,而不需要用户手动刷新。虽然目标相似,但它们在工作方式和适用场景上有几个关键区别:
1. **通信方式:**
- **SSE:** **单向通信**。只有服务器能向客户端(浏览器)发送数据。客户端不能通过同一个连接向服务器发送数据(需要发起新的 HTTP 请求)。
- - **WebSocket:** **双向通信 (全双工)**。客户端和服务器可以随时互相发送消息,实现真正的实时交互。
+ - **WebSocket:** **双向通信(全双工)**。客户端和服务器可以随时互相发送消息,实现真正的实时交互。
2. **底层协议:**
- **SSE:** 基于**标准的 HTTP/HTTPS 协议**。它本质上是一个“长连接”的 HTTP 请求,服务器保持连接打开并持续发送事件流。不需要特殊的服务器或协议支持,现有的 HTTP 基础设施就能用。
- **WebSocket:** 使用**独立的 ws:// 或 wss:// 协议**。它需要通过一个特定的 HTTP "Upgrade" 请求来建立连接,并且服务器需要明确支持 WebSocket 协议来处理连接和消息帧。
@@ -439,18 +477,18 @@ SSE (Server-Sent Events) 和 WebSocket 都是用来实现服务器向浏览器
- **SSE:** **浏览器原生支持**。EventSource API 提供了自动断线重连的机制。
- **WebSocket:** **需要手动实现**。开发者需要自己编写逻辑来检测断线并进行重连尝试。
5. **数据类型:**
- - **SSE:** **主要设计用来传输文本** (UTF-8 编码)。如果需要传输二进制数据,需要先进行 Base64 等编码转换成文本。
+ - **SSE:** **主要设计用来传输文本**(UTF-8 编码)。如果需要传输二进制数据,需要先进行 Base64 等编码转换成文本。
- **WebSocket:** **原生支持传输文本和二进制数据**,无需额外编码。
-为了提供更好的用户体验和利用其简单、高效、基于标准 HTTP 的特性,**Server-Sent Events (SSE) 是目前大型语言模型 API(如 OpenAI、DeepSeek 等)实现流式响应的常用甚至可以说是标准的技木选择**。
+为了提供更好的用户体验和利用其简单、高效、基于标准 HTTP 的特性,**Server-Sent Events(SSE)是目前大型语言模型 API(如 OpenAI、DeepSeek 等)实现流式响应的常用甚至可以说是标准的技术选择**。
这里以 DeepSeek 为例,我们发送一个请求并打开浏览器控制台验证一下:
-
+
-
+
-可以看到,响应头应里包含了 `text/event-stream`,说明使用的确实是SSE。并且,响应数据也确实是持续分块传输。
+可以看到,响应头里包含了 `text/event-stream`,说明使用的确实是 SSE。并且,响应数据也确实是持续分块传输。
## PING
@@ -493,37 +531,60 @@ ICMP 报文中包含了类型字段,用于标识 ICMP 报文类型。ICMP 报
- **查询报文类型**:向目标主机发送请求并期望得到响应。
- **差错报文类型**:向源主机发送错误信息,用于报告网络中的错误情况。
-PING 用到的 ICMP Echo Request(类型为 8 ) 和 ICMP Echo Reply(类型为 0) 属于查询报文类型 。
+PING 用到的 ICMP Echo Request(类型为 8)和 ICMP Echo Reply(类型为 0)属于查询报文类型。
- PING 命令会向目标主机发送 ICMP Echo Request。
- 如果两个主机的连通性正常,目标主机会返回一个对应的 ICMP Echo Reply。
+### ⭐️ 能 Ping 通,TCP 就一定能连通吗?
+
+先说结论:**不是**。
+
+Ping 使用 ICMP(网络层),TCP 连接使用 TCP(传输层),两者可能经过同一条网络路径,但中间设备会按协议类型、端口、连接状态和安全策略分别处理。你能 Ping 通,只能说明 ICMP Echo 这条路径能往返,不等于目标 TCP 端口一定可达。
+
+
+
+常见原因有这几种:
+
+- **防火墙策略不同**:很多网络设备允许 ICMP(方便运维探测),但 TCP 端口规则收得更紧,可能只开放了 `22`、`80`、`443`,其他端口一律不放。
+- **服务没启动或端口没监听**:主机能回 ICMP,但 Nginx 没启动、MySQL 没监听,Ping 通但 TCP 连不上。
+- **中间有 NAT / 负载均衡 / 安全设备**:公网 IP 后面可能不是一台真实服务器,ICMP 响应可能来自中间设备,不能直接等同于后端服务可用。
+- **HTTPS 还可能卡在 TLS 握手的 SNI**:TCP 三次握手可能成功了,但 `ClientHello` 里的 SNI 被中间设备识别并拦截,导致连接重置或卡住。
+
+反过来也成立:**Ping 不通,不代表 TCP 一定不通**。有些服务器或云安全组直接禁 ICMP,但业务端口正常工作。
+
+排查建议:先看 DNS(域名场景),再用 `ping` 看 ICMP,然后用 `nc` 测端口,最后用 `curl` 或 `openssl s_client` 看 HTTPS / TLS。别用一个命令过早下结论。
+
+
+
+详细介绍:[能 Ping 通,TCP 就一定能连通吗?](./can-ping-but-tcp-may-not-connect.md)
+
## DNS
### DNS 的作用是什么?
DNS(Domain Name System)域名管理系统,是当用户使用浏览器访问网址之后,使用的第一个重要协议。DNS 要解决的是**域名和 IP 地址的映射问题**。
-
+
在一台电脑上,可能存在浏览器 DNS 缓存,操作系统 DNS 缓存,路由器 DNS 缓存。如果以上缓存都查询不到,那么 DNS 就闪亮登场了。
-目前 DNS 的设计采用的是分布式、层次数据库结构,**DNS 是应用层协议,它可以在 UDP 或 TCP 协议之上运行,端口为 53** 。
+目前 DNS 的设计采用的是分布式、层次数据库结构,**DNS 是应用层协议,它可以在 UDP 或 TCP 协议之上运行,端口为 53**。
### DNS 服务器有哪些?根服务器有多少个?
-DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务器都属于以下四个类别之一):
+DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务器都属于以下四个类别之一):
- 根 DNS 服务器。根 DNS 服务器提供 TLD 服务器的 IP 地址。目前世界上只有 13 组根服务器,我国境内目前仍没有根服务器。
-- 顶级域 DNS 服务器(TLD 服务器)。顶级域是指域名的后缀,如`com`、`org`、`net`和`edu`等。国家也有自己的顶级域,如`uk`、`fr`和`ca`。TLD 服务器提供了权威 DNS 服务器的 IP 地址。
+- 顶级域 DNS 服务器(TLD 服务器)。顶级域是指域名的后缀,如 `com`、`org`、`net` 和 `edu` 等。国家也有自己的顶级域,如 `uk`、`fr` 和 `ca`。TLD 服务器提供了权威 DNS 服务器的 IP 地址。
- 权威 DNS 服务器。在因特网上具有公共可访问主机的每个组织机构必须提供公共可访问的 DNS 记录,这些记录将这些主机的名字映射为 IP 地址。
- 本地 DNS 服务器。每个 ISP(互联网服务提供商)都有一个自己的本地 DNS 服务器。当主机发出 DNS 请求时,该请求被发往本地 DNS 服务器,它起着代理的作用,并将该请求转发到 DNS 层次结构中。严格说来,不属于 DNS 层级结构
世界上并不是只有 13 台根服务器,这是很多人普遍的误解,网上很多文章也是这么写的。实际上,现在根服务器数量远远超过这个数量。最初确实是为 DNS 根服务器分配了 13 个 IP 地址,每个 IP 地址对应一个不同的根 DNS 服务器。然而,由于互联网的快速发展和增长,这个原始的架构变得不太适应当前的需求。为了提高 DNS 的可靠性、安全性和性能,目前这 13 个 IP 地址中的每一个都有多个服务器,截止到 2023 年底,所有根服务器之和达到了 1700 多台,未来还会继续增加。
-### DNS 解析的过程是什么样的?
+### ⭐️ DNS 解析的过程是什么样的?
-整个过程的步骤比较多,我单独写了一篇文章详细介绍:[DNS 域名系统详解(应用层)](./dns.md) 。
+整个过程的步骤比较多,我单独写了一篇文章详细介绍:[DNS 域名系统详解(应用层)](https://javaguide.cn/cs-basics/network/dns.html)。
### DNS 劫持了解吗?如何应对?
@@ -536,6 +597,6 @@ DNS 劫持是一种网络攻击,它通过修改 DNS 服务器的解析结果
- 详解 HTTP/2.0 及 HTTPS 协议:
- HTTP 请求头字段大全| HTTP Request Headers:
- HTTP1、HTTP2、HTTP3:
-- 如何看待 HTTP/3 ? - 车小胖的回答 - 知乎:
+- 如何看待 HTTP/3? - 车小胖的回答 - 知乎:
diff --git a/docs/cs-basics/network/other-network-questions2.md b/docs/cs-basics/network/other-network-questions2.md
index 67c731f44c0..5408f860509 100644
--- a/docs/cs-basics/network/other-network-questions2.md
+++ b/docs/cs-basics/network/other-network-questions2.md
@@ -1,22 +1,29 @@
---
-title: 计算机网络常见面试题总结(下)
+title: 计算机网络常见面试题总结(下)
+description: 最新计算机网络高频面试题总结(下):TCP/UDP深度对比、三次握手四次挥手、HTTP/3 QUIC优化、IPv6优势、NAT/ARP详解,附表格+⭐️重点标注,一文掌握传输层&网络层核心考点,快速通关后端技术面试!
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: 计算机网络面试题,TCP vs UDP,TCP三次握手,HTTP/3 QUIC,IPv4 vs IPv6,TCP可靠性,IP地址,NAT协议,ARP协议,传输层面试,网络层高频题,基于TCP协议,基于UDP协议,队头阻塞,四次挥手
---
-下篇主要是传输层和网络层相关的内容。
+计算机网络面试题里,真正容易被追问到细节的部分,往往集中在 **TCP、UDP、IP、ARP、NAT、IPv4/IPv6** 这些传输层和网络层知识点上。比如:为什么 TCP 可靠?为什么要三次握手和四次挥手?HTTP/3 为什么改用基于 UDP 的 QUIC?这些问题不仅考概念,也考你对网络通信过程的理解。
+
+这篇《计算机网络常见面试题总结(下)》会重点梳理 TCP 与 UDP、TCP 连接管理、可靠传输、IP 地址、ARP、NAT 等后端面试高频内容,帮助你把传输层和网络层的核心考点串起来。
## TCP 与 UDP
-### TCP 与 UDP 的区别(重要)
+### ⭐️ TCP 与 UDP 的区别(重要)
1. **是否面向连接**:
- TCP 是面向连接的。在传输数据之前,必须先通过“三次握手”建立连接;数据传输完成后,还需要通过“四次挥手”来释放连接。这保证了双方都准备好通信。
- UDP 是无连接的。发送数据前不需要建立任何连接,直接把数据包(数据报)扔出去。
2. **是否是可靠传输**:
- - TCP 提供可靠的数据传输服务。它通过序列号、确认应答 (ACK)、超时重传、流量控制、拥塞控制等一系列机制,来确保数据能够无差错、不丢失、不重复且按顺序地到达目的地。
- - UDP 提供不可靠的传输。它尽最大努力交付 (best-effort delivery),但不保证数据一定能到达,也不保证到达的顺序,更不会自动重传。收到报文后,接收方也不会主动发确认。
+ - TCP 提供可靠的数据传输服务。它通过序列号、确认应答(ACK)、超时重传、流量控制、拥塞控制等一系列机制,来确保数据能够无差错、不丢失、不重复且按顺序地到达目的地。
+ - UDP 提供不可靠的传输。它尽最大努力交付(best-effort delivery),但不保证数据一定能到达,也不保证到达的顺序,更不会自动重传。收到报文后,接收方也不会主动发确认。
3. **是否有状态**:
- TCP 是有状态的。因为要保证可靠性,TCP 需要在连接的两端维护连接状态信息,比如序列号、窗口大小、哪些数据发出去了、哪些收到了确认等。
- UDP 是无状态的。它不维护连接状态,发送方发出数据后就不再关心它是否到达以及如何到达,因此开销更小(**这很“渣男”!**)。
@@ -24,14 +31,14 @@ tag:
- TCP 因为需要建立连接、发送确认、处理重传等,其开销较大,传输效率相对较低。
- UDP 结构简单,没有复杂的控制机制,开销小,传输效率更高,速度更快。
5. **传输形式**:
- - TCP 是面向字节流 (Byte Stream) 的。它将应用程序交付的数据视为一连串无结构的字节流,可能会对数据进行拆分或合并。
- - UDP 是面向报文 (Message Oriented) 的。应用程序交给 UDP 多大的数据块,UDP 就照样发送,既不拆分也不合并,保留了应用程序消息的边界。
+ - TCP 是面向字节流(Byte Stream)的。它将应用程序交付的数据视为一连串无结构的字节流,可能会对数据进行拆分或合并。
+ - UDP 是面向报文(Message Oriented)的。应用程序交给 UDP 多大的数据块,UDP 就照样发送,既不拆分也不合并,保留了应用程序消息的边界。
6. **首部开销**:
- TCP 的头部至少需要 20 字节,如果包含选项字段,最多可达 60 字节。
- UDP 的头部非常简单,固定只有 8 字节。
7. **是否提供广播或多播服务**:
- - TCP 只支持点对点 (Point-to-Point) 的单播通信。
- - UDP 支持一对一 (单播)、一对多 (多播/Multicast) 和一对所有 (广播/Broadcast) 的通信方式。
+ - TCP 只支持点对点(Point-to-Point)的单播通信。
+ - UDP 支持一对一(单播)、一对多(多播/Multicast)和一对所有(广播/Broadcast)的通信方式。
8. ……
为了更直观地对比,可以看下面这个表格:
@@ -39,33 +46,33 @@ tag:
| 特性 | TCP | UDP |
| ------------ | -------------------------- | ----------------------------------- |
| **连接性** | 面向连接 | 无连接 |
-| **可靠性** | 可靠 | 不可靠 (尽力而为) |
+| **可靠性** | 可靠 | 不可靠(尽力而为) |
| **状态维护** | 有状态 | 无状态 |
| **传输效率** | 较低 | 较高 |
-| **传输形式** | 面向字节流 | 面向数据报 (报文) |
+| **传输形式** | 面向字节流 | 面向数据报(报文) |
| **头部开销** | 20 - 60 字节 | 8 字节 |
-| **通信模式** | 点对点 (单播) | 单播、多播、广播 |
+| **通信模式** | 点对点(单播) | 单播、多播、广播 |
| **常见应用** | HTTP/HTTPS, FTP, SMTP, SSH | DNS, DHCP, SNMP, TFTP, VoIP, 视频流 |
-### 什么时候选择 TCP,什么时候选 UDP?
+### ⭐️ 什么时候选择 TCP,什么时候选 UDP?
选择 TCP 还是 UDP,主要取决于你的应用**对数据传输的可靠性要求有多高,以及对实时性和效率的要求有多高**。
当**数据准确性和完整性至关重要,一点都不能出错**时,通常选择 TCP。因为 TCP 提供了一整套机制(三次握手、确认应答、重传、流量控制等)来保证数据能够可靠、有序地送达。典型应用场景如下:
-- **Web 浏览 (HTTP/HTTPS):** 网页内容、图片、脚本必须完整加载才能正确显示。
-- **文件传输 (FTP, SCP):** 文件内容不允许有任何字节丢失或错序。
-- **邮件收发 (SMTP, POP3, IMAP):** 邮件内容需要完整无误地送达。
-- **远程登录 (SSH, Telnet):** 命令和响应需要准确传输。
-- ......
+- **Web 浏览(HTTP/HTTPS):** 网页内容、图片、脚本必须完整加载才能正确显示。
+- **文件传输(FTP, SCP):** 文件内容不允许有任何字节丢失或错序。
+- **邮件收发(SMTP, POP3, IMAP):** 邮件内容需要完整无误地送达。
+- **远程登录(SSH, Telnet):** 命令和响应需要准确传输。
+- ……
当**实时性、速度和效率优先,并且应用能容忍少量数据丢失或乱序**时,通常选择 UDP。UDP 开销小、传输快,没有建立连接和保证可靠性的复杂过程。典型应用场景如下:
-- **实时音视频通信 (VoIP, 视频会议, 直播):** 偶尔丢失一两个数据包(可能导致画面或声音短暂卡顿)通常比因为等待重传(TCP 机制)导致长时间延迟更可接受。应用层可能会有自己的补偿机制。
+- **实时音视频通信(VoIP, 视频会议,直播):** 偶尔丢失一两个数据包(可能导致画面或声音短暂卡顿)通常比因为等待重传(TCP 机制)导致长时间延迟更可接受。应用层可能会有自己的补偿机制。
- **在线游戏:** 需要快速传输玩家位置、状态等信息,对实时性要求极高,旧的数据很快就没用了,丢失少量数据影响通常不大。
-- **DHCP (动态主机配置协议):** 客户端在请求 IP 时自身没有 IP 地址,无法满足 TCP 建立连接的前提条件,并且 DHCP 有广播需求、交互模式简单以及自带可靠性机制。
-- **物联网 (IoT) 数据上报:** 某些场景下,传感器定期上报数据,丢失个别数据点可能不影响整体趋势分析。
-- ......
+- **DHCP(动态主机配置协议):** 客户端在请求 IP 时自身没有 IP 地址,无法满足 TCP 建立连接的前提条件,并且 DHCP 有广播需求、交互模式简单以及自带可靠性机制。
+- **物联网(IoT)数据上报:** 某些场景下,传感器定期上报数据,丢失个别数据点可能不影响整体趋势分析。
+- ……
### HTTP 基于 TCP 还是 UDP?
@@ -73,21 +80,21 @@ tag:
🐛 修正(参见 [issue#1915](https://github.com/Snailclimb/JavaGuide/issues/1915)):
-HTTP/3.0 之前是基于 TCP 协议的,而 HTTP/3.0 将弃用 TCP,改用 **基于 UDP 的 QUIC 协议** :
+HTTP/3.0 之前是基于 TCP 协议的,而 HTTP/3.0 将弃用 TCP,改用 **基于 UDP 的 QUIC 协议**:
- **HTTP/1.x 和 HTTP/2.0**:这两个版本的 HTTP 协议都明确建立在 TCP 之上。TCP 提供了可靠的、面向连接的传输,确保数据按序、无差错地到达,这对于网页内容的正确展示非常重要。发送 HTTP 请求前,需要先通过 TCP 的三次握手建立连接。
- **HTTP/3.0**:这是一个重大的改变。HTTP/3 弃用了 TCP,转而使用 QUIC 协议,而 QUIC 是构建在 UDP 之上的。
-
+
**为什么 HTTP/3 要做这个改变呢?主要有两大原因:**
-1. 解决队头阻塞 (Head-of-Line Blocking,简写:HOL blocking) 问题。
+1. 解决队头阻塞(Head-of-Line Blocking,简写:HOL blocking)问题。
2. 减少连接建立的延迟。
下面我们来详细介绍这两大优化。
-在 HTTP/2 中,虽然可以在一个 TCP 连接上并发传输多个请求/响应流(多路复用),但 TCP 本身的特性(保证有序、可靠)意味着如果其中一个流的某个 TCP 报文丢失或延迟,整个 TCP 连接都会被阻塞,等待该报文重传。这会导致所有在这个 TCP 连接上的 HTTP/2 流都受到影响,即使其他流的数据包已经到达。**QUIC (运行在 UDP 上) 解决了这个问题**。QUIC 内部实现了自己的多路复用和流控制机制。不同的 HTTP 请求/响应流在 QUIC 层面是真正独立的。如果一个流的数据包丢失,它只会阻塞该流,而不会影响同一 QUIC 连接上的其他流(本质上是多路复用+轮询),大大提高了并发传输的效率。
+在 HTTP/2 中,虽然可以在一个 TCP 连接上并发传输多个请求/响应流(多路复用),但 TCP 本身的特性(保证有序、可靠)意味着如果其中一个流的某个 TCP 报文丢失或延迟,整个 TCP 连接都会被阻塞,等待该报文重传。这会导致所有在这个 TCP 连接上的 HTTP/2 流都受到影响,即使其他流的数据包已经到达。**QUIC(运行在 UDP 上)解决了这个问题**。QUIC 内部实现了自己的多路复用和流控制机制。不同的 HTTP 请求/响应流在 QUIC 层面是真正独立的。如果一个流的数据包丢失,它只会阻塞该流,而不会影响同一 QUIC 连接上的其他流(本质上是多路复用+轮询),大大提高了并发传输的效率。
除了解决队头阻塞问题,HTTP/3.0 还可以减少握手过程的延迟。在 HTTP/2.0 中,如果要建立一个安全的 HTTPS 连接,需要经过 TCP 三次握手和 TLS 握手:
@@ -101,57 +108,150 @@ HTTP/3.0 之前是基于 TCP 协议的,而 HTTP/3.0 将弃用 TCP,改用 **
-
-
+### 为什么 TCP 是面向字节流,UDP 是面向报文?
+
+
+
+TCP 是面向字节流的。应用层写入的数据会进入内核缓冲区,TCP 只保证这些字节可靠、有序地到达对端,不保证一次 `send()` 对应一次 `recv()`,也不保留应用层消息边界。因此接收方可能一次读到多条消息,也可能只读到半条消息,这就是常说的粘包、拆包现象。
+
+
+UDP 是面向报文的。应用层交给 UDP 的一次数据会作为一个 UDP 数据报发送,接收端也是按数据报读取,所以天然保留消息边界。不过 UDP 不保证可靠到达,也不保证顺序。
+
+解决 TCP 粘包/拆包,本质是应用层协议自己定义消息边界。常见方案有固定长度、分隔符、长度头。工程里更常用长度头,因为它对二进制协议和变长消息更友好,但要处理字节序、最大长度限制、半包缓存和异常连接关闭等问题。
+
+
+
+详细介绍:[为什么 TCP 是面向字节流,UDP 是面向报文?](./tcp-byte-stream-udp-datagram.md)
+
### 你知道哪些基于 TCP/UDP 的协议?
-TCP (传输控制协议) 和 UDP (用户数据报协议) 是互联网传输层的两大核心协议,它们为各种应用层协议提供了基础的通信服务。以下是一些常见的、分别构建在 TCP 和 UDP 之上的应用层协议:
-
-**运行于 TCP 协议之上的协议 (强调可靠、有序传输):**
-
-| 中文全称 (缩写) | 英文全称 | 主要用途 | 说明与特性 |
-| -------------------------- | ---------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
-| 超文本传输协议 (HTTP) | HyperText Transfer Protocol | 传输网页、超文本、多媒体内容 | **HTTP/1.x 和 HTTP/2 基于 TCP**。早期版本不加密,是 Web 通信的基础。 |
-| 安全超文本传输协议 (HTTPS) | HyperText Transfer Protocol Secure | 加密的网页传输 | 在 HTTP 和 TCP 之间增加了 SSL/TLS 加密层,确保数据传输的机密性和完整性。 |
-| 文件传输协议 (FTP) | File Transfer Protocol | 文件传输 | 传统的 FTP **明文传输**,不安全。推荐使用其安全版本 **SFTP (SSH File Transfer Protocol)** 或 **FTPS (FTP over SSL/TLS)** 。 |
-| 简单邮件传输协议 (SMTP) | Simple Mail Transfer Protocol | **发送**电子邮件 | 负责将邮件从客户端发送到服务器,或在邮件服务器之间传递。可通过 **STARTTLS** 升级到加密传输。 |
-| 邮局协议第 3 版 (POP3) | Post Office Protocol version 3 | **接收**电子邮件 | 通常将邮件从服务器**下载到本地设备后删除服务器副本** (可配置保留)。**POP3S** 是其 SSL/TLS 加密版本。 |
-| 互联网消息访问协议 (IMAP) | Internet Message Access Protocol | **接收和管理**电子邮件 | 邮件保留在服务器,支持多设备同步邮件状态、文件夹管理、在线搜索等。**IMAPS** 是其 SSL/TLS 加密版本。现代邮件服务首选。 |
-| 远程终端协议 (Telnet) | Teletype Network | 远程终端登录 | **明文传输**所有数据 (包括密码),安全性极差,基本已被 SSH 完全替代。 |
-| 安全外壳协议 (SSH) | Secure Shell | 安全远程管理、加密数据传输 | 提供了加密的远程登录和命令执行,以及安全的文件传输 (SFTP) 等功能,是 Telnet 的安全替代品。 |
-
-**运行于 UDP 协议之上的协议 (强调快速、低开销传输):**
-
-| 中文全称 (缩写) | 英文全称 | 主要用途 | 说明与特性 |
-| ----------------------- | ------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------ |
-| 超文本传输协议 (HTTP/3) | HyperText Transfer Protocol version 3 | 新一代网页传输 | 基于 **QUIC** 协议 (QUIC 本身构建于 UDP 之上),旨在减少延迟、解决 TCP 队头阻塞问题,支持 0-RTT 连接建立。 |
-| 动态主机配置协议 (DHCP) | Dynamic Host Configuration Protocol | 动态分配 IP 地址及网络配置 | 客户端从服务器自动获取 IP 地址、子网掩码、网关、DNS 服务器等信息。 |
-| 域名系统 (DNS) | Domain Name System | 域名到 IP 地址的解析 | **通常使用 UDP** 进行快速查询。当响应数据包过大或进行区域传送 (AXFR) 时,会**切换到 TCP** 以保证数据完整性。 |
-| 实时传输协议 (RTP) | Real-time Transport Protocol | 实时音视频数据流传输 | 常用于 VoIP、视频会议、直播等。追求低延迟,允许少量丢包。通常与 RTCP 配合使用。 |
-| RTP 控制协议 (RTCP) | RTP Control Protocol | RTP 流的质量监控和控制信息 | 配合 RTP 工作,提供丢包、延迟、抖动等统计信息,辅助流量控制和拥塞管理。 |
-| 简单文件传输协议 (TFTP) | Trivial File Transfer Protocol | 简化的文件传输 | 功能简单,常用于局域网内无盘工作站启动、网络设备固件升级等小文件传输场景。 |
-| 简单网络管理协议 (SNMP) | Simple Network Management Protocol | 网络设备的监控与管理 | 允许网络管理员查询和修改网络设备的状态信息。 |
-| 网络时间协议 (NTP) | Network Time Protocol | 同步计算机时钟 | 用于在网络中的计算机之间同步时间,确保时间的一致性。 |
+TCP(传输控制协议)和 UDP(用户数据报协议)是互联网传输层的两大核心协议,它们为各种应用层协议提供了基础的通信服务。以下是一些常见的、分别构建在 TCP 和 UDP 之上的应用层协议:
+
+**运行于 TCP 协议之上的协议(强调可靠、有序传输):**
+
+| 中文全称(缩写) | 英文全称 | 主要用途 | 说明与特性 |
+| --------------------------- | ---------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
+| 超文本传输协议(HTTP) | HyperText Transfer Protocol | 传输网页、超文本、多媒体内容 | **HTTP/1.x 和 HTTP/2 基于 TCP**。早期版本不加密,是 Web 通信的基础。 |
+| 安全超文本传输协议(HTTPS) | HyperText Transfer Protocol Secure | 加密的网页传输 | 在 HTTP 和 TCP 之间增加了 SSL/TLS 加密层,确保数据传输的机密性和完整性。 |
+| 文件传输协议(FTP) | File Transfer Protocol | 文件传输 | 传统的 FTP **明文传输**,不安全。推荐使用其安全版本 **SFTP(SSH File Transfer Protocol)** 或 **FTPS (FTP over SSL/TLS)**。 |
+| 简单邮件传输协议(SMTP) | Simple Mail Transfer Protocol | **发送**电子邮件 | 负责将邮件从客户端发送到服务器,或在邮件服务器之间传递。可通过 **STARTTLS** 升级到加密传输。 |
+| 邮局协议第 3 版(POP3) | Post Office Protocol version 3 | **接收**电子邮件 | 通常将邮件从服务器**下载到本地设备后删除服务器副本**(可配置保留)。**POP3S** 是其 SSL/TLS 加密版本。 |
+| 互联网消息访问协议(IMAP) | Internet Message Access Protocol | **接收和管理**电子邮件 | 邮件保留在服务器,支持多设备同步邮件状态、文件夹管理、在线搜索等。**IMAPS** 是其 SSL/TLS 加密版本。现代邮件服务首选。 |
+| 远程终端协议(Telnet) | Teletype Network | 远程终端登录 | **明文传输**所有数据(包括密码),安全性极差,基本已被 SSH 完全替代。 |
+| 安全外壳协议(SSH) | Secure Shell | 安全远程管理、加密数据传输 | 提供了加密的远程登录和命令执行,以及安全的文件传输(SFTP)等功能,是 Telnet 的安全替代品。 |
+
+**运行于 UDP 协议之上的协议(强调快速、低开销传输):**
+
+| 中文全称(缩写) | 英文全称 | 主要用途 | 说明与特性 |
+| ------------------------ | ------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------ |
+| 超文本传输协议(HTTP/3) | HyperText Transfer Protocol version 3 | 新一代网页传输 | 基于 **QUIC** 协议(QUIC 本身构建于 UDP 之上),旨在减少延迟、解决 TCP 队头阻塞问题,支持 0-RTT 连接建立。 |
+| 动态主机配置协议(DHCP) | Dynamic Host Configuration Protocol | 动态分配 IP 地址及网络配置 | 客户端从服务器自动获取 IP 地址、子网掩码、网关、DNS 服务器等信息。 |
+| 域名系统(DNS) | Domain Name System | 域名到 IP 地址的解析 | **通常使用 UDP** 进行快速查询。当响应数据包过大或进行区域传送(AXFR)时,会**切换到 TCP** 以保证数据完整性。 |
+| 实时传输协议(RTP) | Real-time Transport Protocol | 实时音视频数据流传输 | 常用于 VoIP、视频会议、直播等。追求低延迟,允许少量丢包。通常与 RTCP 配合使用。 |
+| RTP 控制协议(RTCP) | RTP Control Protocol | RTP 流的质量监控和控制信息 | 配合 RTP 工作,提供丢包、延迟、抖动等统计信息,辅助流量控制和拥塞管理。 |
+| 简单文件传输协议(TFTP) | Trivial File Transfer Protocol | 简化的文件传输 | 功能简单,常用于局域网内无盘工作站启动、网络设备固件升级等小文件传输场景。 |
+| 简单网络管理协议(SNMP) | Simple Network Management Protocol | 网络设备的监控与管理 | 允许网络管理员查询和修改网络设备的状态信息。 |
+| 网络时间协议(NTP) | Network Time Protocol | 同步计算机时钟 | 用于在网络中的计算机之间同步时间,确保时间的一致性。 |
**总结一下:**
-- **TCP** 更适合那些对数据**可靠性、完整性和顺序性**要求高的应用,如网页浏览 (HTTP/HTTPS)、文件传输 (FTP/SFTP)、邮件收发 (SMTP/POP3/IMAP)。
-- **UDP** 则更适用于那些对**实时性要求高、能容忍少量数据丢失**的应用,如域名解析 (DNS)、实时音视频 (RTP)、在线游戏、网络管理 (SNMP) 等。
+- **TCP** 更适合那些对数据**可靠性、完整性和顺序性**要求高的应用,如网页浏览(HTTP/HTTPS)、文件传输(FTP/SFTP)、邮件收发(SMTP/POP3/IMAP)。
+- **UDP** 则更适用于那些对**实时性要求高、能容忍少量数据丢失**的应用,如域名解析(DNS)、实时音视频(RTP)、在线游戏、网络管理(SNMP)等。
+
+### ⭐️ TCP Keepalive 和 HTTP Keep-Alive 有什么区别
+
+| 对比维度 | HTTP Keep-Alive | TCP Keepalive |
+| ----------------- | ------------------------------------------------------- | --------------------------------------------------- |
+| **所属层** | 应用层(HTTP 协议) | 传输层(TCP 协议) |
+| **解决的问题** | 复用 TCP 连接,减少重复建连、挥手、慢启动等开销 | 探测长时间空闲的 TCP 连接,对端失联后释放连接资源 |
+| **默认行为** | HTTP/1.0 默认短连接;HTTP/1.1 默认长连接 | 默认关闭,应用需要显式开启 `SO_KEEPALIVE` |
+| **控制粒度** | 由 HTTP 客户端、Web 服务器或代理按连接策略控制 | 由操作系统内核控制,也可在部分平台逐 socket 调整 |
+| **常见参数** | `Connection`、`Keep-Alive: timeout/max`、服务器超时配置 | `tcp_keepalive_time/intvl/probes` 或平台对应参数 |
+| **关闭触发** | 到达空闲超时、请求次数上限,或任意一方主动关闭 | 空闲后发探测包,多次无响应或收到 RST 才关闭 |
+| **对端在线时** | 服务端仍可按配置主动回收空闲连接 | 只要对端内核能回 ACK,连接通常继续维持 |
+| **能否替代心跳** | 不能判断业务是否健康,只能管理 HTTP 连接复用 | 不能判断应用线程池、事件循环、业务依赖是否正常 |
+| **中间层影响** | 代理、网关可独立管理前后两段 HTTP/TCP 连接 | NAT/LB/反向代理可能让你探测到的只是某一段 TCP 连接 |
+| **HTTP/2/3 关系** | HTTP/2 禁用连接级头;HTTP/3/QUIC 不使用这套机制 | 只作用于 TCP;真正的 HTTP/3/QUIC 连接不受它直接影响 |
+
+**不同 HTTP 版本里,Keep-Alive 的默认行为不一样**:
+
+
+
+如果从“谁来决定关连接”的角度看,两个机制的态度完全相反:
+
+HTTP Keep-Alive 是“主动回收”——服务器到了超时或请求次数上限,就可以按自己的配置关闭连接,不需要先探测对方是否在线。它是一种比较主动的资源回收方式。
+
+TCP Keepalive 是“被动回收”——它必须先发探测包去问“你还在吗?”。只要对方在线、能回 ACK,服务器就只能继续维持连接,刷新定时器。只有确认对方已经不在了,才能释放资源。这是一种温和的回收策略。
+
+
+
+
+
+实际项目中,两者经常同时在跑,各管各的。HTTP Keep-Alive 管的是“一条连接最多用多久、服务多少次请求”,TCP Keepalive 管的是“如果长时间没数据,检查一下对方是不是已经消失了”。两者互不干扰,也不能互相替代。
+
+详细介绍:[TCP Keepalive 和 HTTP Keep-Alive 有什么区别?](./tcp-keepalive-vs-http-keepalive.md)
-### TCP 三次握手和四次挥手(非常重要)
+### ⭐️ TCP 三次握手和四次挥手(非常重要)
**相关面试题**:
-- 为什么要三次握手?
+- 为什么要三次握手?
- 第 2 次握手传回了 ACK,为什么还要传回 SYN?
- 为什么要四次挥手?
- 为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?
- 如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?
- 为什么第四次挥手客户端需要等待 2\*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?
-**参考答案**:[TCP 三次握手和四次挥手(传输层)](./tcp-connection-and-disconnection.md) 。
+**参考答案**:[TCP 三次握手和四次挥手(传输层)](https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html)。
+
+### TCP TIME_WAIT 到底在等什么?为什么要等?
+
+**相关面试题**:
+
+1. `TIME_WAIT` 到底在等什么?
+2. `TIME_WAIT` 大量堆积会不会真的出问题?
+3. `tcp_tw_reuse` 能不能随便开?
+4. `TIME_WAIT` 和 `CLOSE_WAIT` 怎么区分?
+
+**参考答案**: [TCP TIME_WAIT 详解:为什么要等、会不会出问题、能不能复用?](./tcp-time-wait.md)。
+
+### ⭐️ TCP 如何保证传输的可靠性?(重要)
+
+[TCP 传输可靠性保障(传输层)](https://javaguide.cn/cs-basics/network/tcp-reliability-guarantee.html)
+
+### TCP 和 UDP 可以使用同一个端口吗?
+
+结论:**可以**。TCP 和 UDP 的端口绑定命名空间按传输层协议区分,同一个数字端口在不同协议下不冲突。
+
+内核收到 IP 包后,会先看 IP 层的协议标识(TCP 协议号是 `6`,UDP 是 `17`),根据协议号把报文交给对应的 TCP 或 UDP 协议栈,然后再在各自协议栈内按地址和端口分发。所以 `TCP/8080` 和 `UDP/8080` 可以共存,内核压根不会把它们当成同一条通信。
+
+
+
+真正容易冲突的是**同一协议**下的重复绑定,比如两个 TCP 服务通常不能同时监听同一个本地 IP 和端口;这时才涉及 `SO_REUSEADDR`、`SO_REUSEPORT` 这类 socket 复用选项。
+
+经典例子:DNS 同时使用 `UDP/53`(日常查询)和 `TCP/53`(响应过大、区域传送);HTTP/3 常见部署是 `UDP/443`(QUIC),可以和传统 HTTPS 的 `TCP/443` 同时存在。
+
+
+
+详细介绍:[TCP 和 UDP 可以使用同一个端口吗?](./can-tcp-and-udp-use-the-same-port.md)
+
+### ⭐️ 一台主机上只能保持最多 65535 个 TCP 连接吗?
+
+结论:**不是**。`65535` 是最大端口号,不是连接数上限。
+
+TCP 连接靠四元组区分:源 IP、源端口、目的 IP、目的端口。只要四元组不同,内核就识别为不同连接。服务端监听同一个端口时,只要客户端 IP 或客户端端口不同,连接就可以继续增加。
+
+
+
+真正限制连接数的因素:
+
+- **服务端**:主要受文件描述符、内存、CPU、网卡和应用处理能力限制,而不是端口数。
+- **客户端**:连同一个目标时,源 IP 和目的 IP:Port 都固定,只剩源端口可变,更容易撞到临时端口上限(Linux 默认约 2.8 万个)。`TIME_WAIT` 堆积会加剧这个问题。
+- **NAT 网关**:大量内网机器共享同一个公网 IP 访问同一个外部目标时,NAT 侧的公网源端口也会成为瓶颈。
-### TCP 如何保证传输的可靠性?(重要)
+生产环境最常见的坑不是端口不够,而是**连接池没配好导致短连接疯狂创建和销毁**,把临时端口耗光。排查时优先看连接池和 keep-alive 是否生效,不要一上来就改内核参数。
-[TCP 传输可靠性保障(传输层)](./tcp-reliability-guarantee.md)
+详细介绍:[一台主机上只能保持最多 65535 个 TCP 连接吗?](./maximum-number-of-tcp-connections-per-host.md)
## IP
@@ -163,9 +263,9 @@ TCP (传输控制协议) 和 UDP (用户数据报协议) 是互联网传输层
### 什么是 IP 地址?IP 寻址如何工作?
-每个连入互联网的设备或域(如计算机、服务器、路由器等)都被分配一个 **IP 地址(Internet Protocol address)**,作为唯一标识符。每个 IP 地址都是一个字符序列,如 192.168.1.1(IPv4)、2001:0db8:85a3:0000:0000:8a2e:0370:7334(IPv6) 。
+每个连入互联网的设备或域(如计算机、服务器、路由器等)都被分配一个 **IP 地址(Internet Protocol address)**,作为唯一标识符。每个 IP 地址都是一个字符序列,如 192.168.1.1(IPv4)、2001:0db8:85a3:0000:0000:8a2e:0370:7334(IPv6)。
-当网络设备发送 IP 数据包时,数据包中包含了 **源 IP 地址** 和 **目的 IP 地址** 。源 IP 地址用于标识数据包的发送方设备或域,而目的 IP 地址则用于标识数据包的接收方设备或域。这类似于一封邮件中同时包含了目的地地址和回邮地址。
+当网络设备发送 IP 数据包时,数据包中包含了 **源 IP 地址** 和 **目的 IP 地址**。源 IP 地址用于标识数据包的发送方设备或域,而目的 IP 地址则用于标识数据包的接收方设备或域。这类似于一封邮件中同时包含了目的地地址和回邮地址。
网络设备根据目的 IP 地址来判断数据包的目的地,并将数据包转发到正确的目的地网络或子网络,从而实现了设备间的通信。
@@ -179,20 +279,20 @@ TCP (传输控制协议) 和 UDP (用户数据报协议) 是互联网传输层
IP 地址过滤是一种简单的网络安全措施,实际应用中一般会结合其他网络安全措施,如认证、授权、加密等一起使用。单独使用 IP 地址过滤并不能完全保证网络的安全。
-### IPv4 和 IPv6 有什么区别?
+### ⭐️ IPv4 和 IPv6 有什么区别?
-**IPv4(Internet Protocol version 4)** 是目前广泛使用的 IP 地址版本,其格式是四组由点分隔的数字,例如:123.89.46.72。IPv4 使用 32 位地址作为其 Internet 地址,这意味着共有约 42 亿( 2^32)个可用 IP 地址。
+**IPv4(Internet Protocol version 4)** 是目前广泛使用的 IP 地址版本,其格式是四组由点分隔的数字,例如:123.89.46.72。IPv4 使用 32 位地址作为其 Internet 地址,这意味着共有约 42 亿(2^32)个可用 IP 地址。
-
+
-这么少当然不够用啦!为了解决 IP 地址耗尽的问题,最根本的办法是采用具有更大地址空间的新版本 IP 协议 - **IPv6(Internet Protocol version 6)**。IPv6 地址使用更复杂的格式,该格式使用由单或双冒号分隔的一组数字和字母,例如:2001:0db8:85a3:0000:0000:8a2e:0370:7334 。IPv6 使用 128 位互联网地址,这意味着越有 2^128(3 开头的 39 位数字,恐怖如斯) 个可用 IP 地址。
+这么少当然不够用啦!为了解决 IP 地址耗尽的问题,最根本的办法是采用具有更大地址空间的新版本 IP 协议 - **IPv6(Internet Protocol version 6)**。IPv6 地址使用更复杂的格式,该格式使用由单或双冒号分隔的一组数字和字母,例如:2001:0db8:85a3:0000:0000:8a2e:0370:7334。IPv6 使用 128 位互联网地址,这意味着越有 2^128(3 开头的 39 位数字,恐怖如斯)个可用 IP 地址。
-
+
除了更大的地址空间之外,IPv6 的优势还包括:
- **无状态地址自动配置(Stateless Address Autoconfiguration,简称 SLAAC)**:主机可以直接通过根据接口标识和网络前缀生成全局唯一的 IPv6 地址,而无需依赖 DHCP(Dynamic Host Configuration Protocol)服务器,简化了网络配置和管理。
-- **NAT(Network Address Translation,网络地址转换) 成为可选项**:IPv6 地址资源充足,可以给全球每个设备一个独立的地址。
+- **NAT(Network Address Translation,网络地址转换)成为可选项**:IPv6 地址资源充足,可以给全球每个设备一个独立的地址。
- **对标头结构进行了改进**:IPv6 标头结构相较于 IPv4 更加简化和高效,减少了处理开销,提高了网络性能。
- **可选的扩展头**:允许在 IPv6 标头中添加不同的扩展头(Extension Headers),用于实现不同类型的功能和选项。
- **ICMPv6(Internet Control Message Protocol for IPv6)**:IPv6 中的 ICMPv6 相较于 IPv4 中的 ICMP 有了一些改进,如邻居发现、路径 MTU 发现等功能的改进,从而提升了网络的可靠性和性能。
@@ -202,7 +302,7 @@ IP 地址过滤是一种简单的网络安全措施,实际应用中一般会
获取客户端真实 IP 的方法有多种,主要分为应用层方法、传输层方法和网络层方法。
-**应用层方法** :
+**应用层方法**:
通过 [X-Forwarded-For](https://en.wikipedia.org/wiki/X-Forwarded-For) 请求头获取,简单方便。不过,这种方法无法保证获取到的是真实 IP,这是因为 X-Forwarded-For 字段可能会被伪造。如果经过多个代理服务器,X-Forwarded-For 字段可能会有多个值(附带了整个请求链中的所有代理服务器 IP 地址)。并且,这种方法只适用于 HTTP 和 SMTP 协议。
@@ -214,7 +314,7 @@ IP 地址过滤是一种简单的网络安全措施,实际应用中一般会
**网络层方法**:
-隧道 +DSR 模式。这种方法可以适用于任何协议,就是实施起来会比较麻烦,也存在一定限制,实际应用中一般不会使用这种方法。
+隧道 + DSR 模式。这种方法可以适用于任何协议,就是实施起来会比较麻烦,也存在一定限制,实际应用中一般不会使用这种方法。
### NAT 的作用是什么?
@@ -224,7 +324,7 @@ NAT 不光可以缓解 IPv4 地址资源短缺的问题,还可以隐藏内部

-相关阅读:[NAT 协议详解(网络层)](./nat.md)。
+相关阅读:[NAT 协议详解(网络层)](https://javaguide.cn/cs-basics/network/nat.html)。
## ARP
@@ -232,29 +332,29 @@ NAT 不光可以缓解 IPv4 地址资源短缺的问题,还可以隐藏内部
MAC 地址的全称是 **媒体访问控制地址(Media Access Control Address)**。如果说,互联网中每一个资源都由 IP 地址唯一标识(IP 协议内容),那么一切网络设备都由 MAC 地址唯一标识。
-
+
可以理解为,MAC 地址是一个网络设备真正的身份证号,IP 地址只是一种不重复的定位方式(比如说住在某省某市某街道的张三,这种逻辑定位是 IP 地址,他的身份证号才是他的 MAC 地址),也可以理解为 MAC 地址是身份证号,IP 地址是邮政地址。MAC 地址也有一些别称,如 LAN 地址、物理地址、以太网地址等。
> 还有一点要知道的是,不仅仅是网络资源才有 IP 地址,网络设备也有 IP 地址,比如路由器。但从结构上说,路由器等网络设备的作用是组成一个网络,而且通常是内网,所以它们使用的 IP 地址通常是内网 IP,内网的设备在与内网以外的设备进行通信时,需要用到 NAT 协议。
-MAC 地址的长度为 6 字节(48 比特),地址空间大小有 280 万亿之多( $2^{48}$ ),MAC 地址由 IEEE 统一管理与分配,理论上,一个网络设备中的网卡上的 MAC 地址是永久的。不同的网卡生产商从 IEEE 那里购买自己的 MAC 地址空间(MAC 的前 24 比特),也就是前 24 比特由 IEEE 统一管理,保证不会重复。而后 24 比特,由各家生产商自己管理,同样保证生产的两块网卡的 MAC 地址不会重复。
+MAC 地址的长度为 6 字节(48 比特),地址空间大小有 280 万亿之多($2^{48}$),MAC 地址由 IEEE 统一管理与分配,理论上,一个网络设备中的网卡上的 MAC 地址是永久的。不同的网卡生产商从 IEEE 那里购买自己的 MAC 地址空间(MAC 的前 24 比特),也就是前 24 比特由 IEEE 统一管理,保证不会重复。而后 24 比特,由各家生产商自己管理,同样保证生产的两块网卡的 MAC 地址不会重复。
MAC 地址具有可携带性、永久性,身份证号永久地标识一个人的身份,不论他到哪里都不会改变。而 IP 地址不具有这些性质,当一台设备更换了网络,它的 IP 地址也就可能发生改变,也就是它在互联网中的定位发生了变化。
最后,记住,MAC 地址有一个特殊地址:FF-FF-FF-FF-FF-FF(全 1 地址),该地址表示广播地址。
-### ARP 协议解决了什么问题?
+### ⭐️ ARP 协议解决了什么问题?
ARP 协议,全称 **地址解析协议(Address Resolution Protocol)**,它解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但 IP 地址属于逻辑地址,而 MAC 地址才是物理地址,ARP 协议解决了 IP 地址转 MAC 地址的一些问题。
### ARP 协议的工作原理?
-[ARP 协议详解(网络层)](./arp.md)
+[ARP 协议详解(网络层)](https://javaguide.cn/cs-basics/network/arp.html)
## 复习建议
-非常推荐大家看一下 《图解 HTTP》 这本书,这本书页数不多,但是内容很是充实,不管是用来系统的掌握网络方面的一些知识还是说纯粹为了应付面试都有很大帮助。下面的一些文章只是参考。大二学习这门课程的时候,我们使用的教材是 《计算机网络第七版》(谢希仁编著),不推荐大家看这本教材,书非常厚而且知识偏理论,不确定大家能不能心平气和的读完。
+非常推荐大家看一下 《图解 HTTP》这本书,这本书页数不多,但是内容很是充实,不管是用来系统的掌握网络方面的一些知识还是说纯粹为了应付面试都有很大帮助。下面的一些文章只是参考。大二学习这门课程的时候,我们使用的教材是 《计算机网络第七版》(谢希仁编著),不推荐大家看这本教材,书非常厚而且知识偏理论,不确定大家能不能心平气和的读完。
## 参考
diff --git a/docs/cs-basics/network/tcp-byte-stream-udp-datagram.md b/docs/cs-basics/network/tcp-byte-stream-udp-datagram.md
new file mode 100644
index 00000000000..f19ef68b3dd
--- /dev/null
+++ b/docs/cs-basics/network/tcp-byte-stream-udp-datagram.md
@@ -0,0 +1,164 @@
+---
+title: 为什么 TCP 是面向字节流,UDP 是面向报文?(传输层)
+description: 讲清 TCP 字节流与 UDP 报文的本质差异,解析粘包/拆包成因与解决方案,覆盖 Nagle、Delayed ACK 等常见面试考点。
+category: 计算机基础
+tag:
+ - 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP,UDP,字节流,报文,粘包,拆包,消息边界,Nagle,Delayed ACK,TCP_NODELAY
+---
+
+前面说 TCP 是面向字节流,UDP 是面向报文。这个点看起来像一句定义,但很多粘包、拆包问题,其实都藏在这里。
+
+先说结论:**TCP 只保证字节可靠、有序地到达,不保证应用层消息边界;UDP 会保留应用层交给它的报文边界。**
+
+这篇文章主要回答几个问题:
+
+1. 为什么说 TCP 是面向字节流,UDP 是面向报文?
+2. TCP 粘包、拆包到底是怎么产生的?
+3. 应用层应该如何定义消息边界?
+4. Nagle 算法和 Delayed ACK 为什么可能让小包变慢?
+
+举个例子,应用层连续发送两条消息:
+
+```
+消息 1:hello
+消息 2:world
+```
+
+如果用 UDP 发送,通常会对应两个 UDP 数据报。接收方调用 `recvfrom()` 时,也是按数据报来读:一次读取一个 UDP 报文,不会把两次发送的报文合成一个流。UDP 的接收队列里,一个元素就是一个数据报,消息边界天然保留了下来。
+
+不过这里也有一个细节:UDP 保留的是传输层报文边界,不代表它适合发送任意大的消息。数据报太大时,底层 IP 层仍可能分片;接收端缓冲区太小时,也可能出现截断。所以 UDP 的“面向报文”不是“随便发多大都没事”,而是说它不会像 TCP 那样把应用数据抽象成一条连续字节流。RFC 768 对 UDP 的定义就是 datagram mode,并说明它提供的是最小协议机制,不保证可靠交付和去重。
+
+如果用 TCP 发送,就不能这么理解。应用层调用两次 `send()`,只是把两段字节写进内核发送缓冲区。至于这些字节什么时候发、合成几个 TCP 段发、对端一次 `recv()` 能读到多少,都不是由这两次 `send()` 直接决定的。
+
+比如,接收端可能一次读到(粘包):
+
+```
+helloworld
+```
+
+也可能分几次读到(拆包):
+
+```
+hel
+lowor
+ld
+```
+
+这不是 TCP 出错,而是 TCP 的工作方式本来就是这样。TCP 处理的是连续字节流,它只关心这些字节是否可靠、有序地到达,不关心应用层定义的“第几条消息”从哪里开始、到哪里结束。RFC 9293 也明确提到,TCP segment 和应用层 `send()` / socket write 的边界通常不是一一对应的,TCP 不保证应用读写缓冲区边界和网络分段边界相关。
+
+
+
+所以,“TCP 粘包/拆包”这个说法更像是应用层视角下的现象。严格来说,TCP 没有“包”的概念,它传的是连续字节流。真正需要解决的是:**应用层协议如何定义消息边界**。
+
+#### 为什么会出现粘包和拆包?
+
+常见原因有这几个。
+
+**1. TCP 是字节流协议,没有应用层消息边界。**
+
+TCP 负责把字节可靠、有序地送到对端,但不会记录“这 20 个字节是第一条消息,那 30 个字节是第二条消息”。
+
+**2. 一次 `send()` 不等于一次网络发送。**
+
+`send()` 成功通常只表示数据从应用进程拷贝到了内核发送缓冲区。至于什么时候真正发出去、拆成几个 TCP 段发,要看 MSS、发送窗口、拥塞窗口、Nagle 算法、网卡队列等因素。
+
+**3. 一次 `recv()` 也不等于读到一条完整消息。**
+
+接收端只是从 TCP 接收缓冲区取字节。缓冲区里可能已经堆了多条消息,也可能只有半条消息。`recv()` 只会把当前可读的数据拷贝给应用,不会帮你按业务消息切分。
+
+**4. 小包优化可能改变发送时机。**
+
+Nagle 算法、Delayed ACK、Linux 自动合并小写入等机制,都可能影响小数据的发送时机。比如 Linux 从 3.14 开始有 `tcp_autocorking`,内核会尽量合并连续的小写入,减少发送包数量;应用也可以用 `TCP_CORK` 明确控制何时“拔塞”发送。
+
+这也是为什么在 Netty、Dubbo、自定义 RPC、IM 网关、游戏服务里,协议编解码都很重要。只要底层用的是 TCP,就必须在应用层定义清楚消息边界。
+
+
+
+#### 怎么解决 TCP 粘包/拆包?
+
+核心思路只有一个:**让接收方知道一条消息到哪里结束。**
+
+
+
+常见做法有三种。
+
+**1. 固定长度**
+
+规定每条消息都是固定长度,比如 64 字节。接收方每读满 64 字节,就认为读到了一条完整消息。
+
+这种方式实现简单,但灵活性差。消息短了要补齐,浪费空间;消息长了又要额外拆分。它适合消息格式非常固定的场景,不太适合通用业务协议。
+
+**2. 分隔符**
+
+在消息之间加特殊分隔符,比如换行符 `\n`、`\r\n`,或者自定义结束标记。
+
+```
+hello\n
+world\n
+```
+
+接收方不断从缓冲区读数据,只要遇到分隔符,就切出一条完整消息。很多文本协议都会用类似思路。
+
+这种方式直观,但要注意两个问题:第一,分隔符可能刚好出现在消息体里,这时需要转义;第二,分隔符本身也可能被拆在两次读取里,所以接收端解析时不能假设一次 `recv()` 就能读到完整分隔符。
+
+**3. 长度头**
+
+这是工程里更常见的一种方式。协议头里固定放一个长度字段,表示后面的消息体有多少字节。
+
+```
+| 4 字节长度 | 消息体 |
+```
+
+接收方先读固定长度的协议头,解析出消息体长度,再继续读取指定字节数。只要没有读满,就继续等待;如果读多了,就把多出来的字节留在缓冲区,作为下一条消息的开头。
+
+很多二进制协议、RPC 协议都会用这种方式。实际设计时,协议头里通常不只放长度,还会放魔数、版本号、消息类型、序列号、序列化方式等字段。
+
+长度头方案也有坑。长度字段要约定字节序,通常使用网络字节序;还要限制最大包体长度,避免对端传一个特别大的长度值,把内存撑爆。线上做协议解析时,不能只考虑正常路径,还要处理半包、异常长度、连接中途关闭、恶意构造请求等情况。
+
+#### Nagle 算法和 Delayed ACK 为什么会让小包变慢?
+
+讲粘包时,经常会顺带问到 Nagle 算法。
+
+Nagle 算法的目标是减少小包数量。早期网络带宽有限,如果应用每次只写 1 个字节,TCP/IP 头部却有几十个字节,网络里就会充满“小包”,效率很低。RFC 896 讨论的就是这类 small-packet problem,并提出当连接上还有未确认数据时,新的小数据可以先暂缓发送,等 ACK 到来后再继续发送。
+
+Delayed ACK 是接收端的优化。接收端收到数据后,不一定立刻发 ACK,而是等一小段时间,看能不能把 ACK 和要返回的数据一起发出去,减少纯 ACK 包数量。RFC 9293 也把这种“少于每个数据段一个 ACK”的策略称为 delayed ACK。
+
+这两个机制单独看都有道理,放在一起就可能放大延迟。典型场景是:
+
+```
+客户端 write 小数据 A
+客户端马上 write 小数据 B
+客户端等待服务端响应
+```
+
+
+
+小数据 A 发出去了,小数据 B 可能因为 Nagle 算法暂存在发送缓冲区里,等待 A 的 ACK。服务端收到 A 后,如果暂时没有业务响应要返回,Delayed ACK 又可能延迟发送 ACK。于是发送端等 ACK,接收端等更多数据或等延迟确认定时器,延迟就被放大了。
+
+这类问题在短小 RPC、交互式协议、游戏同步、远程终端里更容易被感知。
+
+解决思路不是“无脑关 Nagle”。更稳的做法是:
+
+- 能合并的小写入,在应用层先合并成一次完整消息,再调用一次 `write()`。
+- 请求/响应模型里,尽量避免连续多次小 `write()` 后马上等待响应。
+- 对延迟敏感、消息很小的连接,可以评估开启 `TCP_NODELAY`,让小数据尽快发送。
+- 对吞吐优先、希望攒够数据再发的场景,可以在 Linux 上评估 `TCP_CORK`,但它不适合写跨平台代码。
+- 调参前先抓包确认,不要看到“慢”就直接改 socket 选项。
+
+在 Java 里,很多网络框架都会暴露 `TCP_NODELAY` 配置,例如 Netty 的 `ChannelOption.TCP_NODELAY`。它确实能降低小消息的等待时间,但也可能增加小包数量。对高 QPS 服务来说,这个 trade-off 要结合消息大小、RTT、吞吐、CPU 和网卡包量一起看。Linux `tcp(7)` 也说明,`TCP_NODELAY` 会关闭 Nagle 算法,而 `TCP_CORK` 则用于避免发送不完整帧、等应用确认“可以发了”再发送。
+
+#### 面试时怎么回答?
+
+可以这么回答:
+
+TCP 是面向字节流的。应用层写入的数据会进入内核缓冲区,TCP 只保证这些字节可靠、有序地到达对端,不保证一次 `send()` 对应一次 `recv()`,也不保留应用层消息边界。因此接收方可能一次读到多条消息,也可能只读到半条消息,这就是常说的粘包、拆包现象。
+
+UDP 是面向报文的。应用层交给 UDP 的一次数据会作为一个 UDP 数据报发送,接收端也是按数据报读取,所以天然保留消息边界。不过 UDP 不保证可靠到达,也不保证顺序。
+
+解决 TCP 粘包/拆包,本质是应用层协议自己定义消息边界。常见方案有固定长度、分隔符、长度头。工程里更常用长度头,因为它对二进制协议和变长消息更友好,但要处理字节序、最大长度限制、半包缓存和异常连接关闭等问题。
+
+
diff --git a/docs/cs-basics/network/tcp-connection-and-disconnection.md b/docs/cs-basics/network/tcp-connection-and-disconnection.md
index 63bc97f82c9..ae7ae9b578c 100644
--- a/docs/cs-basics/network/tcp-connection-and-disconnection.md
+++ b/docs/cs-basics/network/tcp-connection-and-disconnection.md
@@ -1,103 +1,347 @@
---
title: TCP 三次握手和四次挥手(传输层)
+description: 一文讲清 TCP 三次握手与四次挥手:SEQ/ACK/SYN/FIN 如何同步,TIME_WAIT 与 2MSL 的原因,半连接队列(SYN Queue)与全连接队列(Accept Queue)的工作机制,以及 backlog/somaxconn/syncookies 在高并发与 SYN Flood 下的影响。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP,三次握手,四次挥手,三次握手为什么,四次挥手为什么,TIME_WAIT,CLOSE_WAIT,2MSL,状态机,SEQ,ACK,SYN,FIN,RST,半连接队列,全连接队列,SYN队列,Accept队列,backlog,somaxconn,SYN Flood,syncookies
---
-为了准确无误地把数据送达目标处,TCP 协议采用了三次握手策略。
+TCP 三次握手和四次挥手很容易被背成一张流程图:客户端发 `SYN`,服务端回 `SYN+ACK`,最后再来一个 `ACK`;关闭连接时,再按 `FIN`、`ACK`、`FIN`、`ACK` 走一遍。
-## 建立连接-TCP 三次握手
+但真正排查网络问题、看抓包或者聊面试题时,只记顺序往往不够。比如:为什么建立连接不是两次握手?服务端收到第三次握手之后,连接到底放在哪个队列?四次挥手中的 ACK 和 FIN 为什么通常分开发?又在什么条件下能合并成三次挥手?
+
+这篇文章就围绕 TCP 连接的建立和释放,把这些问题串起来讲清楚:
+
+1. TCP 三次握手每一步分别做了什么?
+2. 为什么建立连接需要三次握手,而不是两次或四次?
+3. 半连接队列和全连接队列分别保存什么?
+4. TCP 四次挥手每一步分别做了什么?
+5. `TIME_WAIT`、`CLOSE_WAIT`、三次挥手这些细节该怎么理解?
+
+> **术语约定**:本文正文统一使用 `SYN_RCVD`、`TIME_WAIT` 这类下划线写法;RFC 中常写作 `SYN-RECEIVED`、`TIME-WAIT`,Linux `ss` 命令中常显示为 `syn-recv`、`time-wait`。它们指向的是同一类 TCP 状态,只是不同语境下的写法不同。
+
+## 建立连接:TCP 三次握手

-建立一个 TCP 连接需要“三次握手”,缺一不可:
+在最常见的“一端主动发起连接、一端被动监听”的场景下,TCP 连接通常通过三次握手建立:
-- **一次握手**:客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 **SYN_SEND** 状态,等待服务端的确认;
-- **二次握手**:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 –> 客户端,然后服务端进入 **SYN_RECV** 状态;
-- **三次握手**:客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务端都进入**ESTABLISHED** 状态,完成 TCP 三次握手。
+1. **第一次握手(SYN)**:客户端向服务端发送一个 SYN(Synchronize Sequence Numbers)报文段,其中包含客户端生成的初始序列号(Initial Sequence Number,ISN),例如 `seq=x`。发送后,客户端进入 `SYN_SENT` 状态,等待服务端确认。
+2. **第二次握手(SYN+ACK)**:服务端收到 SYN 后,如果同意建立连接,会回复一个 SYN+ACK 报文段。这个报文段包含两个关键信息:
+ - **SYN**:服务端也需要同步自己的初始序列号,因此会携带服务端生成的 ISN,例如 `seq=y`。
+ - **ACK**:用于确认收到客户端的 SYN,确认号设置为客户端初始序列号加一,即 `ack=x+1`。
+ - 发送该报文段后,服务端进入 `SYN_RCVD` 状态。
+3. **第三次握手(ACK)**:客户端收到服务端的 SYN+ACK 后,会向服务端发送最终确认报文段。由于客户端的 SYN 会消耗一个序列号,因此这个 ACK 报文段的序列号通常为 `seq=x+1`;它用于确认服务端的 SYN,确认号为 `ack=y+1`。发送后,客户端进入 `ESTABLISHED` 状态。服务端收到这个 ACK 后,也进入 `ESTABLISHED` 状态。
-当建立了 3 次握手之后,客户端和服务端就可以传输数据啦!
+至此,双方完成初始序列号同步,并确认这条连接可以开始双向传输数据。
### 什么是半连接队列和全连接队列?
-在 TCP 三次握手过程中,Linux 内核会维护两个队列来管理连接请求:
+```mermaid
+sequenceDiagram
+ autonumber
+ participant C as 客户端 Client
+ participant K as 服务端内核 TCP
+ box 服务端内核队列
+ participant SQ as 半连接队列 SYN queue
+ participant AQ as 全连接队列 Accept queue
+ end
+ participant App as 用户态应用 Server app
+
+ C->>K: SYN
+ K-->>C: SYN+ACK
+ Note over SQ: 内核为该连接创建请求条目
连接状态 SYN_RCVD
放入 SYN queue
+
+ C->>K: ACK 第三次握手
+ Note over SQ,AQ: 内核收到 ACK 后完成握手
将连接从 SYN queue 迁移到 Accept queue
队列未满才可进入
+ Note over AQ: 连接已完成 可被 accept
连接状态 ESTABLISHED
+
+ App->>K: accept
+ K-->>App: 返回已就绪的 socket
+ Note over AQ: 该连接从 Accept queue 移除
+```
+
+在 TCP 三次握手过程中,服务端内核通常会用两个队列来管理连接请求。下面以常见 Linux 行为为例,不同操作系统、内核版本、socket 选项和部署环境可能会有细节差异。
+
+1. **半连接队列(SYN Queue)**:
+ - 保存“握手未完成”的请求。服务端收到 SYN 并回复 SYN+ACK 后,连接进入 `SYN_RCVD`,等待客户端最终 ACK。
+ - 如果一直收不到 ACK,内核会按重传策略重发 SYN+ACK,最终超时清理。
+ - 常见相关参数包括 `net.ipv4.tcp_max_syn_backlog`。在 SYN Flood 场景下,还会涉及 `net.ipv4.tcp_syncookies`。
+2. **全连接队列(Accept Queue)**:
+
+ - 保存“握手已完成但应用还没有 accept”的连接。服务端收到最终 ACK 后,连接变为 `ESTABLISHED`,并进入全连接队列,等待应用层 `accept()` 取走。
+ - 队列容量受 `listen(fd, backlog)` 和系统上限 `net.core.somaxconn` 共同影响。实践中常见有效上限可以近似理解为 `min(backlog, somaxconn)`,具体行为仍要看内核版本和应用配置。
+
+总结一下:
+
+| 队列 | 作用 | 状态 | 移出条件 |
+| -------------------------- | -------------------------------------- | ------------- | ------------------------ |
+| 半连接队列(SYN Queue) | 保存未完成握手的连接 | `SYN_RCVD` | 收到 ACK / 超时重传失败 |
+| 全连接队列(Accept Queue) | 保存已完成握手、等待应用 accept 的连接 | `ESTABLISHED` | 被应用层 `accept()` 取出 |
+
+当全连接队列满时,`net.ipv4.tcp_abort_on_overflow` 会影响处理策略:
+
+- `0`(默认):Linux 通常不会立即返回 RST,而可能丢弃第三次握手 ACK,使服务端继续停留在握手未完全完成的状态,并重传 SYN+ACK。客户端发出第三次 ACK 后,通常已经认为 `connect()` 成功;但服务端并没有把这个连接放进全连接队列,所以客户端后续发送数据时可能迟迟得不到正常响应,最终表现为首包阻塞、读超时或重试。
+- `1`:直接对客户端回复 `RST`,让连接快速失败。
+
+排查时可以用 `ss -ltn` 看监听 socket。对于 `LISTEN` 状态,`Recv-Q` 通常表示当前 backlog 中等待应用 accept 的连接数,`Send-Q` 表示 socket backlog 上限。如果 `Recv-Q` 长时间接近 `Send-Q`,就要重点怀疑应用 accept 不及时、backlog 偏小、线程池卡住、GC 抖动或者短时间连接突刺。
+
+当半连接队列满时,如果 `tcp_syncookies=1`,Linux 会在 SYN backlog 溢出时启用 SYN Cookie:服务端把必要信息编码进返回的 SYN+ACK 中,而不是为每个请求都保留完整的半连接状态。也就是说,SYN Cookie 生效时,服务端不会为这个 SYN 在半连接队列中分配常规状态;只有收到合法的最终 ACK 后,内核才会校验 cookie,并重建连接所需的信息。
+
+但 SYN Cookie 是防护手段,不是扩容手段。它能缓解 SYN Flood 对半连接队列的冲击,但仍会消耗 CPU;如果攻击流量已经打满带宽,SYN Cookie 也无法从根本上恢复可用性。另外,SYN Cookie 模式下部分 TCP 扩展能力可能受限,在高延迟、高带宽链路下可能出现性能退化。`tcp_syncookies=2` 更偏测试用途,不建议作为生产环境默认配置。
+
+### 为什么要三次握手?
+
+TCP 三次握手主要做两件事:**同步双方的初始序列号**,并且**确认双方的收发路径是可用的**。真正的数据可靠交付,还要依赖后续传输过程中的确认、重传、窗口控制和拥塞控制。
-1. **半连接队列**(也称 SYN Queue):当服务端收到客户端的 SYN 请求时,此时双方还没有完全建立连接,它会把半连接状态的连接放在半连接队列。
-2. **全连接队列**(也称 Accept Queue):当服务端收到客户端对 ACK 响应时,意味着三次握手成功完成,服务端会将该连接从半连接队列移动到全连接队列。如果未收到客户端的 ACK 响应,会进行重传,重传的等待时间通常是指数增长的。如果重传次数超过系统规定的最大重传次数,系统将从半连接队列中删除该连接信息。
+#### 1. 确认双方收发能力,并同步初始序列号
-这两个队列的存在是为了处理并发连接请求,确保服务端能够有效地管理新的连接请求。另外,新的连接请求被拒绝或忽略除了和每个队列的大小限制有关系之外,还和很多其他因素有关系,这里就不详细介绍了,整体逻辑比较复杂。
+```mermaid
+sequenceDiagram
+ autonumber
+ participant C as 客户端 Client
+ participant S as 服务端 Server
-### 为什么要三次握手?
+ Note over C,S: 目标 同步双方 ISN 并确认双向可达
-三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
+ C->>S: SYN seq=ISN_C
+ Note right of S: 服务端知道 C→S 方向可达
客户端能发 服务端能收
+ Note right of S: 服务端状态 SYN_RCVD
-1. **第一次握手**:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
-2. **第二次握手**:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
-3. **第三次握手**:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常
+ S->>C: SYN+ACK seq=ISN_S ack=ISN_C+1
+ Note left of C: 客户端知道 S→C 方向可达
也知道服务端收到了自己的 SYN
-三次握手就能确认双方收发功能都正常,缺一不可。
+ C->>S: ACK seq=ISN_C+1 ack=ISN_S+1
+ Note left of C: 客户端状态 ESTABLISHED
+ Note right of S: 服务端知道客户端收到了 SYN+ACK
握手闭环 双方 ISN 同步完成
+ Note right of S: 服务端状态 ESTABLISHED
-更详细的解答可以看这个:[TCP 为什么是三次握手,而不是两次或四次? - 车小胖的回答 - 知乎](https://www.zhihu.com/question/24853633/answer/115173386) 。
+ Note over C,S: 连接建立 可以开始传输数据
+```
-### 第 2 次握手传回了 ACK,为什么还要传回 SYN?
+TCP 依赖序列号(SEQ)和确认号(ACK)来保证数据有序、去重和重传。三次握手通过交换并确认双方的 ISN,让两端对“从哪个序号开始收发数据”达成一致,同时避免只凭单向信息就进入已建立状态。
-服务端传回发送端所发送的 ACK 是为了告诉客户端:“我接收到的信息确实就是你所发送的信号了”,这表明从客户端到服务端的通信是正常的。回传 SYN 则是为了建立并确认从服务端到客户端的通信。
+可以用下面这张表来记:
-> SYN 同步序列编号(Synchronize Sequence Numbers) 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务端之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务端使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement)消息响应。这样在客户机和服务端之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务端之间传递。
+| 步骤 | 报文 | 能确认什么 |
+| ---- | ------------ | ---------------------------------------------------------------------- |
+| 1 | C→S:SYN | 服务端知道:客户端能发,服务端能收,C→S 方向可达 |
+| 2 | S→C:SYN+ACK | 客户端知道:服务端能发,客户端能收;同时确认服务端收到了自己的 SYN |
+| 3 | C→S:ACK | 服务端知道:客户端收到了 SYN+ACK,S→C 方向也被服务端确认;至此握手闭环 |
+
+注意:第 2 步完成时,只有客户端确认了双向可达;服务端此时还不知道自己发出的 SYN+ACK 是否被客户端收到。服务端只有收到第 3 次握手的 ACK 后,才真正确认这个闭环,这也是两次握手不够的核心原因。
+
+#### 2. 防止已失效的连接请求被错误建立
+
+```mermaid
+sequenceDiagram
+ participant C as 客户端 Client
+ participant S as 服务端 Server
+
+ Note over C,S: 场景 旧的 SYN 报文在网络中滞留
+
+ C->>S: 1. 发送 SYN 旧请求 滞留中
+ Note over C: 客户端超时 放弃该请求
+
+ C->>S: 2. 发送 SYN 新请求
+ S-->>C: 3. 建立连接并正常释放
+
+ rect rgb(255, 240, 240)
+ Note right of S: 此时旧 SYN 终于到达服务端
+ S->>C: 4. 发送 SYN+ACK 针对旧请求
+
+ alt 如果是两次握手
+ Note right of S: 假设服务端回复 SYN+ACK 后
就认为连接建立
+ Note right of S: 错误建立连接
分配资源 造成浪费
+ else 如果是三次握手
+ Note left of C: 客户端无该连接状态
或认为这是非期望报文
+ C->>S: 5. 发送 RST 或直接丢弃
+ Note right of S: 收到 RST 立即清理
或等不到 ACK 后超时清理
+ end
+ end
+```
+
+设想一个场景:客户端发送的第一个连接请求 SYN1 因网络延迟而滞留。客户端超时后,重新发送 SYN2,并成功建立连接,数据传输完毕后连接也释放了。此时,延迟的 SYN1 才到达服务端。
+
+- **如果是两次握手**:服务端收到这个失效的 SYN1 后,可能误认为这是一个新的连接请求,并立即分配资源、建立连接。但客户端已经没有这个连接意图,不会继续配合传输,服务端就会单方面维持一个无效连接。
+- **有了第三次握手**:服务端收到失效的 SYN1 并回复 SYN+ACK 后,还要等待客户端最终 ACK。由于客户端当前没有这个连接状态,它可能直接丢弃,也可能发送 RST。服务端收不到合法 ACK,最终就会清理这个错误连接。
+
+所以,三次握手不是“多发一次包而已”,它让连接建立过程形成闭环,避免网络中的延迟、重复历史请求干扰新的连接。
+
+### 第 2 次握手已经传回 ACK,为什么还要传回 SYN?
+
+第二次握手里的 ACK 是为了确认“服务端收到了客户端的 SYN”,也就是确认 C→S 方向的请求已经到达。
+
+同时携带 SYN,是因为服务端也需要把自己的 ISN 同步给客户端,并要求客户端确认。只有双方的 ISN 都完成同步,后续可靠传输才有共同的序列号起点。
+
+简言之:ACK 表示“我收到了你的 SYN”,SYN 表示“我也要同步我的初始序列号,请你确认”。
+
+> SYN(Synchronize Sequence Numbers)是 TCP 建立连接时使用的同步信号。客户端先发送 SYN,服务端使用 SYN+ACK 应答,最后客户端再用 ACK 确认。这样双方才能完成初始序列号同步,建立一条可用于可靠数据传输的 TCP 连接。
### 三次握手过程中可以携带数据吗?
-在 TCP 三次握手过程中,第三次握手是可以携带数据的(客户端发送完 ACK 确认包之后就进入 ESTABLISHED 状态了),这一点在 RFC 793 文档中有提到。也就是说,一旦完成了前两次握手,TCP 协议允许数据在第三次握手时开始传输。
+普通 TCP 中,第三次握手的 ACK 可以携带数据。RFC 9293 也允许连接同步阶段出现携带数据的报文,但接收端在确认数据有效前,不能把这部分数据交付给应用;通常需要等连接进入 `ESTABLISHED` 后,应用层才能读到这些数据。
-如果第三次握手的 ACK 确认包丢失,但是客户端已经开始发送携带数据的包,那么服务端在收到这个携带数据的包时,如果该包中包含了 ACK 标记,服务端会将其视为有效的第三次握手确认。这样,连接就被认为是建立的,服务端会处理该数据包,并继续正常的数据传输流程。
+如果第三次握手的 ACK 丢失,但客户端随后发送了一个携带数据且带 ACK 标志的报文,服务端收到后可以把它视为有效的第三次握手确认。连接被认为建立后,服务端再继续处理该数据。
-## 断开连接-TCP 四次挥手
+需要注意,这和 TCP Fast Open(TFO)不是一回事。TFO 讨论的是第一次 SYN 就携带应用数据,需要客户端、服务端和系统配置共同支持,不是普通 TCP 默认行为。
+
+## 断开连接:TCP 四次挥手

-断开一个 TCP 连接则需要“四次挥手”,缺一不可:
+TCP 是全双工通信,两端的发送方向彼此独立。关闭连接时,通常需要两个方向分别完成“我不发了”和“我确认你不发了”的过程,所以逻辑上常被讲成“四次挥手”。
+
+不过要注意:四次挥手说的是逻辑动作,不一定意味着抓包时总能看到 4 个独立报文段。在某些场景下,ACK 和 FIN 可以合并在同一个报文段里。
-1. **第一次挥手**:客户端发送一个 FIN(SEQ=x) 标志的数据包->服务端,用来关闭客户端到服务端的数据传送。然后客户端进入 **FIN-WAIT-1** 状态。
-2. **第二次挥手**:服务端收到这个 FIN(SEQ=X) 标志的数据包,它发送一个 ACK (ACK=x+1)标志的数据包->客户端 。然后服务端进入 **CLOSE-WAIT** 状态,客户端进入 **FIN-WAIT-2** 状态。
-3. **第三次挥手**:服务端发送一个 FIN (SEQ=y)标志的数据包->客户端,请求关闭连接,然后服务端进入 **LAST-ACK** 状态。
-4. **第四次挥手**:客户端发送 ACK (ACK=y+1)标志的数据包->服务端,然后客户端进入**TIME-WAIT**状态,服务端在收到 ACK (ACK=y+1)标志的数据包后进入 CLOSE 状态。此时如果客户端等待 **2MSL** 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。
+典型流程如下:
-**只要四次挥手没有结束,客户端和服务端就可以继续传输数据!**
+1. **第一次挥手(FIN)**:客户端,或者任意一方,决定关闭自己的发送方向时,会发送一个 FIN 报文段,表示自己已经没有数据要发送了。该报文段包含一个序列号,例如 `seq=u`。发送后,主动关闭方进入 `FIN_WAIT_1` 状态。
+2. **第二次挥手(ACK)**:服务端收到 FIN 后,会回复 ACK,确认号为 `ack=u+1`。发送后,服务端进入 `CLOSE_WAIT` 状态。客户端收到 ACK 后,进入 `FIN_WAIT_2` 状态。此时连接处于**半关闭(Half-Close)**状态:客户端到服务端的发送方向已关闭,但服务端仍然可以继续向客户端发送剩余数据。
+3. **第三次挥手(FIN)**:当服务端确认剩余数据都发送完毕后,也会发送 FIN,表示自己也准备关闭发送方向。该报文段同样包含一个序列号,例如 `seq=v`;通常也会继续携带当前确认号,例如 `ack=u+1`。发送后,服务端进入 `LAST_ACK` 状态,等待客户端最终确认。
+4. **第四次挥手(ACK)**:客户端收到服务端的 FIN 后,回复最终 ACK,确认号为 `ack=v+1`。发送后,客户端进入 `TIME_WAIT` 状态。服务端收到这个 ACK 后进入 `CLOSED`。客户端则在 `TIME_WAIT` 状态等待 2MSL 后,最终进入 `CLOSED`。
+
+这里为了方便理解,用客户端发起关闭作为例子。实际中谁主动关闭连接,谁就会进入 `TIME_WAIT`,这和“客户端 / 服务端”的角色没有必然关系。
+
+> 注意区分:**半关闭(Half-Close)** 指一个方向已经发送 FIN,另一个方向仍可继续发送数据;**半开连接(Half-Open Connection)** 通常指一端崩溃、重启或状态丢失后,另一端仍以为连接存在。两者不是同一个概念。
+
+TCP 连接建立与关闭的常见状态迁移路径如下。图中省略了同时打开、同时关闭、RST、CLOSING 等少见或异常分支。
+
+
### 为什么要四次挥手?
-TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
+因为 TCP 是全双工的。A 不想发了,不代表 B 也立刻没有数据要发。
+
+举个例子,A 和 B 打电话,通话即将结束:
+
+1. A 说:“我没什么要说的了。”(A 发 FIN)
+2. B 回答:“我知道了。”但 B 可能还有话要说。(B 回 ACK)
+3. B 继续说完剩下的话,最后说:“我也说完了。”(B 发 FIN)
+4. A 回答:“知道了。”(A 回 ACK)
+
+这对应到 TCP 中,就是两个方向分别关闭、分别确认。
+
+### 为什么通常不能把服务端发送的 ACK 和 FIN 合并起来,变成三次挥手?
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant C as 客户端
+ participant K as 服务端内核
+ participant A as 服务端应用
+
+ Note over C,K: 客户端发起关闭
+ C->>K: FIN
+ Note right of K: 内核回复 ACK
用于确认对端 FIN
+ K-->>C: ACK
+ Note right of K: 服务端状态变为 CLOSE_WAIT
+
+ Note over K,A: 应用处理阶段
+ K->>A: 通知本端应用
对端已关闭发送方向 例如 read 返回 0
+ A->>A: 读取和处理剩余数据
+ A->>A: 发送最后响应
+ A->>K: 调用 close 或 shutdown
+
+ Note right of K: 发送本端 FIN
并进入 LAST_ACK
+ K-->>C: FIN
+ Note left of C: 客户端回复 ACK
并进入 TIME_WAIT
+ C->>K: ACK
+ Note right of K: 服务端收到最终 ACK
进入 CLOSED
+```
+
+关键原因是:**回复 ACK** 和 **发送 FIN** 的触发时机通常不同。
+
+- 当服务端收到客户端 FIN 时,内核协议栈需要回复 ACK,确认“我收到了你要关闭发送方向的请求”。此时服务端进入 `CLOSE_WAIT`,等待本端应用处理剩余数据。
+- 只有当服务端应用处理完毕,并调用 `close()` 或 `shutdown()` 后,内核才会发送本端 FIN。
+- 因此,“内核自动回 ACK”和“应用决定发 FIN”在时间上是解耦的,通常无法合并。只有在服务端恰好也准备立即关闭时,才可能出现 FIN+ACK 合并在一个报文段中的情况。
+
+### CLOSE_WAIT 为什么会堆积?
+
+`CLOSE_WAIT` 是被动关闭方收到 FIN、并回复 ACK 之后进入的状态。正常情况下,它只是一个过渡状态:应用读到对端关闭发送方向的信号后,处理完剩余数据,再调用 `close()` 或 `shutdown()`,连接就会继续进入 `LAST_ACK`。
+
+如果机器上出现大量 `CLOSE_WAIT`,通常不是内核参数没调好,而是应用层没有及时关闭连接。常见原因包括:异常分支漏掉 `close()`、连接池归还和真实关闭逻辑不一致、业务线程被慢查询或外部调用卡住,导致代码迟迟走不到关闭 socket 的位置。
+
+排查时可以用 `ss -tan state close-wait` 先看哪些连接停在 `CLOSE_WAIT`,再结合应用日志、线程栈和连接池监控定位具体代码路径。`CLOSE_WAIT` 的重点在“本端应用还没关闭”,所以单纯调 TCP 参数通常解决不了根因。
+
+### 什么情况下会出现三次挥手?
-举个例子:A 和 B 打电话,通话即将结束后。
+四次挥手变成三次挥手,本质上不是少了关闭步骤,而是**第二次挥手的 ACK 和第三次挥手的 FIN 被合并到同一个报文段里**。
-1. **第一次挥手**:A 说“我没啥要说的了”
-2. **第二次挥手**:B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话
-3. **第三次挥手**:于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”
-4. **第四次挥手**:A 回答“知道了”,这样通话才算结束。
+比较典型的条件是:被动关闭方收到 FIN 后,本端已经没有待发送的数据,应用也立刻决定关闭连接。
-### 为什么不能把服务端发送的 ACK 和 FIN 合并起来,变成三次挥手?
+这里还要结合 TCP 延迟确认(Delayed ACK)来理解。延迟确认的目的,是让 ACK 有机会和窗口更新、应用响应或其他出站报文合并,减少纯 ACK 报文数量。RFC 1122 要求 ACK 不能被过度延迟,具体等待多久则由实现决定。在 Linux 等实现中,如果“确认对端 FIN”的 ACK 还在等待合并,本端应用又很快调用了 `close()` 或 `shutdown()`,内核就可以发出一个 FIN+ACK:既确认对端的 FIN,也表达“我这边也不再发送数据了”。
-因为服务端收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务端到客户端的数据传送。
+抓包时看到的流程就会变成:
+
+1. 主动关闭方发送 FIN;
+2. 被动关闭方发送 FIN+ACK;
+3. 主动关闭方回复 ACK,并进入 `TIME_WAIT`。
+
+这里有两个细节容易混淆:
+
+- 三次挥手并不违背 TCP 全双工关闭语义。两个方向仍然都要关闭,只是被动关闭方的“确认”和“关闭发送方向”刚好放进了同一个 TCP 报文段。
+- 能不能合并,还和具体 TCP 实现、延迟确认策略、应用关闭时机有关。如果 ACK 已经被内核单独发出,后面再发送 FIN 时就无法“倒回去”合并;如果开启了类似 `TCP_QUICKACK` 的快速确认策略,使 ACK 尽快独立发出,也更容易看到完整的四次挥手。
### 如果第二次挥手时服务端的 ACK 没有送达客户端,会怎样?
-客户端没有收到 ACK 确认,会重新发送 FIN 请求。
+客户端发送第一次 FIN 后进入 `FIN_WAIT_1`,并启动重传计时器。如果在超时时间内没有收到对端对 FIN 的确认 ACK,客户端会重传 FIN。
+
+服务端如果收到重复 FIN,通常会再次发送 ACK。如果由于网络问题 ACK 一直无法送达,客户端在达到一定重试或超时阈值后,可能报错或放弃。具体行为受实现和参数影响:在 Linux 中,如果 socket 已经被应用关闭、成为 orphaned socket,后续重试更直接受 `tcp_orphan_retries` 影响;普通存活连接上的 RTO 重传超时则和 `tcp_retries2` 有关。
+
+### 为什么第四次挥手后要等待 2MSL?
+
+第四次挥手时,主动关闭方发送给被动关闭方的最后一个 ACK 可能丢失。如果被动关闭方没有收到 ACK,就会重传 FIN。主动关闭方还在 `TIME_WAIT` 里,就能再次回复 ACK。
+
+如果主动关闭方发完最后一个 ACK 后立刻进入 `CLOSED`,当对端重传 FIN 到达时,本端可能已经没有对应连接状态,只能回复 RST,导致对端看到异常关闭或连接被重置。
+
+```mermaid
+sequenceDiagram
+ participant A as 主动关闭方
+ participant B as 被动关闭方
+
+ B->>A: FIN
+ A-->>B: ACK 丢失
+ Note over A: A 进入 TIME_WAIT
没有立刻释放连接
+ B->>A: 重传 FIN
+ A-->>B: 再次 ACK
+ Note over B: B 收到 ACK 后进入 CLOSED
+```
-### 为什么第四次挥手客户端需要等待 2\*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?
+**MSL(Maximum Segment Lifetime)** 是报文段在网络中的最大生存时间。2MSL 不是一次请求-响应的最大 RTT,而是一个保守等待窗口:既给最后 ACK 丢失后的 FIN 重传留出处理机会,也尽量保证旧连接中的延迟报文从网络中消失。
-第四次挥手时,客户端发送给服务端的 ACK 有可能丢失,如果服务端因为某些原因而没有收到 ACK 的话,服务端就会重发 FIN,如果客户端在 2\*MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2MSL,防止 Server 没有收到 ACK 而不断重发 FIN。
+需要注意,RFC 里的 MSL 是协议层概念,具体系统实现可能不同。Linux 常见实现中,`TIME_WAIT` 保留时间通常是 60 秒,对应内核中的 `TCP_TIMEWAIT_LEN` 常量,并不是根据实时网络环境动态计算出来的“2 倍 MSL”。还有一个常见误区:`tcp_fin_timeout` 控制的是 orphaned connection 的 `FIN_WAIT_2` 超时,不是 `TIME_WAIT`。想缓解 `TIME_WAIT` 带来的端口压力,优先看连接复用、端口范围、主动关闭方和 `tcp_tw_reuse` 条件,而不是试图用 `tcp_fin_timeout` 缩短 `TIME_WAIT`。
-> **MSL(Maximum Segment Lifetime)** : 一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 连接。
+## TIME_WAIT 常见问题:为什么要等、会不会出问题、能不能复用?
+
+这部分内容已单独成文,详见 [TCP TIME_WAIT 详解:为什么要等、会不会出问题、能不能复用?](./tcp-time-wait.md)。
+
+## 总结
+
+TCP 三次握手的核心,不是“刚好发了三次包”,而是通过 `SYN`、`ACK` 和初始序列号同步,让客户端和服务端都确认连接具备双向通信能力。少一次握手,服务端就可能无法确认客户端是否收到了自己的 `SYN+ACK`,也更容易被网络中的旧连接请求干扰。
+
+服务端在握手过程中会涉及半连接队列和全连接队列:前者保存还没完成握手的连接,后者保存已经建立、等待应用 `accept()` 的连接。排查连接建立慢、偶发超时、SYN Flood 或 accept 不及时等问题时,这两个队列是很重要的观察点。
+
+TCP 四次挥手的核心,是全双工连接的两个发送方向要分别关闭。主动关闭方发 FIN,只表示“我不再发送数据了”,并不代表对端也立刻没有数据要发。因此,ACK 和 FIN 通常分开发送;只有被动关闭方没有待发数据、应用立刻关闭连接,并且 ACK 还可以借助延迟确认等机制等待合并时,ACK 和 FIN 才可能合并成一个 FIN+ACK,抓包上看起来就是三次挥手。`CLOSE_WAIT` 则通常提醒我们:被动关闭方的应用还没有真正关闭连接。
+
+最后,`TIME_WAIT` 不是多余等待。它既给最后一个 ACK 丢失后的 FIN 重传留出处理机会,也尽量避免旧连接中的延迟报文影响后续新连接。理解这些状态和报文的触发时机,比单纯记住“几次握手、几次挥手”更有用。
## 参考
- 《计算机网络(第 7 版)》
-
- 《图解 HTTP》
-
- TCP and UDP Tutorial:
-
- 从一次线上问题说起,详解 TCP 半连接队列、全连接队列:
+- RFC 9293: Transmission Control Protocol(TCP):
+- RFC 1122: Requirements for Internet Hosts - Communication Layers:
+- RFC 1337: TIME-WAIT Assassination Hazards in TCP:
+- tcp(7) - Linux manual page:
+- Linux 内核 ip-sysctl 文档:
+- Linux 内核 `include/net/tcp.h`:
+- SoByte - 为什么 TCP 需要 TIME_WAIT 状态:
diff --git a/docs/cs-basics/network/tcp-keepalive-vs-http-keepalive.md b/docs/cs-basics/network/tcp-keepalive-vs-http-keepalive.md
new file mode 100644
index 00000000000..aa94c81b33a
--- /dev/null
+++ b/docs/cs-basics/network/tcp-keepalive-vs-http-keepalive.md
@@ -0,0 +1,178 @@
+---
+title: TCP Keepalive 和 HTTP Keep-Alive 有什么区别?
+description: 对比 TCP Keepalive 与 HTTP Keep-Alive 的协议层级、核心作用、默认行为、回收方式和典型使用场景,讲清 HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/3 中 Keep-Alive 相关机制的演进。
+category: 计算机基础
+tag:
+ - 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP Keepalive,HTTP Keep-Alive,Keep-Alive,长连接,短连接,TCP保活,HTTP长连接,HTTP/1.0,HTTP/1.1,HTTP/2,HTTP/3,QUIC,UDP,SO_KEEPALIVE
+---
+
+你好,我是小 G。TCP Keepalive 和 HTTP Keep-Alive 的对比,经常作为面试题出现在技术面试中。这篇文章来详细聊一聊。
+
+简单来说,这俩只是后缀名字一样,但完全不是一回事,毕竟一个在应用层,一个在传输层,根本不在同一层:
+
+- **HTTP Keep-Alive** 是应用层的机制,解决的问题是:一个 TCP 连接能不能被多个 HTTP 请求复用,别每次请求都重新握手。
+- **TCP Keepalive** 是传输层的机制,解决的问题是:一条 TCP 连接长时间没有数据往来,怎么判断对端还在不在,要不要把连接占用的资源回收掉。
+
+
+
+一个管“连接要不要复用”,一个管“连接还活不活着”。协议层不同,目的也不同,只是名字撞了。
+
+下面分开讲。
+
+## HTTP 的 Keep-Alive 是什么?
+
+先说问题。HTTP 1.0 的默认行为是:每个 TCP 连接只服务一次 HTTP 请求和响应。服务器发完响应,马上发起关闭连接的请求,客户端跟着关,TCP 连接就双向断开了。
+
+
+
+你打开一个网页,HTML、CSS、JS、图片可能有几十个资源要加载。如果每个资源都独立建连接再销毁,光三次握手和四次挥手的开销就不小,TCP 连接的利用率很低。
+
+这个问题很明显:**为什么一个 TCP 连接不能服务多次 HTTP 请求呢?**
+
+于是 HTTP 引入了 `Connection` 头部。以 HTTP/1.0 为例,客户端可以在请求头里带上:
+
+```
+Connection: Keep-Alive
+```
+
+服务器如果也在响应头里确认这个字段,就表示双方都同意这次 HTTP 交易用到的 TCP 连接是一个**长连接(Persistent Connection)**——请求/响应结束后先别关,后续其他 HTTP 请求还可以接着复用这条连接,直到连接空闲超时、达到请求次数上限,或者被任意一方主动关闭。
+
+**在不同 HTTP 版本里,Keep-Alive 的默认行为不一样:**
+
+
+
+- **HTTP 1.0**:默认是短连接。要用长连接,请求头里得显式带上 `Connection: Keep-Alive`,而且服务器也要在响应头里带上这个字段才算生效。
+- **HTTP 1.1**:默认就是长连接,不需要额外声明。如果希望请求结束后关闭连接,需要显式指定: `Connection: close`。这也是为什么 HTTP/1.1 相比 HTTP/1.0 能明显减少 TCP 建连和挥手开销。
+- **HTTP/2**:HTTP/2 不再沿用 HTTP/1.x “一个连接串行处理多个请求”的方式,而是引入了多路复用(Multiplexing),也就是说一个 TCP 连接上可以同时并发多个 Stream,请求和响应可以交错传输,不再互相阻塞,解决了 HTTP/1.1 应用层的队头阻塞问题。不过,HTTP/2 依然跑在单条 TCP 连接上,一旦底层 TCP 出现丢包,后续数据仍然要等待重传,因此它依然会受到 TCP 层队头阻塞的影响。这种基于 HTTP/1.x 的连接控制方式在 HTTP/2 中已经没有意义了。更严格地说,`Connection`、`Keep-Alive`、`Transfer-Encoding` 等 connection-specific headers 在 HTTP/2 中是被禁止使用的,带有这些头部的消息会被视为不合法。
+- **HTTP/3**:HTTP/3 基于 QUIC,运行在 UDP 之上,不再依赖 TCP 连接,也不使用 HTTP/1.x 的 `Connection: Keep-Alive` 这套连接控制方式。QUIC 自己负责连接管理、保活和多路复用,并在传输层面缓解了 TCP 队头阻塞问题。
+
+一句话总结:HTTP/1.0 需要显式 Keep-Alive,HTTP/1.1 默认连接复用,HTTP/2 从“连接复用”升级成了“单 TCP 连接上的多路复用”,而 HTTP/3 则直接换成了基于 QUIC 的连接管理。
+
+## HTTP 长连接怎么关闭和回收?
+
+长连接提高了 TCP 利用率,但也带来一个新的问题:客户端打开一个页面,TCP 连接建好了,结果用户就把页面扔在那里不管了。这条连接一直空闲着,服务器不能无限等下去,但也不能完全靠客户端自觉关闭。
+
+如果这类空闲连接堆积多了,服务器的 TCB(TCP Control Block)资源会被白白占掉。
+
+HTTP 的解决办法是在 `Keep-Alive` 头部里带两个参数:
+
+```
+Keep-Alive: timeout=5, max=10
+```
+
+- **timeout=5**:连接空闲超过 5 秒,服务器就可以关闭。
+- **max=10**:这条连接最多服务 10 次 HTTP 请求,到了次数上限就强制关闭。
+
+这里有个点容易忽略:**到了 timeout 或 max 的阈值,不管客户端当时在不在线,服务器都可以关闭连接。** 如果客户端刚好复用这条旧连接发送新请求,就可能遇到连接已经关闭、请求失败后需要重试的情况。
+
+也就是说,HTTP Keep-Alive 的空闲连接回收通常由服务器配置主导。客户端当然可以主动关闭连接,但服务器不会一直等客户端“表态”。
+
+在实际的 Web 服务器配置中,这些参数由服务端决定。比如 Nginx 的 `keepalive_timeout` 默认值是 75 秒,`keepalive_requests` 默认值是 1000(Nginx 1.19.10 之前的版本默认是 100)。
+
+## TCP 的 Keepalive 是什么?
+
+TCP Keepalive 要解决的问题完全不一样:它不关心连接上跑不跑 HTTP 请求,它关心的是——**对端到底还在不在**。
+
+考虑这样一个场景:客户端和服务器之间建了一条 TCP 连接,但客户端突然断电了、网线被拔了、或者系统直接崩了。这时候服务器这边完全不知道对面已经没了,因为 TCP 又不像打电话,没有“忙音”。这条连接就变成了一条“半打开”(Half-Open)的死连接,白白占着服务器内存中的 TCB 资源。
+
+TCP Keepalive 就是用来发现这种情况的。它的工作流程如下:
+
+
+
+1. 一条 TCP 连接上如果一段时间没有任何数据往来(默认 **7200 秒,也就是 2 小时**),内核会自动给对端发一个**探测报文(Probe)**。
+2. 如果对端正常在线,会回复一个 ACK,然后计时器重置,再等 2 小时。
+3. 如果对端没有回复,每隔 **75 秒** 重发一个探测包,最多重试 **9 次**。
+4. 9 次都没回复,内核判定连接已死,发 RST 关闭连接,释放资源。
+
+这三个参数在 Linux 上对应的内核配置是:
+
+| 参数 | 含义 | Linux 默认值 |
+| ---------------------- | ---------------------------- | ----------------- |
+| `tcp_keepalive_time` | 连接空闲多久后开始发送探测包 | 7200 秒(2 小时) |
+| `tcp_keepalive_intvl` | 两次探测包之间的间隔 | 75 秒 |
+| `tcp_keepalive_probes` | 最多发送几次探测包 | 9 次 |
+
+macOS 属于 BSD 系网络栈风格,没有 `net.ipv4.*`,对应的是:`net.inet.tcp.*`。
+
+
+
+按默认值算,从连接开始空闲到最终被判死,最长要等 **7200 + 75 × 9 = 7875 秒**,差不多 2 小时 11 分钟。
+
+可以通过 `sysctl` 查看和修改:
+
+```bash
+sysctl net.ipv4.tcp_keepalive_time
+sysctl net.ipv4.tcp_keepalive_intvl
+sysctl net.ipv4.tcp_keepalive_probes
+```
+
+还有一个很容易踩的坑:**TCP Keepalive 默认是关闭的。** 应用程序必须在创建 socket 时通过 `SO_KEEPALIVE` 选项显式开启,否则内核不会发探测包。这在 RFC 1122 里有明确规定:Keepalive 是可选功能,必须默认不启用。
+
+理解了工作原理之后,TCP Keepalive 的性质就很清楚了——它是一种**“温和”的资源回收机制**。它只能在确认对方不在线之后才回收资源。只要对方还在线、还能回 ACK,这条连接就只能继续维持着,定时器重置,再等下一个 2 小时。对方在线的时候,服务器没有任何办法通过 TCP Keepalive 来关掉这条连接。
+
+这和 HTTP Keep-Alive 的“到时间就关,不管你在不在”形成了鲜明的对比。
+
+## TCP Keepalive 探测后会出现哪几种情况?
+
+内核发出探测报文后,根据对端的实际状态,会走向不同的结果:
+
+
+
+**1. 对端正常在线**
+
+对端收到探测包,TCP 栈回复一个 ACK。发送方收到 ACK,把空闲计时器重置为 `tcp_keepalive_time`,继续等待。连接不会被关闭。
+
+**2. 对端曾经崩溃,但已经重启**
+
+对端虽然在线,但由于重启过,内核里已经没有这条连接的上下文了。收到探测包后,对端的 TCP 栈会回复一个 RST(因为它不认识这条连接)。发送方收到 RST,立即关闭连接。
+
+**3. 对端崩溃且未恢复,或者网络不可达**
+
+探测包发出去后得不到任何回复。发送方每隔 `tcp_keepalive_intvl` 秒重试一次,连续 `tcp_keepalive_probes` 次都没响应,判定连接已死,内核关闭连接并释放资源。
+
+第 3 种情况也覆盖了一些中间网络设备导致的问题。比如 NAT 网关通常有会话超时机制,如果一条连接长时间没有数据传输,NAT 表项会被清掉。后续的探测包就没法到达对端,效果和对端崩溃一样——都是得不到回复,最终超时关闭。
+
+## TCP Keepalive 有什么局限?
+
+这里的 TCP Keepalive 指的是 TCP 层的 keep-alive 探测机制,不是 HTTP 的 Keep-Alive 连接复用。它能检测死连接,但在生产环境中,光靠它通常不够,原因有几个:
+
+**默认探测太慢了。** 以 Linux 默认配置为例,连接空闲 7200 秒后才开始发送探测;Windows 默认 keep-alive timeout 也是 2 小时。这个量级对大部分在线业务连接来说都偏长。Linux 的 `net.ipv4.tcp_keepalive_*` 是系统默认值,会影响未单独设置的连接;如果应用需要按连接区分策略,可以在支持的平台上逐 socket 设置 `TCP_KEEPIDLE`、`TCP_KEEPINTVL`、`TCP_KEEPCNT`。不过,这类选项不适合写成跨平台通用方案,具体还要看操作系统和语言运行时是否暴露。
+
+**只能检测连接存活,不能检测应用健康。** TCP Keepalive 的探测包是内核发的,对端的 TCP 栈收到后直接回 ACK,应用层完全不参与。所以它只能说明对端内核还能收到包并返回 ACK,不能说明对端应用线程池、事件循环、数据库连接池、业务依赖是否正常。这是它最大的盲区。
+
+**经过中间层时容易看错对象。** 如果客户端和服务器之间有 NAT、四层负载均衡或反向代理,要先看 TCP 连接有没有被中间层终止。如果中间层只是做 NAT/连接跟踪,Keepalive 间隔需要小于中间设备的空闲超时,才可能避免表项被清掉;如果中间层终止了 TCP 连接,后端检测到的只是后端到中间层这一段连接是否存活,不代表真实客户端一定还活着。
+
+**各操作系统的实现和默认值不一致。** 比如 Linux 默认是 7200 秒后开始探测、75 秒间隔、最多 9 次;Windows 默认 timeout 也是 2 小时,但 interval 默认 1 秒,Windows Vista 及之后 probe 次数固定为 10,不能改;macOS 属于 BSD 系网络栈风格,没有 Linux 的 `net.ipv4.*` 这组 sysctl,相关参数通常在 `net.inet.tcp.*` 下面。靠 TCP Keepalive 做跨平台连接健康检查,一致性很难保证,具体参数名、单位和默认值最好以目标系统实测为准。
+
+**不直接作用于 HTTP/3/QUIC。** 对真正的 HTTP/3/QUIC 连接来说,TCP Keepalive 不参与连接存活检测;但客户端如果因为 UDP 被阻断等原因回退到 HTTP/1.1 或 HTTP/2,那回退后的 TCP 连接仍然可能受 TCP Keepalive 影响。HTTP/3 的连接存活和超时由 QUIC 处理,例如 QUIC 有 idle timeout,必要时可以发送 PING frame 做 liveness testing;HTTP/3 层关闭连接时还可以用 GOAWAY 协助优雅关闭。
+
+所以实际工程中,TCP Keepalive 更多是作为兜底手段,帮你清理那些明确已经死掉的连接。如果需要更快速、更细粒度、且能感知应用层状态的健康检查,还是得在应用层自己做心跳,比如 WebSocket 的 Ping/Pong、gRPC 的 keepalive ping,或者业务自定义的心跳协议。
+
+应用层心跳也不是越频繁越好。心跳间隔太短会增加包量、服务端定时器压力和弱网误判概率;间隔太长又发现故障不及时。实际配置要结合连接规模、NAT/LB idle timeout、业务可接受的故障发现时间一起定。
+
+## TCP Keepalive 和 HTTP Keep-Alive 对比总结
+
+| 对比维度 | HTTP Keep-Alive | TCP Keepalive |
+| ----------------- | ------------------------------------------------------- | --------------------------------------------------- |
+| **所属层** | 应用层(HTTP 协议) | 传输层(TCP 协议) |
+| **解决的问题** | 复用 TCP 连接,减少重复建连、挥手、慢启动等开销 | 探测长时间空闲的 TCP 连接,对端失联后释放连接资源 |
+| **默认行为** | HTTP/1.0 默认短连接;HTTP/1.1 默认长连接 | 默认关闭,应用需要显式开启 `SO_KEEPALIVE` |
+| **控制粒度** | 由 HTTP 客户端、Web 服务器或代理按连接策略控制 | 由操作系统内核控制,也可在部分平台逐 socket 调整 |
+| **常见参数** | `Connection`、`Keep-Alive: timeout/max`、服务器超时配置 | `tcp_keepalive_time/intvl/probes` 或平台对应参数 |
+| **关闭触发** | 到达空闲超时、请求次数上限,或任意一方主动关闭 | 空闲后发探测包,多次无响应或收到 RST 才关闭 |
+| **对端在线时** | 服务端仍可按配置主动回收空闲连接 | 只要对端内核能回 ACK,连接通常继续维持 |
+| **能否替代心跳** | 不能判断业务是否健康,只能管理 HTTP 连接复用 | 不能判断应用线程池、事件循环、业务依赖是否正常 |
+| **中间层影响** | 代理、网关可独立管理前后两段 HTTP/TCP 连接 | NAT/LB/反向代理可能让你探测到的只是某一段 TCP 连接 |
+| **HTTP/2/3 关系** | HTTP/2 禁用连接级头;HTTP/3/QUIC 不使用这套机制 | 只作用于 TCP;真正的 HTTP/3/QUIC 连接不受它直接影响 |
+
+如果从“谁来决定关连接”的角度看,两个机制的态度完全相反:
+
+HTTP Keep-Alive 是“主动回收”——服务器到了超时或请求次数上限,就可以按自己的配置关闭连接,不需要先探测对方是否在线。它是一种比较主动的资源回收方式。
+
+TCP Keepalive 是“被动回收”——它必须先发探测包去问“你还在吗?”。只要对方在线、能回 ACK,服务器就只能继续维持连接,刷新定时器。只有确认对方已经不在了,才能释放资源。这是一种温和的回收策略。
+
+实际项目中,两者经常同时在跑,各管各的。HTTP Keep-Alive 管的是“一条连接最多用多久、服务多少次请求”,TCP Keepalive 管的是“如果长时间没数据,检查一下对方是不是已经消失了”。两者互不干扰,也不能互相替代。
diff --git a/docs/cs-basics/network/tcp-reliability-guarantee.md b/docs/cs-basics/network/tcp-reliability-guarantee.md
index d4c9bea80ed..35b0f02066c 100644
--- a/docs/cs-basics/network/tcp-reliability-guarantee.md
+++ b/docs/cs-basics/network/tcp-reliability-guarantee.md
@@ -1,48 +1,188 @@
---
-title: TCP 传输可靠性保障(传输层)
+title: TCP 如何保证可靠传输?重传、滑动窗口与拥塞控制详解
+description: 系统梳理 TCP 的可靠性保障机制,覆盖重传/选择确认、流量与拥塞控制,明确端到端可靠传输的实现要点。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP,可靠性,重传,SACK,D-SACK,流量控制,拥塞控制,滑动窗口,校验和,CUBIC,BBR
---
+TCP 常被说成可靠传输协议,但“可靠”不是一句抽象承诺,而是一组具体机制共同配合出来的结果。
+
+丢包要重传,乱序要重排,接收方处理不过来要流量控制,网络拥塞时要主动降速。把这些机制串起来,才能真正理解 TCP 为什么能在不可靠的 IP 网络之上提供可靠传输。
+
+这篇文章主要回答几个问题:
+
+1. TCP 通过哪些机制保证数据可靠到达?
+2. 超时重传、快速重传、SACK、D-SACK 分别解决什么问题?
+3. TCP 如何通过滑动窗口实现流量控制?
+4. 拥塞控制中的慢开始、拥塞避免、快速重传、快恢复分别怎么理解?
+
+先澄清一个容易误解的点:TCP 可靠的是**字节流**,不是应用层的一条条“消息”。TCP 不会保留 HTTP、RPC 或业务协议里的消息边界,它做的是给字节流编号,并尽量把这些字节按序、无重复地交付给应用层。至于“一个请求从哪里开始、到哪里结束”,要靠上层协议自己定义,比如长度字段、分隔符、HTTP 报文格式等。
+
+
+
## TCP 如何保证传输的可靠性?
1. **基于数据块传输**:应用数据被分割成 TCP 认为最适合发送的数据块,再传输给网络层,数据块被称为报文段或段。
-2. **对失序数据包重新排序以及去重**:TCP 为了保证不发生丢包,就给每个包一个序列号,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据就可以实现数据包去重。
-3. **校验和** : TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
-4. **重传机制** : 在数据包丢失或延迟的情况下,重新发送数据包,直到收到对方的确认应答(ACK)。TCP 重传机制主要有:基于计时器的重传(也就是超时重传)、快速重传(基于接收端的反馈信息来引发重传)、SACK(在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了)、D-SACK(重复 SACK,在 SACK 的基础上,额外携带信息,告知发送方有哪些数据包自己重复接收了)。关于重传机制的详细介绍,可以查看[详解 TCP 超时与重传机制](https://zhuanlan.zhihu.com/p/101702312)这篇文章。
-5. **流量控制** : TCP 连接的每一方都有固定大小的缓冲空间,TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议(TCP 利用滑动窗口实现流量控制)。
-6. **拥塞控制** : 当网络拥塞时,减少数据的发送。TCP 在发送数据的时候,需要考虑两个因素:一是接收方的接收能力,二是网络的拥塞程度。接收方的接收能力由滑动窗口表示,表示接收方还有多少缓冲区可以用来接收数据。网络的拥塞程度由拥塞窗口表示,它是发送方根据网络状况自己维护的一个值,表示发送方认为可以在网络中传输的数据量。发送方发送数据的大小是滑动窗口和拥塞窗口的最小值,这样可以保证发送方既不会超过接收方的接收能力,也不会造成网络的过度拥塞。
+2. **对失序数据重新排序以及去重**:TCP 不能阻止网络丢包,它能做的是给字节流编号,并通过 ACK、重传、排序、去重等机制,让应用层看到的是有序、无重复的数据流。TCP 的序列号本质上是字节序号,不是按报文段逐个编号。一个 TCP 段携带一段连续字节,接收端根据这些序号区间完成重排和去重。
+3. **校验和**:TCP 会对 TCP 首部、数据以及 IP 伪首部计算校验和。这是一个端到端的校验和,目的是检测数据在传输过程中的变化。如果收到的报文段校验和有差错,TCP 会丢弃这个报文段,并且不会确认收到它。不过,TCP 校验和只是 16 位的一补和校验,主要用于发现常见的传输错误,并不是强完整性校验,也不能防止恶意篡改。实际系统里的数据完整性通常还会依赖链路层 CRC、TLS AEAD 或应用层 hash 等机制。
+4. **重传机制**:在 TCP 段丢失或延迟的情况下,重新发送数据,直到收到对方的确认应答(ACK)。TCP 重传机制主要有:基于计时器的重传(也就是超时重传)、快速重传(基于接收端的反馈信息来引发重传)、SACK(选择确认,在 ACK 选项中携带已经收到的非连续数据块范围,这样发送方就知道哪些数据段已经到达接收方了)、D-SACK(重复 SACK,在 SACK 的基础上,额外告知发送方有哪些数据段被重复接收)。D-SACK 的价值在于帮助发送方判断一次重传是否可能是“误重传”:比如原始数据其实已经到达接收方,只是 ACK 丢失、网络乱序或重传定时器过早触发,导致发送方误以为丢包并触发重传。接收方通过 D-SACK 告诉发送方“这段数据我重复收到了”,发送方就能推断这次重传可能只是误判,而不一定是真正发生了拥塞。不过,D-SACK 只能提供线索,不能单独证明某一种具体原因。
+5. **流量控制**:TCP 连接的每一方都有一定大小的缓冲空间。接收端通过接收窗口(rwnd)告诉发送端自己还能接收多少数据,发送端据此控制发送速率,避免接收端处理不过来而丢包。
+6. **拥塞控制**:当网络拥塞时,减少数据的发送。TCP 在发送数据的时候,需要考虑两个因素:一是接收端当前可用接收缓冲区能力,二是网络的拥塞程度。接收方的接收能力由接收窗口(rwnd)表示,网络的拥塞程度由拥塞窗口(cwnd)表示。发送方允许保持在网络中的未确认数据量,通常受 `min(rwnd, cwnd)` 约束,这样既不会超过接收方的处理能力,也不会给网络注入过多数据。
+
+## 先用 ARQ 理解 TCP 重传
+
+上面几个机制里,最能体现“可靠传输”的是重传。为了不让超时重传、快速重传、SACK 这些概念显得凭空出现,我们先看 ARQ 这个抽象模型。
+
+**自动重传请求**(Automatic Repeat-reQuest,ARQ)是 OSI 模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认应答(Acknowledgments,ACK),它通常会重新发送,直到收到确认或者重试超过一定的次数。
+
+TCP 可以用 ARQ 思想来理解,但它不是教材里的某一种简单 ARQ。现代 TCP 同时结合了累积 ACK、RTO、快速重传、SACK、拥塞控制和流量控制,重传策略会受到这些机制共同影响。
+
+- 默认 ACK 是**累积确认**:ACK 表示“这个序号之前的数据我都已经收到了”。
+- 开启 SACK 后,接收方还能额外告诉发送方“我已经乱序收到了哪些区间”,发送方可以只重传缺失的数据段。
+
+因此,停止等待 ARQ 和 Go-Back-N 更适合理解可靠传输的基础思想,而现代 TCP 在 SACK 的帮助下更接近选择重传。
+
+
+
+ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。
+
+### 停止等待 ARQ 协议
+
+停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复 ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组。
+
+在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认。
+
+**1)无差错情况:**
+
+发送方发送分组,接收方在规定时间内收到,并且回复确认。发送方再次发送。
+
+**2)出现差错情况(超时重传):**
+
+停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为**自动重传请求(ARQ)**。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。
+
+**3)确认丢失和确认迟到**
+
+- **确认丢失**:确认消息在传输过程丢失。当 A 发送 M1 消息,B 收到后,B 向 A 发送了一个 M1 确认消息,但却在传输过程中丢失。而 A 并不知道,在超时计时过后,A 重传 M1 消息,B 再次收到该消息后采取以下两点措施:1. 丢弃这个重复的 M1 消息,不向上层交付。2. 向 A 发送确认消息。(不会认为已经发送过了,就不再发送。A 能重传,就证明 B 的确认消息丢失)。
+- **确认迟到**:确认消息在传输过程中迟到。A 发送 M1 消息,B 收到并发送确认。在超时时间内没有收到确认消息,A 重传 M1 消息,B 仍然收到并继续发送确认消息(B 收到了 2 份 M1)。此时 A 收到了 B 第二次发送的确认消息。接着发送其他数据。过了一会,A 收到了 B 第一次发送的对 M1 的确认消息(A 也收到了 2 份确认消息)。处理如下:1. A 收到重复的确认后,直接丢弃。2. B 收到重复的 M1 后,也直接丢弃重复的 M1。
+
+### 连续 ARQ 协议
+
+连续 ARQ 是一类滑动窗口式重传思想,典型形式包括 Go-Back-N 和选择重传。它可以提高信道利用率:发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
+
+- **优点**:信道利用率高,容易实现,即使确认丢失,也不必重传。
+- **缺点**:如果采用 Go-Back-N,不能向发送方反映出接收方已经正确收到的所有分组的信息。比如:发送方发送了 5 条消息,中间第三条丢失(3 号)。在 Go-Back-N 中,即使 4、5 号分组已经到达,接收方也会因为它们失序而丢弃,只重复确认最后一个按序收到的 2 号分组。发送方最终需要从 3 号开始回退重传。SACK 的作用,正是让 TCP 接收方能告诉发送方这些非连续但已经收到的数据区间,避免大量不必要的回退重传。
+
+有了 ARQ 这条线,再看 TCP 的具体重传机制就顺了。
+
+## TCP 重传机制速查
+
+TCP 的重传不是只有一种触发方式。最基础的是**超时重传**:发送方等 ACK 等太久,就认为这段数据可能丢了,于是重传。后来又有**快速重传**:接收方连续 ACK 同一个旧序号,说明中间可能缺了一段,发送方就不用傻等超时。SACK 和 D-SACK 则是在 ACK 里带更多信息,让发送方知道“哪些段已经到了、哪些重传可能是误判”。
+
+所以下面这张表不是新知识点,而是一张地图:先把几种重传相关机制摆在一起,后面再从最基础、也最兜底的 **RTO 超时重传** 开始展开。
+
+| 机制 | 触发条件 | 解决什么问题 |
+| --------------- | ------------------------------------------------------ | -------------------------------------------- |
+| 超时重传(RTO) | 超过 RTO 仍未收到 ACK | 兜底处理丢包 |
+| 快速重传 | 收到 3 个 duplicate ACK,即连续确认同一个旧累计 ACK 号 | 不等超时,尽快重传疑似丢失的数据段 |
+| SACK | ACK 中携带已收到的数据区间 | 告诉发送方哪些段已收到,只重传真正缺失的部分 |
+| D-SACK | SACK 中报告重复收到的数据段 | 帮助识别误重传、ACK 丢失或网络乱序 |
+
+## 超时重传如何实现?超时重传时间怎么确定?
+
+先看表里的第一行:**超时重传(RTO)**。它是 TCP 重传机制的兜底方案。无论有没有 SACK、有没有触发快速重传,只要某段数据发出去以后迟迟没有等到 ACK,最终都要靠 RTO 来判断“不能再等了,该重传了”。
+
+当发送方发送数据之后,它会启动一个定时器,等待目的端确认收到这个报文段。接收端对已成功收到的 TCP 段发回相应的 ACK。如果发送端在合理的往返时延(RTT)内未收到确认,那么对应的数据段就会被认为可能已经丢失,并进行重传。
+
+- **RTT(Round Trip Time)**:往返时间,也就是 TCP 段从发出去到收到对应 ACK 的时间。
+- **RTO(Retransmission Time Out)**:重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传。
+
+
+
+RTO 的确定是一个关键问题,因为它直接影响到 TCP 的性能和效率。如果 RTO 设置得太小,会导致不必要的重传,增加网络负担;如果 RTO 设置得太大,会导致数据传输的延迟,降低吞吐量。因此,RTO 应该根据网络的实际状况,动态地进行调整。
+
+RTT 的值会随着网络的波动而变化,所以 TCP 不能直接使用某一次 RTT 样本作为 RTO。现代 TCP 的 RTO 计算应以 RFC 6298 为主线:根据 RTT 样本维护平滑后的往返时间 SRTT 和往返时间波动 RTTVAR,再计算 RTO;发生超时后还会做指数退避。
+
+Karn 算法的核心点是:对已经重传过的报文段,其 ACK 不用于 RTT 采样,避免“这个 ACK 到底对应原始发送还是重传”的样本歧义。
+
+简单理解就是:RTO 不是 RTT,而是“平滑 RTT + 抖动余量”。如果一条连接的 RTT 样本大约是 100 ms,但抖动很大,RTO 就必须留出更大的安全余量;如果仍然超时,下一次 RTO 还会继续退避,避免在拥塞时把重传压力继续打到网络里。
+
+## 快速重传是如何工作的?
+
+超时重传可靠但偏慢,因为发送方必须等到 RTO 过期以后才会重传。快速重传(Fast Retransmit)解决的就是这个等待问题:它不依赖计时器,而是依赖接收方连续发回的重复 ACK 来更早发现疑似丢包。
+
+TCP 使用累积确认。假设接收方已经按序收到了 `[0, 1000)` 这段字节,接下来期望收到从 1000 开始的数据。如果它先收到了 `[2000, 3000)`,说明中间 `[1000, 2000)` 这段还没到。接收方不会把 ACK 推进到 3000,而是继续回复 ACK = 1000,表示“我仍然在等 1000 之后的数据”。这种再次确认同一个旧 ACK 号的报文,就是 duplicate ACK。
+
+发送方如果连续收到 3 个 duplicate ACK,通常会认为 ACK 指向的那段数据大概率已经丢失,于是不等 RTO 超时,直接重传缺失的数据段。之所以不是收到 1 个 duplicate ACK 就重传,是因为网络里可能出现短暂乱序:后发出的包先到,不一定代表前面的包真的丢了。3 个 duplicate ACK 是在“尽快恢复”和“避免误判乱序”之间做的折中。
+
+快速重传只能更快定位“最早的缺口”。如果一个发送窗口里同时丢了多段数据,仅靠累积 ACK 仍然很难告诉发送方哪些区间已经到了、哪些区间还缺着,这就需要 SACK。
+
+## SACK 是如何提升重传效率的?
+
+SACK(Selective Acknowledgment,选择性确认)用来补足累积 ACK 的信息盲区。普通 ACK 只能表达“某个序号之前的数据都收到了”,但无法表达“后面的某些区间虽然乱序,也已经收到了”。SACK 会在 ACK 的 TCP 选项里携带已经收到的非连续字节区间,帮助发送方只重传真正缺失的部分。
+
+SACK 需要在三次握手时通过 SACK-Permitted 选项协商。启用后,ACK 号本身仍然遵循累积确认规则,SACK 选项额外携带一个或多个 SACK block。每个 SACK block 由 Left Edge 和 Right Edge 组成,表示接收方已经收到的字节区间 `[Left Edge, Right Edge)`。
+
+举个例子:发送方连续发送 `[0, 1000)`、`[1000, 2000)`、`[2000, 3000)`、`[3000, 4000)`,其中 `[1000, 2000)` 丢失,但后面两段已经到达。接收方的累计 ACK 仍然只能停在 ACK = 1000,但它可以在 SACK 里报告已经收到 `[2000, 4000)`。发送方据此就知道 `[1000, 2000)` 需要重传,而 `[2000, 4000)` 不必重复发送。
+
+TCP 选项长度有限。SACK 选项本身需要 2 字节,每个 SACK block 需要 8 字节,所以一个 TCP 段最多能携带 4 个 SACK block;如果同时携带时间戳等其他 TCP 选项,可用空间还会更少。也就是说,SACK 不能无限记录所有乱序区间,但它已经足以显著减少多段丢包时的无效重传。
+
+## D-SACK 有什么作用?
+
+D-SACK(Duplicate SACK,重复选择性确认)是对 SACK 的扩展,定义在 RFC 2883 中。SACK 主要告诉发送方“哪些非连续区间已经收到”,D-SACK 则进一步告诉发送方“哪些区间被重复收到了”。
+
+D-SACK 不引入新的 TCP 选项,而是复用 SACK block。它约定:如果第一个 SACK block 描述的是一段已经被累计 ACK 覆盖的数据,或者描述的是一段已经出现在后续 SACK block 中的数据,那么这个 block 就是在报告重复数据。
+
+为什么重复数据有价值?因为一次重传不一定代表原始数据真的丢了。常见情况包括:
+
+- 原始数据已经到达接收方,但 ACK 在返回途中丢失,发送方等到 RTO 后误以为数据丢了,于是重传。
+- 原始数据在网络中严重乱序或延迟,发送方先触发了快速重传,随后原始数据和重传数据都到达接收方。
+
+收到 D-SACK 后,发送方可以推断这次重传可能是误重传,并进一步判断网络中是否存在 ACK 丢失、严重乱序或 RTO 设置过小等问题。它不能单独证明某一种具体原因,但能为拥塞控制和排查重传异常提供重要线索。
## TCP 如何实现流量控制?
-**TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。** 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
+**TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。** 滑动窗口是 TCP 的核心机制之一,它既用于追踪“哪些数据已经发送但还没被 ACK”,也用于承载流量控制。接收方会在 ACK 报文中通过窗口字段通告自己的接收窗口(rwnd),发送方据此调整发送窗口。将窗口字段设置为 0,就表示接收方暂时没有可用缓冲区,发送方不能继续发送普通新数据。
+
+TCP 首部里的窗口字段本身是 16 位,最大只能表示 65,535 字节。如果需要更大的接收窗口,还要依赖 TCP Window Scale 选项对窗口大小进行扩展。实际排查时,可以在三次握手的 SYN/SYN-ACK 报文里看到双方是否协商了 Window Scale。
+
+**零窗口怎么恢复?** 当接收方通告 `rwnd = 0` 时,发送方会暂停发送新数据。但如果接收方后来腾出了缓冲区,并发送了新的窗口通告,而这个 ACK 在网络中丢失,双方就可能陷入互相等待:发送方等窗口打开,接收方等新数据到来。
-**为什么需要流量控制?** 这是因为双方在通信的时候,发送方的速率与接收方的速率是不一定相等,如果发送方的发送速率太快,会导致接收方处理不过来。如果接收方处理不过来的话,就只能把处理不过来的数据存在 **接收缓冲区(Receiving Buffers)** 里(失序的数据包也会被存放在缓存区里)。如果缓存区满了发送方还在狂发数据的话,接收方只能把收到的数据包丢掉。出现丢包问题的同时又疯狂浪费着珍贵的网络资源。因此,我们需要控制发送方的发送速率,让接收方与发送方处于一种动态平衡才好。
+
+
+为了解决这个问题,TCP 引入了**零窗口探测(Zero Window Probe)**。发送方在窗口为 0 时,依赖持续计时器(persist timer)定期发送很小的探测报文,迫使接收方回复当前窗口大小。这样即使之前的窗口更新 ACK 丢失,发送方也能重新得知窗口是否已经打开。
+
+零窗口探测只负责打破“窗口更新 ACK 丢失”造成的僵持,不等于业务层连接健康检查。如果接收端应用长期不读取 socket,连接可能长期停留在小窗口或零窗口状态,仍然会占用双方资源。实际工程中通常还需要应用层读写超时、空闲连接回收等机制兜底。
+
+**为什么需要流量控制?** 这是因为双方在通信的时候,发送方的速率与接收方的速率不一定相等。如果发送方的发送速率太快,会导致接收方处理不过来。如果接收方处理不过来,就只能把数据先放到 **接收缓冲区(receive buffer)** 里(失序的数据段也会被存放在缓冲区里)。正常情况下,接收方会通过缩小 `rwnd`,甚至通告零窗口,让发送方停止发送新数据。只有在窗口控制来不及生效、对端实现异常、缓冲耗尽或网络队列溢出时,才可能出现丢弃。因此,我们需要控制发送方的发送速率,让接收方与发送方处于一种动态平衡。
这里需要注意的是(常见误区):
- 发送端不等同于客户端
- 接收端不等同于服务端
-TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客户端和服务端既可能是发送端又可能是服务端。因此,两端各有一个发送缓冲区与接收缓冲区,两端都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制(TCP 传输速率不能大于应用的数据处理速率)。通信双方的发送窗口和接收窗口的要求相同
+TCP 为全双工(Full-Duplex,FDX)通信,双方可以进行双向通信,客户端和服务端既可能是发送端,也可能是接收端。因此,两端各有一个发送缓冲区与接收缓冲区,两端都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制(TCP 传输速率不能大于应用的数据处理速率)。通信双方维护窗口的逻辑是类似的。
**TCP 发送窗口可以划分成四个部分**:
1. 已经发送并且确认的 TCP 段(已经发送并确认);
2. 已经发送但是没有确认的 TCP 段(已经发送未确认);
3. 未发送但是接收方准备接收的 TCP 段(可以发送);
-4. 未发送并且接收方也并未准备接受的 TCP 段(不可发送)。
+4. 未发送并且接收方也暂时不能接收的 TCP 段(不可发送)。
**TCP 发送窗口结构图示**:

- **SND.WND**:发送窗口。
-- **SND.UNA**:Send Unacknowledged 指针,指向发送窗口的第一个字节。
+- **SND.UNA**:Send Unacknowledged,表示最早尚未被确认的序号,也就是发送窗口左边界。
- **SND.NXT**:Send Next 指针,指向可用窗口的第一个字节。
-**可用窗口大小** = `SND.UNA + SND.WND - SND.NXT` 。
+只看接收窗口约束时,**可用发送窗口大小** 约为 `SND.UNA + SND.WND - SND.NXT`。真实发送还要再受 `cwnd`、MSS、发送缓冲区等限制。
**TCP 接收窗口可以划分成三个部分**:
@@ -54,75 +194,77 @@ TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客

-**接收窗口的大小是根据接收端处理数据的速度动态调整的。** 如果接收端读取数据快,接收窗口可能会扩大。 否则,它可能会缩小。
+**接收窗口的大小是动态调整的。** 它通常会受应用读取速度、接收缓冲区占用、系统 socket buffer 配置和自动调优策略影响。
另外,这里的滑动窗口大小只是为了演示使用,实际窗口大小通常会远远大于这个值。
-## TCP 的拥塞控制是怎么实现的?
-
-在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
+**糊涂窗口综合征(Silly Window Syndrome,SWS)** 指的是发送方或接收方不断以很小的粒度发送数据、通告窗口,导致网络中充满“头部很大、载荷很小”的小包,传输效率很差。
-
+
-为了进行拥塞控制,TCP 发送方要维持一个 **拥塞窗口(cwnd)** 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
+常见优化有几类:
-TCP 的拥塞控制采用了四种算法,即 **慢开始**、 **拥塞避免**、**快重传** 和 **快恢复**。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
+- **接收方侧 SWS 避免**:不要每释放一点点缓冲区就立刻通告新窗口,而是等到可用空间达到一定阈值后再更新窗口。
+- **发送方侧 Nagle 算法**:如果还有未确认的小包在网络中,新的小数据先缓存起来,等收到 ACK 或凑够 MSS 后再发送。
+- **延迟 ACK**:接收方不一定每收到一个段就马上 ACK,可以稍等一小段时间,看能否和反向数据一起发送,或对多个段合并确认。它本身是 ACK 生成策略,不是专门为 SWS 设计的机制,但会和小包发送策略发生交互。
-- **慢开始:** 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd 初始值为 1,每经过一个传播轮次,cwnd 加倍。
-- **拥塞避免:** 拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送方的 cwnd 加 1.
-- **快重传与快恢复:** 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。 当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。
+需要注意,Nagle 算法和延迟 ACK 在某些小包交互场景下可能相互等待,带来几十毫秒级的额外延迟。对延迟敏感的交互式应用,常见做法是通过 `TCP_NODELAY` 关闭 Nagle。代价是小包数量可能增加,包头开销和系统中断压力也会升高。批量响应或文件发送更适合聚合写入;在 Linux 上还可以结合 `TCP_CORK` 这类平台相关选项控制“攒包”时机。
-## ARQ 协议了解吗?
-
-**自动重传请求**(Automatic Repeat-reQuest,ARQ)是 OSI 模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认信息(Acknowledgements,就是我们常说的 ACK),它通常会重新发送,直到收到确认或者重试超过一定的次数。
-
-ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。
-
-### 停止等待 ARQ 协议
-
-停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复 ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;
-
-在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认。
+## TCP 的拥塞控制是怎么实现的?
-**1) 无差错情况:**
+在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就会下降,表现为排队变长、延迟升高、丢包增加。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
-发送方发送分组,接收方在规定时间内收到,并且回复确认.发送方再次发送。
+
-**2) 出现差错情况(超时重传):**
+为了进行拥塞控制,TCP 发送方要维持一个 **拥塞窗口(cwnd)** 的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接收窗口中较小的一个。
-停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为 **自动重传请求 ARQ** 。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。
+按 RFC 5681 / Reno 系的基础框架,TCP 拥塞控制常拆成四个机制来讲,即 **慢开始**、**拥塞避免**、**快速重传(Fast Retransmit)** 和 **快恢复**。现代系统里的 CUBIC、BBR、DCTCP 会在这个基础上有不同实现和状态机。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
-**3) 确认丢失和确认迟到**
+- **慢开始**:慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,可能会引起网络阻塞,因为现在还不知道网络的负荷情况。较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口。慢开始并不意味着一开始只能发送 1 个 MSS。RFC 6928 提议并实验性允许把初始窗口从 2~4 个段提高到最多 10 个段(IW10),很多现代实现采用了类似 IW10 的默认值,但仍要以具体系统配置为准。以常见 MSS 1460 字节计算,10 个 MSS 在首个 RTT 内大约可以发送 14 KB 数据,这对 HTTP 短连接和页面首屏加载很重要。慢开始阶段的关键是:根据 ACK 反馈快速增大窗口,通常表现为每经过一个 RTT,`cwnd` 近似翻倍。
+- **拥塞避免**:拥塞避免算法的思路是让拥塞窗口 `cwnd` 缓慢增大。简化理解是每个 RTT 大约增加 1 个 MSS;实现上通常通过每个 ACK 小幅增加 `cwnd` 来近似线性增长。慢开始会在 `cwnd` 达到慢开始门限(ssthresh)后进入拥塞避免。`ssthresh` 初始值通常设得比较大,第一次有效调整往往发生在检测到丢包之后。
+- **快速重传**:发送方收到 3 个 duplicate ACK,也就是连续 3 个 ACK 都在确认同一个旧的累计确认号时,通常认为后面某个段丢失,于是不等 RTO 超时就重传缺失的数据段。
+- **快恢复**:下面是 Reno 语境下的经典快恢复流程。收到第 3 个 duplicate ACK 时,将 `ssthresh` 设置为当前拥塞窗口的一半;重传丢失的数据段,并将 `cwnd` 设置为 `ssthresh + 3 × MSS`;后续每多收到一个 duplicate ACK,`cwnd` 再增加 1 个 MSS;当收到新的 ACK,说明重传的数据已经被确认,将 `cwnd` 降回 `ssthresh`,进入拥塞避免阶段。快恢复不直接回到慢开始,是因为重复 ACK 说明后续数据仍然能到达接收方,网络并没有完全不可用。现代 TCP 如果启用了 SACK、NewReno、CUBIC 或 RACK/TLP,丢包恢复过程会更复杂,但理解 Reno 仍然是入门基础。
-- **确认丢失**:确认消息在传输过程丢失。当 A 发送 M1 消息,B 收到后,B 向 A 发送了一个 M1 确认消息,但却在传输过程中丢失。而 A 并不知道,在超时计时过后,A 重传 M1 消息,B 再次收到该消息后采取以下两点措施:1. 丢弃这个重复的 M1 消息,不向上层交付。 2. 向 A 发送确认消息。(不会认为已经发送过了,就不再发送。A 能重传,就证明 B 的确认消息丢失)。
-- **确认迟到**:确认消息在传输过程中迟到。A 发送 M1 消息,B 收到并发送确认。在超时时间内没有收到确认消息,A 重传 M1 消息,B 仍然收到并继续发送确认消息(B 收到了 2 份 M1)。此时 A 收到了 B 第二次发送的确认消息。接着发送其他数据。过了一会,A 收到了 B 第一次发送的对 M1 的确认消息(A 也收到了 2 份确认消息)。处理如下:1. A 收到重复的确认后,直接丢弃。2. B 收到重复的 M1 后,也直接丢弃重复的 M1。
+快速重传对单个报文段丢失很有效,但如果一个窗口内有多个报文段同时丢失,仅靠重复 ACK 很难一次性告诉发送方所有“洞”在哪里。这也是 SACK 出现的重要原因:接收方可以显式告诉发送方哪些数据段已经收到,发送方只重传缺失的部分。
-### 连续 ARQ 协议
+需要注意的是,上面讲的慢开始、拥塞避免、快速重传、快恢复,是理解 TCP 拥塞控制的经典基础框架。现代操作系统通常还会在此基础上使用更复杂的拥塞控制算法:
-连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
+- **CUBIC**:已经由 RFC 9438 更新为标准轨 TCP 拥塞控制算法,并取代旧版 CUBIC 规范。CUBIC 用三次函数调整拥塞窗口,在高带宽、长 RTT 链路上比 Reno 的线性增长更友好,目前已被 Linux、Windows、Apple 等主流协议栈采用为默认拥塞控制算法之一。
+- **BBR**:Google 提出的基于模型(model-based)的拥塞控制算法,通过估计瓶颈带宽和最小 RTT 来控制发送速率,目标是高吞吐和低排队延迟。在 bufferbloat 明显的链路上可能表现更好。但 BBR 的具体效果受版本、队列管理、竞争流类型、RTT 公平性和部署环境影响,不能简单理解成“无脑替代 CUBIC”。
+- **DCTCP**:主要用于受控数据中心网络,依赖 ECN 标记估算拥塞程度,目标是在浅缓冲交换机场景下降低排队延迟。它不适合直接拿到公网环境里泛用。
-- **优点:** 信道利用率高,容易实现,即使确认丢失,也不必重传。
-- **缺点:** 不能向发送方反映出接收方已经正确收到的所有分组的信息。 比如:发送方发送了 5 条 消息,中间第三条丢失(3 号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。
+不过,慢开始、拥塞避免、快速重传、快恢复依然是理解这些现代算法的基础。
-## 超时重传如何实现?超时重传时间怎么确定?
+还有一个工程上很重要的边界:丢包不一定等于拥塞。传统 Reno/CUBIC 主要把丢包视为拥塞信号,但无线链路误码、路径切换、设备队列溢出也可能导致丢包;ECN 则可以在不丢包的情况下反馈拥塞。对比拥塞算法时,也不要只看平均吞吐量,还要看 P95/P99 RTT、丢包率、重传率、队列长度、与 CUBIC/Reno 共存表现,以及具体内核版本和参数配置。
-当发送方发送数据之后,它启动一个定时器,等待目的端确认收到这个报文段。接收端实体对已成功收到的包发回一个相应的确认信息(ACK)。如果发送端实体在合理的往返时延(RTT)内未收到确认消息,那么对应的数据包就被假设为[已丢失](https://zh.wikipedia.org/wiki/丢包)并进行重传。
+## 总结
-- RTT(Round Trip Time):往返时间,也就是数据包从发出去到收到对应 ACK 的时间。
-- RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传。
+TCP 的可靠性不是“保证网络不丢包”,而是在不可靠的 IP 网络之上,通过一组机制让应用层看到的是**有序、无重复、尽量完整的字节流**。它的核心可以概括为四点:
-RTO 的确定是一个关键问题,因为它直接影响到 TCP 的性能和效率。如果 RTO 设置得太小,会导致不必要的重传,增加网络负担;如果 RTO 设置得太大,会导致数据传输的延迟,降低吞吐量。因此,RTO 应该根据网络的实际状况,动态地进行调整。
+1. **用序列号和 ACK 确认数据状态**:TCP 给字节流编号,接收方通过 ACK 告诉发送方哪些数据已经收到,发送方据此判断哪些数据还在路上、哪些数据需要继续等待。
+2. **用重传机制补齐丢失数据**:超时重传负责兜底,快速重传用于更快发现单段丢失,SACK/D-SACK 则让发送方更精确地知道哪些数据已经到达、哪些重传可能是误判。
+3. **用滑动窗口做流量控制**:接收方通过 `rwnd` 告诉发送方自己还能接收多少数据,发送方根据接收窗口控制在途数据量,避免把接收缓冲区打爆。
+4. **用拥塞控制保护网络**:发送方通过 `cwnd` 估计网络承载能力,在慢开始、拥塞避免、快速重传、快恢复以及 CUBIC、BBR 等算法的配合下,尽量避免把过多数据注入网络。
-RTT 的值会随着网络的波动而变化,所以 TCP 不能直接使用 RTT 作为 RTO。为了动态地调整 RTO,TCP 协议采用了一些算法,如加权移动平均(EWMA)算法,Karn 算法,Jacobson 算法等,这些算法都是根据往返时延(RTT)的测量和变化来估计 RTO 的值。
+一句话总结:TCP 不是让网络变得可靠,而是通过**编号、确认、重传、排序去重、流量控制和拥塞控制**,在不可靠网络之上“拼”出一个对应用层相对可靠的字节流通道。
## 参考
1. 《计算机网络(第 7 版)》
2. 《图解 HTTP》
-3. [https://www.9tut.com/tcp-and-udp-tutorial](https://www.9tut.com/tcp-and-udp-tutorial)
-4. [https://github.com/wolverinn/Waking-Up/blob/master/Computer%20Network.md](https://github.com/wolverinn/Waking-Up/blob/master/Computer%20Network.md)
-5. TCP Flow Control—[https://www.brianstorti.com/tcp-flow-control/](https://www.brianstorti.com/tcp-flow-control/)
-6. TCP 流量控制(Flow Control):
-7. TCP 之滑动窗口原理 :
-
-
+3. TCP and UDP Tutorial:
+4. Computer Network:
+5. TCP Flow Control:
+6. TCP 流量控制(Flow Control):
+7. TCP 之滑动窗口原理:
+8. RFC 9293 - Transmission Control Protocol:
+9. RFC 6928 - Increasing TCP's Initial Window:
+10. RFC 5681 - TCP Congestion Control:
+11. RFC 2018 - TCP Selective Acknowledgment Options:
+12. RFC 2883 - An Extension to the Selective Acknowledgement(SACK) Option for TCP:
+13. RFC 9438 - CUBIC for Fast and Long-Distance Networks:
+14. RFC 8257 - Data Center TCP(DCTCP):
+15. BBR: Congestion-Based Congestion Control, ACM Queue, 2016:
+16. RFC 1122 - Requirements for Internet Hosts - Communication Layers:
+17. RFC 6298 - Computing TCP's Retransmission Timer:
+18. RFC 7323 - TCP Extensions for High Performance:
diff --git a/docs/cs-basics/network/tcp-time-wait.md b/docs/cs-basics/network/tcp-time-wait.md
new file mode 100644
index 00000000000..80aa4ca57bb
--- /dev/null
+++ b/docs/cs-basics/network/tcp-time-wait.md
@@ -0,0 +1,192 @@
+---
+title: TCP TIME_WAIT 详解:为什么要等、会不会出问题、能不能复用?
+description: 深入分析 TCP TIME_WAIT 状态的两个存在原因(最后 ACK 补救机会 + 防旧包混入新连接),大量 TIME_WAIT 的危害边界与粗略估算,tcp_tw_reuse 的正确使用姿势,以及 TIME_WAIT 与 CLOSE_WAIT 的区分与线上排查思路。
+category: 计算机基础
+tag:
+ - 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: TCP,TIME_WAIT,CLOSE_WAIT,2MSL,tcp_tw_reuse,tcp_tw_recycle,四次挥手,端口耗尽,连接复用,MSL,PAWS
+---
+
+TCP 四次挥手的最后一步,主动关闭方发完 ACK 后不是立刻关闭,而是进入 `TIME_WAIT` 状态,默认要等上 60 秒。
+
+这 60 秒经常被误解:有人觉得是浪费资源,有人想着用内核参数强行关掉,有人把 `CLOSE_WAIT` 和 `TIME_WAIT` 混着排查。
+
+这篇文章回答线上最常见的几个问题:
+
+1. `TIME_WAIT` 到底在等什么?
+2. `TIME_WAIT` 大量堆积会不会真的出问题?
+3. `tcp_tw_reuse` 能不能随便开?
+4. `TIME_WAIT` 和 `CLOSE_WAIT` 怎么区分?
+
+## TIME_WAIT 不只是“等一会儿再关”
+
+ACK 都已经发出去了,为什么还要占着端口等几十秒?
+
+主动关闭方发出最后一个 ACK 后,不会立刻释放连接,而是进入 `TIME_WAIT`。RFC 9293 的连接状态图里也能看到,`TIME_WAIT` 会在 2MSL 超时后删除 TCB,并进入 `CLOSED`。
+
+这里要注意一个细节:不是“谁收到 FIN 谁就一定进入 TIME_WAIT”。被动关闭方收到 FIN 后,通常会先进入 `CLOSE_WAIT`,等待本端应用处理完剩余数据并调用 `close()` 或 `shutdown()`。更常见的情况是,主动关闭方收到对端最后的 FIN,并回复最后一个 ACK 后,进入 `TIME_WAIT`。
+
+**谁主动关闭连接,谁就更容易进入 TIME_WAIT。** 比如客户端主动断开 HTTP 短连接,`TIME_WAIT` 往往出现在客户端;如果服务端主动断开连接,服务端也可能堆出大量 `TIME_WAIT`。
+
+看起来像是多等了一会儿,实际上是在解决两个问题。
+
+## 第一个原因:让最后一个 ACK 有补救机会
+
+主动关闭方发送最后一个 ACK 后,如果这个 ACK 在网络中丢了,被动关闭方会以为自己的 FIN 没被确认,于是重发 FIN。主动关闭方还在 `TIME_WAIT` 里,就能再次回复 ACK;如果它已经进入 `CLOSED`,就可能回 RST,让对端感知为异常关闭或连接被重置。
+
+```mermaid
+sequenceDiagram
+ participant A as 主动关闭方
+ participant B as 被动关闭方
+
+ B->>A: FIN
+ A-->>B: ACK 丢失
+ Note over A: A 进入 TIME_WAIT
没有立刻释放连接
+ B->>A: 重传 FIN
+ A-->>B: 再次 ACK
+ Note over B: B 收到 ACK 后进入 CLOSED
+```
+
+**MSL(Maximum Segment Lifetime)** 是报文段在网络中的最大生存时间。2MSL 不是一次请求-响应的最大 RTT,而是一个保守等待窗口:既给最后 ACK 丢失后的 FIN 重传留出处理机会,也尽量保证旧连接中的延迟报文从网络中消失。
+
+需要注意,RFC 里的 MSL 是协议层概念,具体系统实现可能不同。Linux 常见实现中,`TIME_WAIT` 保留时间通常是 60 秒。还有一个常见误区:`tcp_fin_timeout` 控制的是 orphaned connection 的 `FIN_WAIT_2` 超时,不是 `TIME_WAIT`。想缓解 `TIME_WAIT` 带来的端口压力,优先看连接复用、端口范围、主动关闭方和 `tcp_tw_reuse` 条件,而不是试图用 `tcp_fin_timeout` 缩短 `TIME_WAIT`。
+
+## 第二个原因:别让旧连接的包混进新连接
+
+TCP 连接靠四元组定位:源 IP、源端口、目的 IP、目的端口。如果旧连接刚关闭,立刻用同一个四元组建立新连接,旧连接里延迟到达的数据包可能刚好落在新连接接收窗口里,被当成新连接的数据处理。
+
+举个例子:
+
+```text
+旧连接:client:50000 -> server:443
+服务端发出的 SEQ=301 数据包在网络里绕了一圈,迟迟没到。
+
+旧连接关闭后,客户端很快复用了同一个源端口:
+新连接:client:50000 -> server:443
+
+这时旧的 SEQ=301 抵达客户端。
+如果它刚好落在新连接接收窗口里,就有可能被误收。
+```
+
+TCP 序列号空间是 0 到 2^32 - 1,会按模 2^32 回绕,所以不能只靠序列号永久区分新老报文。实际系统还有时间戳、PAWS(Protection Against Wrapped Sequences)、随机 ISN 等保护,但它们不是“完全替代 TIME_WAIT”的万能方案。RFC 1337 也讨论过旧重复报文导致的 TIME_WAIT 风险。
+
+## 大量 TIME_WAIT 到底有没有问题?
+
+`TIME_WAIT` 本身是正常状态。真正的问题通常出现在主动关闭方短时间内创建大量到同一个目标 IP + 目标端口的连接,导致本地临时端口被占住。
+
+Linux 本地临时端口范围可通过 `net.ipv4.ip_local_port_range` 查看和调整。上游内核文档里的默认范围是 `32768 60999`,实际环境以本机输出为准:
+
+```bash
+cat /proc/sys/net/ipv4/ip_local_port_range
+```
+
+如果客户端短时间内反复连接同一个目标 IP + 目标端口,旧连接又都停在 `TIME_WAIT`,本地可用临时端口可能被占满,导致新连接无法分配源端口,常见报错如:
+
+```text
+Cannot assign requested address
+```
+
+可以按这个思路判断:
+
+- **如果服务端上看到很多 TIME_WAIT**:先看是不是服务端主动关闭了连接,比如服务端主动断开短连接、网关主动关闭上游连接、连接池主动淘汰连接。
+- **如果客户端或网关上看到很多 TIME_WAIT**:重点看是否存在短连接风暴、连接池未复用、HTTP keep-alive 没打开、上游频繁断连。
+
+还可以做一个粗略估算:
+
+```text
+同一目标 IP:Port 的短连接上限 ≈ 可用临时端口数 / TIME_WAIT 保留时间
+```
+
+比如默认端口范围 `32768~60999`,大约 2.8 万个端口。如果 `TIME_WAIT` 保留约 60 秒,那么同一目标 IP:Port 上持续新建短连接的上限大约是数百 QPS 量级。实际结果还会受到连接复用、端口保留、NAT、内核策略和不同远端四元组复用规则影响,不能只看 `TIME_WAIT` 总数就下结论。
+
+## 为什么不建议随便开 tcp_tw_reuse?
+
+`tcp_tw_reuse` 允许在协议认为安全的条件下,为新的主动连接复用 `TIME_WAIT` socket。它看起来像是缓解端口压力的捷径,但这类参数改变的是 TCP 对旧连接报文的等待策略,不能当成通用开关。
+
+这里要分三层看:
+
+1. **它依赖时间戳等条件判断“新报文是否足够新”**。时间戳可以过滤一部分旧报文,但不是所有异常都能覆盖。RFC 1337 重点讨论过 `TIME_WAIT` 状态被旧 RST 等报文提前终止的风险。旧数据段如果落入新连接可接受窗口,可能造成新旧数据混淆;旧 ACK 的影响则依赖序列号、窗口和实现细节,不宜和旧 RST 直接并列成同一种断连风险。
+2. **当前上游 Linux 文档中,`tcp_tw_reuse` 可取 0/1/2,默认值为 2**,表示仅允许 loopback 流量复用;`1` 才是全局开启。但旧版内核文档、发行版 man page 或历史资料可能仍写作“默认关闭”,实际机器必须以 `sysctl net.ipv4.tcp_tw_reuse` 为准。内核文档也明确提示,不要在没有专家建议或明确需求时修改。
+3. **不要把 `tcp_tw_reuse` 和已经废弃的 `tcp_tw_recycle` 搞混**。`tcp_tw_recycle` 在 NAT 环境下会导致时间戳冲突,大量连接被异常丢弃,Linux 4.12 之后已经被移除。网上很多老文章仍然会建议同时打开 `tcp_tw_reuse` 和 `tcp_tw_recycle`,这类配置不要照搬。
+
+一句话:`tcp_tw_reuse` 可以讨论,但必须结合 Linux 版本、是否 loopback、是否经过 NAT、是否启用时间戳、是否真的存在端口耗尽来判断。能在应用层解决的,优先在应用层解决。
+
+## TIME_WAIT 和 CLOSE_WAIT:一个正常等待,一个更像应用没收尾
+
+排查连接状态时,`CLOSE_WAIT` 通常比 `TIME_WAIT` 更值得警惕。
+
+收到对端 FIN 后,本端内核会回 ACK,然后进入 `CLOSE_WAIT`,等待应用处理完剩余数据并调用 `close()` 或 `shutdown()`。在 Java 服务里,`CLOSE_WAIT` 堆积经常和连接没有正确关闭有关。比如手写 Socket、HTTP 客户端响应体没有 close、异常分支提前 return、连接池连接没有归还,都可能让内核已经 ACK 了对端 FIN,但应用迟迟不调用 close。
+
+可以先按这个思路判断:
+
+- **TIME_WAIT**:主动关闭方在等 2MSL,通常是协议设计的一部分。
+- **CLOSE_WAIT**:被动关闭方已经知道对端不发了,但本端应用还没关闭 socket。大量堆积时,优先怀疑应用代码没释放连接、线程卡住、连接池归还异常、读写流程没有走到 finally。
+
+| 状态 | 常见出现方 | 含义 | 排查方向 |
+| ---------- | ---------- | ----------------------------------- | ------------------------------------------------- |
+| TIME_WAIT | 主动关闭方 | 等最后 ACK 重传机会,也等旧报文消失 | 短连接、连接池、keep-alive、端口范围 |
+| CLOSE_WAIT | 被动关闭方 | 对端已关闭,本端应用还没 close | 代码是否释放 socket、线程是否卡住、连接池是否泄漏 |
+
+## 排查时别只盯着数量,要先看谁在主动关闭
+
+
+
+看到大量 `TIME_WAIT` 或 `CLOSE_WAIT`,可以先用下面几条命令定位方向:
+
+`ss` 是 Linux 上 `iproute2` 提供的命令,macOS 默认没有。如果你的开发环境是 macOS,可以用 `netstat` 和 `lsof` 替代。
+
+```bash
+# Linux:查看各 TCP 状态数量
+ss -ant | awk 'NR>1 {cnt[$1]++} END {for (s in cnt) print s, cnt[s]}'
+
+# macOS:查看各 TCP 状态数量
+netstat -anp tcp | awk '$1 ~ /^tcp/ {cnt[$NF]++} END {for (s in cnt) print s, cnt[s]}'
+
+# Linux:查看 TIME-WAIT 主要集中在哪些目标
+ss -ant state time-wait | awk 'NR>1 {print $5}' | sort | uniq -c | sort -nr | head
+
+# macOS:查看 TIME-WAIT 主要集中在哪些远端
+netstat -anp tcp | awk '$1 ~ /^tcp/ && $NF=="TIME_WAIT" {print $(NF-1)}' | sort | uniq -c | sort -nr | head
+
+# Linux:查看 CLOSE-WAIT 对应哪个进程(需要 sudo 才能看到进程信息)
+sudo ss -tanp state close-wait
+
+# macOS:查看 CLOSE-WAIT 对应哪个进程
+sudo lsof -nP -iTCP -sTCP:CLOSE_WAIT
+
+# Linux:查看监听 socket 的 accept queue 情况
+ss -ltn
+```
+
+
+
+命令背后的判断:
+
+- **TIME_WAIT 集中在某个远端服务**:检查是否短连接太多、HTTP 连接复用没生效、连接池配置过小、连接池被频繁销毁,或者对端频繁主动断开。
+- **CLOSE_WAIT 集中在某个本地进程**:优先查应用代码,尤其是异常分支有没有关闭响应体、socket 或连接对象。
+- **LISTEN socket 的 Recv-Q 长时间接近 Send-Q**:重点排查 accept queue 堆积,看看应用 accept 是否及时、线程池是否卡住、backlog 配置是否过小。
+- 如果是网关、代理、爬虫、压测客户端,`TIME_WAIT` 更常见;如果是 Java 服务端内部依赖调用泄漏,`CLOSE_WAIT` 更常见。
+
+## 克制的优化建议
+
+按优先级排查:
+
+1. **优先减少不必要的短连接**:开启 HTTP keep-alive,复用连接池。
+2. **确认谁在主动关闭连接**:服务端、客户端、网关、连接池都有可能成为主动关闭方。
+3. **检查应用侧资源释放**:尤其是 HTTP 响应体、Socket、数据库连接、连接池连接归还。
+4. **扩大本地端口范围**:在客户端短连接确实很高、且存在端口耗尽证据时,再考虑调整 `ip_local_port_range`。
+5. **最后才看内核参数**:`tcp_tw_reuse`、`tcp_abort_on_overflow`、`tcp_syncookies` 都要结合 Linux 版本、业务连接模型、是否经过 NAT、是否被攻击、是否有真实观测数据来判断,不建议直接照抄网上配置。
+
+`TIME_WAIT` 多,不一定是故障;`CLOSE_WAIT` 多,通常要先看代码。这两个状态看起来都像“连接没关干净”,但问题方向完全不同。
+
+## 参考
+
+- RFC 9293: Transmission Control Protocol(TCP):
+- RFC 1337: TIME-WAIT Assassination Hazards in TCP:
+- Linux 内核 ip-sysctl 文档:
+- SoByte - 为什么 TCP 需要 TIME_WAIT 状态:
+
+
diff --git a/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md b/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md
index 906d16fae2e..2a2357bb26c 100644
--- a/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md
+++ b/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md
@@ -1,79 +1,343 @@
---
-title: 访问网页的全过程(知识串联)
+title: 从输入 URL 到页面展示到底发生了什么?
+description: 串联从输入 URL 到页面渲染的完整链路,涵盖 DNS、TCP、HTTP、TLS、ARP、数据封装与浏览器渲染,助力面试与实践理解。
category: 计算机基础
tag:
- 计算机网络
+head:
+ - - meta
+ - name: keywords
+ content: 访问网页流程,DNS,TCP 建连,HTTP 请求,TLS 握手,ARP,资源加载,浏览器渲染,关闭连接
---
-开发岗中总是会考很多计算机网络的知识点,但如果让面试官只考一道题,便涵盖最多的计网知识点,那可能就是 **网页浏览的全过程** 了。本篇文章将带大家从头到尾过一遍这道被考烂的面试题,必会!!!
+在浏览器地址栏输入 URL 到页面展示,背后会串起 DNS、TCP、TLS、HTTP、ARP、数据封装与浏览器渲染等多个环节。
-总的来说,网络通信模型可以用下图来表示,也就是大家只要熟记网络结构五层模型,按照这个体系,很多知识点都能顺出来了。访问网页的过程也是如此。
+这道题经常被用来考察计网整体理解,因为它能把应用层、传输层、网络层和链路层的知识点都串起来。只背单个协议容易断片,按访问网页的全过程走一遍,会清楚很多。
-
+这篇文章主要回答几个问题:
-开始之前,我们先简单过一遍完整流程:
+1. 输入 URL 后,浏览器会先做哪些本地处理?
+2. DNS 解析域名的过程是怎样的?
+3. TCP 连接如何建立?如果用了 HTTPS,TLS 握手又做了什么?
+4. HTTP 请求和响应的交互流程是什么?
+5. 数据包从主机到服务器,经过了哪些层的封装和转发?
+6. 浏览器拿到 HTML 后,如何继续加载 CSS、JS、图片等资源并渲染页面?
+7. 页面加载完成后,连接会如何复用或关闭?
-1. 在浏览器中输入指定网页的 URL。
-2. 浏览器通过 DNS 协议,获取域名对应的 IP 地址。
-3. 浏览器根据 IP 地址和端口号,向目标服务器发起一个 TCP 连接请求。
-4. 浏览器在 TCP 连接上,向服务器发送一个 HTTP 请求报文,请求获取网页的内容。
-5. 服务器收到 HTTP 请求报文后,处理请求,并返回 HTTP 响应报文给浏览器。
-6. 浏览器收到 HTTP 响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式,同时根据 HTML 中的其他资源的 URL(如图片、CSS、JS 等),再次发起 HTTP 请求,获取这些资源的内容,直到网页完全加载显示。
-7. 浏览器在不需要和服务器通信时,可以主动关闭 TCP 连接,或者等待服务器的关闭请求。
+总的来说,网络通信模型可以用下图来表示。访问网页的过程,就是数据从应用层逐层向下封装,经物理网络传输到对端,再逐层向上解封装的过程。
-## 应用层
+
-一切的开始——打开浏览器,在地址栏输入 URL,回车确认。那么,什么是 URL?访问 URL 有什么用?
+开始之前,先简单过一遍完整流程:
-### URL
+1. **浏览器解析 URL 并检查缓存**:浏览器解析 URL 的各组成部分,并检查 HTTP 缓存(强缓存、协商缓存)是否已有该资源的有效副本。
+2. **DNS 解析**:浏览器通过 DNS 协议,获取域名对应的 IP 地址。
+3. **建立 TCP 连接**:浏览器根据 IP 地址和端口号,向目标服务器发起 TCP 三次握手,建立可靠传输通道。
+4. **TLS 握手(HTTPS)**:如果使用 HTTPS,在 TCP 连接建立后还要进行 TLS 握手,协商加密密钥并验证服务器身份。
+5. **发送 HTTP 请求**:浏览器在连接上向服务器发送 HTTP 请求报文,请求获取网页内容。
+6. **服务器处理并返回响应**:服务器收到请求后处理并返回 HTTP 响应报文。
+7. **浏览器解析与渲染**:浏览器解析 HTML、CSS,执行 JavaScript,并加载页面中引用的其他资源(图片、字体等)。
+8. **连接管理**:页面加载完成后,连接根据 keep-alive 策略复用或关闭。
-URL(Uniform Resource Locators),即统一资源定位器。网络上的所有资源都靠 URL 来定位,每一个文件就对应着一个 URL,就像是路径地址。理论上,文件资源和 URL 一一对应。实际上也有例外,比如某些 URL 指向的文件已经被重定位到另一个位置,这样就有多个 URL 指向同一个文件。
+下面按这个流程逐一展开。
+
+## 第一步:解析 URL 与检查缓存
+
+打开浏览器,在地址栏输入 URL 并回车。浏览器做的第一件事不是发请求,而是解析 URL 并检查是否可以直接使用本地缓存。
+
+### URL 是什么
+
+URL(Uniform Resource Locator,统一资源定位符)是互联网上资源的唯一地址。网络上的每个可访问资源都对应一个 URL,理论上文件和 URL 一一对应。实际上也有例外,比如重定向或 CDN 场景下,多个 URL 可能指向同一份资源。
### URL 的组成结构

-1. 协议。URL 的前缀通常表示了该网址采用了何种应用层协议,通常有两种——HTTP 和 HTTPS。当然也有一些不太常见的前缀头,比如文件传输时用到的`ftp:`。
-2. 域名。域名便是访问网址的通用名,这里也有可能是网址的 IP 地址,域名可以理解为 IP 地址的可读版本,毕竟绝大部分人都不会选择记住一个网址的 IP 地址。
-3. 端口。如果指明了访问网址的端口的话,端口会紧跟在域名后面,并用一个冒号隔开。
-4. 资源路径。域名(端口)后紧跟的就是资源路径,从第一个`/`开始,表示从服务器上根目录开始进行索引到的文件路径,上图中要访问的文件就是服务器根目录下`/path/to/myfile.html`。早先的设计是该文件通常物理存储于服务器主机上,但现在随着网络技术的进步,该文件不一定会物理存储在服务器主机上,有可能存放在云上,而文件路径也有可能是虚拟的(遵循某种规则)。
-5. 参数。参数是浏览器在向服务器提交请求时,在 URL 中附带的参数。服务器解析请求时,会提取这些参数。参数采用键值对的形式`key=value`,每一个键值对使用`&`隔开。参数的具体含义和请求操作的具体方法有关。
-6. 锚点。锚点顾名思义,是在要访问的页面上的一个锚。要访问的页面大部分都多于一页,如果指定了锚点,那么在客户端显示该网页是就会定位到锚点处,相当于一个小书签。值得一提的是,在 URL 中,锚点以`#`开头,并且**不会**作为请求的一部分发送给服务端。
+一个完整的 URL 由以下几部分组成:
+
+1. **协议**(Scheme):URL 的前缀表示采用的协议,最常见的是 `http` 和 `https`,也有文件传输的 `ftp:` 等。
+2. **域名**(Host):访问目标的通用名,也可以直接使用 IP 地址。域名本质上是 IP 地址的可读版本。
+3. **端口**(Port):紧跟域名后面,用冒号隔开。HTTP 默认 80,HTTPS 默认 443,如果使用默认端口可以省略。
+4. **资源路径**(Path):从第一个 `/` 开始,表示服务器上的资源位置。早期设计中路径对应服务器上的物理文件,现在通常是后端路由映射的虚拟路径。
+5. **查询参数**(Query):`?` 之后的部分,采用 `key=value` 键值对形式,多个参数用 `&` 隔开。服务器解析请求时会提取这些参数。
+6. **锚点**(Fragment):`#` 之后的部分,用于定位到页面内的某个位置。锚点**不会**作为请求的一部分发送给服务端,仅由浏览器本地处理。
+
+### 浏览器缓存检查
+
+解析完 URL 之后,浏览器会先检查 HTTP 缓存,看是否已经有该资源的有效副本:
+
+1. **强缓存**:检查 `Cache-Control`(如 `max-age`)或 `Expires` 头,判断缓存是否仍在有效期内。如果有效,直接使用缓存,跳过后续所有网络请求。
+2. **协商缓存**:强缓存未命中时,浏览器向服务器发送验证请求(携带 `If-Modified-Since` 或 `If-None-Match`),服务器判断资源是否变化。如果未变化,返回 `304 Not Modified`,浏览器继续使用本地缓存;如果已变化,返回 `200 OK` 和新资源。
+
+HTTP 缓存命中时,整个访问过程在此结束,无需发起网络请求。
+
+### 域名解析准备
+
+如果 HTTP 缓存未命中,浏览器需要向服务器发起网络请求,首先要拿到域名对应的 IP 地址。在正式发起 DNS 查询之前,浏览器还会依次检查:
+
+1. **浏览器 DNS 缓存**:浏览器自身维护了一份 DNS 缓存,先看有没有该域名的记录。
+2. **操作系统 DNS 缓存**:浏览器缓存未命中时,查询操作系统的 DNS 缓存。
+3. **hosts 文件**:操作系统会检查本地 `hosts` 文件,看是否有域名到 IP 地址的直接映射。如果有,直接使用该 IP 地址,跳过 DNS 解析。
+
+如果以上都没有命中,浏览器就需要发起完整的 DNS 查询。
+
+## 第二步:DNS 解析
+
+DNS(Domain Name System,域名系统)要解决的是**域名和 IP 地址的映射问题**。域名只是便于人类记忆的名字,网络通信真正需要的是 IP 地址。
+
+### DNS 解析过程
+
+浏览器拿到域名后,DNS 解析通常按以下步骤进行:
+
+1. **浏览器 DNS 缓存**:浏览器自身维护了一份 DNS 缓存,先检查缓存中是否有该域名的记录且未过期。
+2. **操作系统 DNS 缓存**:浏览器缓存未命中时,向操作系统发起 DNS 查询请求。操作系统也有自己的 DNS 缓存。
+3. **本地 DNS 服务器**:操作系统配置的本地 DNS 服务器(通常由 ISP 提供,或使用公共 DNS 如 `8.8.8.8`、`114.114.114.114`)。本地 DNS 服务器如果有缓存且未过期,直接返回结果。
+4. **递归/迭代查询**:本地 DNS 服务器缓存未命中时,它会代替客户端发起迭代查询——先问根 DNS 服务器,再问顶级域 DNS 服务器(如 `.com`),最后问权威 DNS 服务器,逐级获取目标 IP 地址。
+5. **返回结果并缓存**:本地 DNS 服务器拿到最终结果后返回给客户端,同时在本地缓存一份,供后续查询使用。
+
+下图展示了一个典型的 DNS 迭代查询过程:
+
+
+
+实际场景中,本地 DNS 服务器通常已经缓存了大量 TLD 服务器地址,多数查询不需要从根服务器开始,跳过根服务器直接查 TLD 的情况非常普遍。
+
+> 关于 DNS 的更多细节(DNS 服务器层级、递归/迭代查询的区别、DNS 记录类型、为什么通常用 UDP 等),可以参考 [DNS 域名系统详解(应用层)](https://javaguide.cn/cs-basics/network/dns.html) 这篇文章。
+
+## 第三步:建立 TCP 连接
+
+拿到目标服务器的 IP 地址后,浏览器需要与服务器建立一个可靠的传输通道。HTTP 基于 TCP 协议,所以在发送 HTTP 请求之前必须先完成 TCP 三次握手。
+
+### TCP 三次握手
+
+TCP 三次握手的目的是**同步双方的初始序列号**,并**确认双方的收发路径是可用的**。
+
+
+
+1. **第一次握手(SYN)**:客户端发送 SYN 报文段,携带自己的初始序列号 `seq=x`,进入 `SYN_SENT` 状态。
+2. **第二次握手(SYN+ACK)**:服务端收到后回复 SYN+ACK,携带自己的初始序列号 `seq=y`,确认号 `ack=x+1`,进入 `SYN_RCVD` 状态。
+3. **第三次握手(ACK)**:客户端收到后发送 ACK,确认号 `ack=y+1`,双方进入 `ESTABLISHED` 状态,连接建立完成。
+
+三次握手的设计不是为了「多走一次」,而是让双方都能确认:对方能收到自己的数据,自己也能收到对方的数据。两次握手做不到这一点——服务端在第二次握手后,还不知道客户端是否收到了自己的 SYN+ACK。
+
+> 关于三次握手的详细分析、半连接队列/全连接队列、SYN Flood 防护等内容,可以参考 [TCP 三次握手和四次挥手(传输层)](https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html)。
+
+### 如果是 HTTPS:TLS 握手
+
+如果 URL 的协议是 HTTPS,TCP 连接建立之后还要进行 TLS 握手。TLS 的核心目标是三个:**加密**(防窃听)、**认证**(防冒充)、**完整性校验**(防篡改)。
+
+TLS 握手大致流程(以 TLS 1.2 RSA 密钥交换为例):
+
+1. **Client Hello**:客户端发送支持的 TLS 版本、加密套件列表和一个随机数。
+2. **Server Hello**:服务端从中选择一个加密套件,返回自己的证书、另一个随机数。
+3. **密钥交换**:客户端验证服务端证书的合法性(通过 CA 签名验证),然后生成预主密钥(Pre-Master Secret),用服务端公钥加密后发送给服务端。双方根据预主密钥和之前交换的两个随机数,计算出对称加密的会话密钥。
+4. **完成**:双方用会话密钥加密通信,握手结束。
+
+需要注意的是,上述流程描述的是 TLS 1.2 中基于 RSA 的密钥交换方式。现代 HTTPS 主流采用的是 ECDHE 密钥交换(TLS 1.2 和 TLS 1.3 均支持),密钥材料不是直接用公钥加密传输的,而是通过椭圆曲线 Diffie-Hellman 交换各自生成,并且具备前向安全性(Forward Secrecy)——即使服务端私钥泄露,历史通信也不会被解密。TLS 1.3 进一步简化了握手流程,将往返次数从 2-RTT 减少到 1-RTT,并移除了 RSA 静态密钥交换等不安全的密码套件。
+
+TLS 握手完成后,后续的 HTTP 请求和响应都会使用协商好的对称密钥进行加密传输。HTTPS 的安全性来自 TLS 层,而不是 HTTP 协议本身的改变。
+
+> 关于 TLS 的加密原理(非对称加密、对称加密、数字签名、CA 证书)的详细分析,可以参考 [HTTP vs HTTPS(应用层)](https://javaguide.cn/cs-basics/network/http-vs-https.html)。关于 RSA 和 ECDHE 两种密钥交换方式的区别,可以参考 [HTTPS RSA vs ECDHE 握手过程](https://javaguide.cn/cs-basics/network/https-rsa-vs-ecdhe.html)。
+
+## 第四步:发送 HTTP 请求
+
+TCP 连接(以及可能的 TLS 通道)建立好之后,浏览器就可以发送 HTTP 请求了。
+
+### HTTP 请求报文结构
+
+一个典型的 HTTP/1.1 请求报文如下:
+
+```http
+GET /index.html HTTP/1.1
+Host: www.example.com
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
+Accept: text/html,application/xhtml+xml
+Accept-Encoding: gzip, deflate, br
+Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
+Connection: keep-alive
+Cookie: session_id=abc123
+```
+
+各部分含义:
+
+- **请求行**:`GET /index.html HTTP/1.1` —— 请求方法(GET)、资源路径(`/index.html`)、协议版本(HTTP/1.1)。
+- **Host 头**:指定目标主机名。这是 HTTP/1.1 的强制要求,因为同一台服务器(同一个 IP)可能通过虚拟主机托管多个网站。
+- **其他请求头**:`User-Agent`(客户端信息)、`Accept`(可接受的响应类型)、`Accept-Encoding`(支持的压缩方式)、`Cookie`(携带的状态信息)等。
+
+### 服务器处理请求
+
+服务器收到请求后,经过一系列处理生成响应:
+
+1. **接收请求**:Web 服务器(如 Nginx、Tomcat)接收并解析 HTTP 请求报文。
+2. **路由分发**:根据 URL 路径将请求路由到对应的后端处理逻辑(Controller、Servlet 等)。
+3. **业务处理**:执行具体的业务逻辑,可能涉及数据库查询、缓存读取、调用其他服务等。
+4. **构建响应**:将处理结果封装成 HTTP 响应报文。
+
+### HTTP 响应报文结构
+
+```http
+HTTP/1.1 200 OK
+Content-Type: text/html; charset=UTF-8
+Content-Encoding: gzip
+Content-Length: 1256
+Cache-Control: max-age=3600
+Set-Cookie: session_id=xyz789; Path=/
+
+
+
+...
+
+```
+
+各部分含义:
+
+- **状态行**:`HTTP/1.1 200 OK` —— 协议版本、状态码(200)、状态描述。
+- **响应头**:`Content-Type`(响应体类型)、`Content-Encoding`(压缩方式)、`Cache-Control`(缓存策略)、`Set-Cookie`(设置 Cookie)等。
+- **响应体**:请求的实际内容,如 HTML 文档、JSON 数据、图片二进制数据等。
+
+常见的状态码:
+
+| 状态码 | 类别 | 常见示例 |
+| ------ | ---------- | --------------------------------------------- |
+| 2xx | 成功 | 200 OK、206 Partial Content |
+| 3xx | 重定向 | 301 永久重定向、302 临时重定向、304 未修改 |
+| 4xx | 客户端错误 | 400 Bad Request、403 Forbidden、404 Not Found |
+| 5xx | 服务端错误 | 500 Internal Server Error、502 Bad Gateway |
+
+> 关于 HTTP 常见状态码的详细总结,可以参考 [HTTP 常见状态码总结(应用层)](https://javaguide.cn/cs-basics/network/http-status-codes.html)。
+
+## 第五步:数据包的封装与转发
+
+HTTP 请求从浏览器发出后,数据并不是直接「飞」到服务器的。它需要经过协议栈的逐层封装,在物理网络上一跳一跳地转发到目的地。
+
+### 数据封装过程
+
+应用层的 HTTP 报文,经过传输层、网络层、链路层的逐层封装,最终变成能在物理介质上传输的比特流:
+
+
+
+每一层只关心自己要添加的头部信息,并使用下层提供的服务来传输数据:
+
+- **传输层(TCP)**:添加源端口和目的端口,用序列号和确认号保证可靠传输。
+- **网络层(IP)**:添加源 IP 和目的 IP,负责寻址和路由,决定数据包从源到目的经过的路径。
+- **链路层**:添加源 MAC 和目的 MAC 地址,负责在相邻节点之间传输数据帧。
+
+### 网络层的路由转发
+
+数据包从源主机到目的主机,通常需要经过多个路由器中转。网络层的核心功能就是**路由与转发**:
+
+- **路由**:确定分组从源到目的经过的路径(由路由协议如 OSPF、BGP 等计算)。
+- **转发**:将分组从路由器的输入端口转移到合适的输出端口。
+
+每个路由器维护一张路由表,根据目的 IP 地址查表决定下一跳。数据包在网络中就像快递包裹,每一站只看「下一站发到哪里」,不用关心全程路径。
+
+### ARP 协议:从 IP 地址到 MAC 地址
+
+数据帧在链路层传输时,需要知道下一跳设备的 MAC 地址,而不能只用 IP 地址。ARP(Address Resolution Protocol,地址解析协议)就是解决「已知 IP 地址,如何获取对应 MAC 地址」的问题。
+
+ARP 的工作方式是**广播问询、单播响应**:
+
+1. 主机先查本地 ARP 缓存表,看是否已有目标 IP 对应的 MAC 地址。
+2. 缓存未命中时,在局域网内广播一个 ARP 请求:「谁的 IP 是 xxx.xxx.xxx.xxx?请告诉我你的 MAC 地址。」
+3. 目标设备(或路由器接口)收到后,以单播方式回复自己的 MAC 地址。
+4. 请求方收到响应后,将 IP-MAC 映射存入 ARP 缓存表,后续通信直接使用。
+
+如果目标主机不在同一子网,主机不需要知道最终目标的 MAC 地址,只需要知道**本地网关(路由器)的 MAC 地址**即可。数据包先发给网关,网关再逐跳转发到目标网络。
+
+> 关于 ARP 的详细工作原理(同子网/跨子网寻址、ARP 表、常见攻击),可以参考 [ARP 协议详解(网络层)](https://javaguide.cn/cs-basics/network/arp.html)。
+
+### 网络地址转换(NAT)
+
+在大多数家庭和企业网络中,内网主机使用的是私有 IP 地址(如 `192.168.x.x`),不能直接在公网上路由。NAT(Network Address Translation)协议负责在内网和公网之间转换 IP 地址。
+
+当内网主机发送数据包到公网时,NAT 设备(通常是路由器)会将源 IP 地址从私有地址替换为公网地址,并记录端口映射关系。响应数据包返回时,NAT 再根据映射表把目的地址转换回内网主机的私有地址。
+
+## 第六步:浏览器解析与渲染
+
+服务器返回 HTML 响应后,浏览器的工作才真正开始。浏览器需要解析 HTML、构建 DOM 树、加载子资源、计算样式、布局并最终渲染到屏幕上。
+
+### HTML 解析与 DOM 构建
+
+浏览器拿到 HTML 文档后,从上到下逐行解析:
+
+1. **构建 DOM 树**:解析 HTML 标签,生成文档对象模型(DOM)树,表示页面的结构。
+2. **构建 CSSOM 树**:遇到 `` 引用的 CSS 文件或 `