计算机网络基础
开篇:数据是怎么从你的浏览器跑到千里之外的服务器的?
你在浏览器里输入一个网址,不到一秒钟,页面就出来了。但你有没有想过,这一秒钟里到底发生了什么?你的数据是怎么穿过网线、路由器、光缆,跑到千里之外的服务器上,又带着网页内容跑回来的?
这背后是一整套精心设计的网络体系在运作。这篇文章就来把计算机网络这件事掰开揉碎讲清楚。我们会用快递运输做类比,从整体架构讲到具体协议,让你真正理解网络通信的全貌。
一、网络体系结构 (OSI 七层 vs TCP/IP 四层 - 快递运输类比)
要理解网络,先得理解"分层"的思想。
想象一下你要寄一个包裹。你不需要关心快递员怎么骑车、走哪条高速公路、在哪个中转站分拣,你只需要把东西打包好、填好地址就行。网络通信也是一样的道理:每一层各司其职,只和相邻层打交道。
1.1 OSI 七层模型 vs TCP/IP 四层模型
OSI 七层模型是国际标准化组织提出的理论模型,概念完整但过于复杂。实际工程中大家用的是更加务实的 TCP/IP 四层模型。两者的对应关系如下:
| OSI 七层 | TCP/IP 四层 | 核心职责 | 快递类比 |
|---|---|---|---|
| 应用层、表示层、会话层 | 应用层 | 为用户提供网络服务 | 你填写快递单,决定寄什么东西 |
| 传输层 | 传输层 | 端到端可靠传输 | 快递公司保证包裹完整送达、不丢件 |
| 网络层 | 网络层 | 寻址和路由 | 物流中转站选最优路线 |
| 数据链路层、物理层 | 网络接口层 | 在物理介质上传输比特流 | 快递员骑车把包裹送到下一个中转站 |
还有一种常见的说法叫 TCP/IP 五层模型,就是把网络接口层拆成数据链路层和物理层。面试中提哪种都可以,本质是一样的。
为什么要分层?因为每一层的问题域完全不同。物理层关心的是电信号怎么在铜线里传输,应用层关心的是 HTTP 请求的格式。把它们混在一起会变得极其复杂。分层之后,替换任何一层的实现(比如从有线换成 WiFi)都不会影响其他层。
1.2 TCP/IP 四层各自干了什么
应用层 运行在操作系统的用户态,是你直接接触的一层。HTTP、FTP、DNS、SMTP 这些协议都在这里。应用层不关心数据怎么传输,只关心"我要发什么内容"。
应用层还提供了 Socket 这样的编程接口,让开发者可以方便地使用底层的传输能力,而不需要了解 TCP 报文怎么拼、IP 地址怎么路由。
传输层 为应用层提供端到端的通信服务。它有两个核心协议:
- TCP(传输控制协议):可靠传输,有连接、有确认、有重传。大部分应用都用它,比如 HTTP。
- UDP(用户数据报协议):不可靠但快速,没连接、没确认。实时性要求高的应用用它,比如视频通话。
传输层通过端口号来区分同一台机器上的不同应用。比如 HTTP 默认用 80 端口,HTTPS 用 443 端口。
传输层还有一个重要的工作:把应用层交下来的大块数据分割成合适大小的数据段(报文段),方便网络层传输。
网络层 负责把数据从一台设备送到另一台设备。核心协议是 IP。
IP 地址由两部分组成:网络号(标识子网)和主机号(标识具体设备)。怎么划分这两部分?靠子网掩码。比如 10.100.122.0/24,其中 /24 表示前 24 位是网络号,后 8 位是主机号。
IP: 10.100.122.2 → 00001010.01100100.01111010.00000010
子网掩码: 255.255.255.0 → 11111111.11111111.11111111.00000000
按位与得到网络号: 00001010.01100100.01111010.00000000 → 10.100.122.0
取反与得到主机号: 00000000.00000000.00000000.00000010 → 0.0.0.2寻址的过程是先匹配网络号找到目标子网,再通过主机号找到具体设备。
除了寻址,网络层还负责路由选择。两台设备之间可能有多条路径,路由器通过路由算法决定数据包走哪条路最优。
网络层还包含两个辅助协议:
- ICMP:用于传递控制消息和错误报告(ping 命令就基于它)
- ARP:根据 IP 地址查询对应的 MAC 地址
网络接口层 负责在物理介质(网线、光纤、WiFi)上发送原始数据包。它使用 MAC 地址(硬件地址)来标识网络上的每个接口。
MAC 地址在网卡出厂时就写死了,全球唯一。但 MAC 地址只在局域网内有效,数据每经过一个路由器,MAC 头部就会被替换。需要注意的是,MAC 地址标识的是网络接口(网卡),不是设备。一台设备可以有多个网卡,就有多个 MAC 地址。
1.3 协议三要素
不管哪一层的协议,都必须定义三件事:
- 语法:数据的格式和结构(比如 HTTP 请求行的格式是
METHOD URL VERSION) - 语义:每个字段代表什么含义(比如状态码 200 表示成功,404 表示找不到)
- 同步:通信双方的时序关系(比如先建连接再传数据,先请求再响应)
二、从输入 URL 到页面展示的全过程
这是网络知识的"全景图",一次请求把所有层全部串了起来。面试中出现的频率极高,理解了这个过程,网络的整体脉络就清楚了。
下面逐步展开每个关键步骤。
2.1 URL 解析
浏览器拿到你输入的 URL 后,首先进行解析:提取协议(http/https)、域名、端口、路径等信息。对 URL 中的特殊字符进行自动编码,然后检查本地缓存里有没有这个页面的有效副本。如果有且没过期,就直接用缓存,不发请求了。
2.2 DNS 域名解析
浏览器需要把域名(如 www.example.com)翻译成 IP 地址。就像你知道"工商银行"这个名字,但打电话需要知道具体号码,DNS 就是那个"114 查号台"。
查找顺序是一条链路:
- 浏览器缓存 → 2. 操作系统缓存 → 3. hosts 文件 → 4. 本地 DNS 服务器
如果本地 DNS 服务器也没有,就开始递归查询。域名的层级关系是一个树状结构。www.example.com 其实完整写法是 www.example.com.,最后那个点代表根域名。
客户端 → 本地 DNS 服务器:"www.example.com 的 IP 是多少?"
本地 DNS → 根域名服务器:"请问 .com 的服务器地址?"
本地 DNS → .com 顶级域名服务器:"请问 example.com 的权威服务器地址?"
本地 DNS → example.com 权威服务器:"请问 www.example.com 的 IP?"
权威服务器 → 本地 DNS:"IP 是 93.184.216.34"
本地 DNS → 客户端:"IP 是 93.184.216.34"(同时缓存结果)注意,客户端到本地 DNS 是递归查询(客户端发一次请求就等结果),本地 DNS 到各级域名服务器之间是迭代查询(本地 DNS 一步步去问)。
拿到的 IP 会被层层缓存,下次访问就不需要重复查询了。
2.3 TCP 连接建立
拿到 IP 地址后,浏览器通过 Socket 接口把 HTTP 请求交给操作系统的协议栈。因为 HTTP 基于 TCP 传输,所以先要进行三次握手建立连接(三次握手的细节在第三章详解)。如果是 HTTPS,TCP 连接建立后还要进行 TLS 握手来建立加密通道。
2.4 数据的层层封装
连接建立后,浏览器发出 HTTP 请求。数据在协议栈中每经过一层都会被加上该层的头部信息,就像套娃一样:
[HTTP 请求数据]
→ 加 TCP 头(源端口、目标端口、序列号、确认号、状态位、窗口大小...)
→ 加 IP 头(源 IP、目标 IP、TTL、协议类型...)
→ 加 MAC 头(源 MAC、目标 MAC)+ 帧尾(FCS 校验)
→ 网卡把数字信号转成电信号/光信号发出去TCP 头部中有几个关键字段:
- 源端口和目标端口:标识发送方和接收方的应用程序
- 序列号:给每个字节编号,用于排序和去重
- 确认号:告诉对方"你前面的数据我都收到了"
- 状态位:SYN(建连)、ACK(确认)、FIN(断连)、RST(重置)
- 窗口大小:流量控制,告诉对方自己还能接收多少数据
如果 HTTP 请求数据太大,超过了一个报文段的最大长度(MSS),TCP 会先把数据拆分成多个段再发送。
2.5 交换机和路由器的接力赛
数据包出了网卡后,就进入了网络设备的接力赛。
交换机 工作在数据链路层(二层设备),职责是在局域网内转发数据帧。它维护一张 MAC 地址表,记录每个 MAC 地址对应哪个端口。收到数据帧后,先做 FCS 校验,通过后查 MAC 地址表。找到对应端口就转发,找不到就广播到所有端口(除了来源端口)。交换机不会修改数据帧的内容,只负责"原样转发"。
路由器 工作在网络层(三层设备),职责是在不同网络之间转发数据包。路由器收到数据包后:
- 检查 MAC 头部,确认是发给自己的。确认后丢弃 MAC 头部(MAC 头的使命完成了)。
- 查看 IP 头部中的目标 IP,对照路由表找到下一跳地址。
- 如果路由表的网关列是一个 IP 地址,说明还没到终点,需要继续转发。如果网关为空,说明目标就在直连网络中。
- 通过 ARP 获取下一跳的 MAC 地址,重新构建 MAC 头部(源 MAC 是路由器出口网卡的 MAC,目标 MAC 是下一跳的 MAC)。
- 把数据包从对应端口发出去。
数据包就这样经过一个又一个路由器的接力,最终到达目标服务器所在的网络。
一个关键点:数据包在传输过程中,源 IP 和目标 IP 始终不变,但 MAC 地址每经过一个路由器就会更新一次。 IP 地址负责全程的端到端寻址,MAC 地址只负责相邻两个节点之间的一跳传输。
2.6 服务器响应与页面渲染
数据包到达服务器后,反向拆包:
电信号 → 网卡转为数字信号
→ 检查 MAC 地址是否是自己 → 拆 MAC 头
→ 拆 IP 头,根据协议字段知道上层是 TCP
→ 拆 TCP 头,检查序列号是否正确,正确则放入缓存并返回 ACK
→ 把 HTTP 请求交给 Web 服务器(如 Nginx)处理
→ Nginx 根据负载均衡算法转发给后端应用服务器
→ 应用服务器处理业务逻辑,生成 HTTP 响应响应数据按同样的方式层层封装发回客户端。浏览器收到响应后:
- 根据状态码判断结果(200 成功、301/302 重定向、404 找不到等)
- 根据 Content-Type 和 Content-Encoding 解码数据
- 解析 HTML 构建 DOM 树,加载 CSS 构建 CSSOM 树
- 合并成渲染树,计算布局,绘制页面
- 执行 JavaScript
完成通信后,通过 TCP 四次挥手断开连接(HTTP/1.1 的 Keep-Alive 模式下可以复用连接)。
三、TCP 协议详解
TCP 是网络的"靠谱快递"。它不是最快的,但一定是最可靠的。它的核心使命就三个字:不丢包。
IP 层本身是不可靠的——它不保证数据包的交付、不保证按序到达、不保证数据完整。所有这些可靠性保障,都由 TCP 来提供。
3.1 三次握手 (Why 3, not 2 or 4? - 打电话类比)
想象你给朋友打电话:
- 你说:"喂,听得到吗?" → 这是第一次握手(SYN)
- 朋友说:"听到了,你能听到我吗?" → 这是第二次握手(SYN+ACK)
- 你说:"能听到,咱们开始说正事吧" → 这是第三次握手(ACK)
三次交互之后,双方都确认了四件事:我能发、我能收、你能发、你能收。
具体过程:
- 第一次握手:客户端随机生成一个初始序列号(client_isn),将序列号放入 TCP 首部,SYN 标志位置为 1。发送后客户端进入
SYN_SENT状态。这个报文不携带应用层数据。 - 第二次握手:服务端收到后,也随机生成自己的初始序列号(server_isn),回复 SYN+ACK 报文。确认号填
client_isn + 1,表示"你的 SYN 我收到了"。服务端进入SYN_RCVD状态。这个报文也不携带应用层数据。 - 第三次握手:客户端收到后,回复 ACK 报文,确认号填
server_isn + 1。客户端进入ESTABLISHED状态。这个 ACK 可以携带应用层数据。服务端收到后也进入ESTABLISHED状态。
为什么不能两次?
两次握手有三个致命问题。
问题一:无法防止历史连接。 假设客户端发了一个 SYN(seq=90),但网络拥堵导致这个包迟迟没到。客户端超时后重启,又发了一个新的 SYN(seq=100)。结果旧的 SYN 先到达服务端。如果只有两次握手,服务端收到旧 SYN 就直接建立连接了——但客户端根本不知道这个连接的存在,这是一个废连接。
三次握手怎么避免的?服务端回复 SYN+ACK(ack=91) 给客户端,客户端一看:"我现在的序列号是 100 啊,91 是什么鬼?"于是发 RST 拒绝这个连接。等新的 SYN(seq=100) 到达后才正常建立连接。
问题二:无法同步双方序列号。 双方各自生成随机初始序列号,每个序列号都需要对方确认才算同步成功。客户端发 SYN 需要服务端 ACK,服务端发 SYN 也需要客户端 ACK。一来一回,最少需要三次。
问题三:浪费资源。 两次握手时,服务端收到一个 SYN 就建立连接。如果客户端的 SYN 在网络中重复了(比如经过不同路径产生了多个副本),服务端会为每个 SYN 都建立连接,造成大量无效连接占用资源。
为什么不是四次?
理论上可以四次:客户端发 SYN → 服务端回 ACK → 服务端发 SYN → 客户端回 ACK。但没必要,因为第二步和第三步可以合并成一个 SYN+ACK 包,省一次网络交互。
3.2 四次挥手 (Why 4? TIME_WAIT 的意义)
断开连接比建立连接多一步,因为 TCP 是全双工通信——双方可以同时独立发送数据,关闭时需要分别关闭两个方向的通道。
还是打电话的类比:
- 你说:"我没什么要说的了"(FIN) → 但你的耳朵还开着,可以继续听
- 朋友说:"知道了"(ACK) → 然后继续说他没说完的话
- 朋友说完后:"我也说完了"(FIN)
- 你说:"好的,再见"(ACK) → 等一会儿确认对方不会再说话了,然后挂电话
为什么不能三次? 因为服务端收到客户端的 FIN 时,可能还有数据没发完。它不能像握手那样把 ACK 和 FIN 合并成一个包发出去。必须先回 ACK 告诉客户端"我知道你要关了",然后把剩余数据发完,最后再发 FIN 表示"我也发完了"。所以是四步。
不过在某些情况下(服务端没有剩余数据要发),第二步和第三步也可以合并,实际上变成三次挥手。这是 TCP 延迟确认和 FIN 合并优化的结果。
TIME_WAIT 为什么要等 2MSL?
MSL(Maximum Segment Lifetime)是一个报文在网络中的最大生存时间,通常是 30 秒到 2 分钟。等 2MSL 有两个原因:
原因一:确保最后一个 ACK 能送达。 如果客户端发的最后一个 ACK 在网络中丢了,服务端因为超时没收到 ACK 会重发 FIN。客户端在 TIME_WAIT 期间收到重发的 FIN 后,可以重新发送 ACK。如果客户端直接关闭了,服务端就永远收不到 ACK,会一直卡在 LAST_ACK 状态。
原因二:让网络中残留的旧数据包彻底消失。 等 2MSL 可以保证上一次连接产生的所有报文都从网络中消失。否则,如果新连接恰好使用了相同的源 IP、源端口、目标 IP、目标端口,旧连接残留的数据包可能会被新连接当作有效数据接收,造成数据混乱。
3.3 可靠传输机制 (ACK, 滑动窗口, 拥塞控制)
TCP 保证可靠性的手段是一套组合拳。我们逐个来看。
序列号和确认号
TCP 给发送的每一个字节都编号(序列号)。接收方收到数据后回复 ACK,确认号的含义是"这个号码之前的所有字节我都收到了,下一个字节从这个号码开始发"。
通过序列号可以实现三件事:
- 排序:即使数据包乱序到达,接收方也能按序列号重新排列
- 去重:收到重复的包(序列号相同)直接丢弃
- 确认:通过确认号知道哪些数据对方已经收到
校验和
TCP 首部和数据部分都参与校验和计算。这是一个端到端的检验。如果收到的报文校验不通过,直接丢弃,不发 ACK,发送方超时后会重传。
超时重传
发送数据后启动一个定时器,如果超时没收到 ACK 就重发。超时时间(RTO)是动态计算的,根据网络的 RTT(Round Trip Time,往返时间)自动调整。网络延迟大的时候 RTO 也会变大,避免不必要的重传。
滑动窗口(流量控制)
如果发送方发得太快,接收方来不及处理怎么办?TCP 的流量控制机制就是解决这个问题的。
接收方在每个 ACK 中都会带上"窗口大小"(Window Size)字段,告诉发送方"我的接收缓冲区还能装多少字节的数据"。发送方根据这个窗口大小来控制发送速度。
这就像水管和水桶的关系:桶快满了就把水管拧小点,桶空了就拧大点。当窗口大小为 0 时,发送方停止发送(但会定期发探测包询问窗口是否恢复)。
"滑动窗口"的名字来源于它的工作方式:窗口就像一个滑动的框,框内的数据可以发送,框外的不能。随着 ACK 的到来,窗口向前滑动,允许发送新的数据。
拥塞控制
流量控制是防止接收方被淹没,拥塞控制是防止整个网络被淹没。两个是不同层面的问题。
想象一条高速公路,每辆车(数据包)都想尽快到达目的地。如果所有车同时涌入,公路就堵了。拥塞控制就是交通管制——根据路况动态调整每辆车的发车速度。
TCP 通过维护一个拥塞窗口(cwnd)来控制发送速率。实际的发送窗口 = min(接收方窗口, 拥塞窗口)。拥塞控制有四个核心机制:
慢启动:连接刚建立或发生超时后,cwnd 从 1 个 MSS 开始,每收到一个 ACK 就加 1 个 MSS。效果是指数增长:1 → 2 → 4 → 8 → 16... 虽然叫"慢启动",增长速度其实很快,只是相比于一上来就全速发送,起步是"慢"的。
拥塞避免:当 cwnd 达到慢启动阈值(ssthresh)后,增长方式变为线性:每个 RTT 只增加 1 个 MSS。小心翼翼地试探网络容量。
快重传:不等超时就重传。当发送方连续收到 3 个重复的 ACK 时,说明某个包大概率丢了,立即重传。
举个例子:发送方发了 1、2、3、4、5 号包,其中 2 号丢了。接收方收到 3、4、5 后,因为 2 还没到,所以每收到一个包都只能回复 ACK(1),意思是"1 号之前的我都收到了,但 2 号还没到"。发送方收到 3 个重复的 ACK(1),就知道 2 号丢了,立刻重传。
快恢复:快重传触发后,不回到慢启动阶段(那样太慢),而是把 ssthresh 设为 cwnd 的一半,cwnd 设为 ssthresh + 3(对应三个重复 ACK),然后继续线性增长。
四、UDP 与 TCP 对比
UDP 和 TCP 都在传输层,但设计理念截然不同。
打个比方:TCP 是顺丰快递(签收确认、丢件必赔、严格按顺序送),UDP 是传单小哥(塞你手里就走,不管你看不看,丢了也不负责)。
| 对比项 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(三次握手) | 无连接,即发即走 |
| 可靠性 | 可靠(确认、重传、排序) | 不可靠(尽力而为) |
| 通信模式 | 一对一 | 一对一、一对多、多对多 |
| 首部大小 | 20-60 字节(可变) | 8 字节(固定) |
| 传输方式 | 字节流(无消息边界) | 数据报(有消息边界) |
| 速度 | 较慢(握手、确认、重传有开销) | 较快 |
| 拥塞/流量控制 | 有 | 无 |
| 分片方式 | 在传输层按 MSS 分片 | 在 IP 层按 MTU 分片 |
| 典型协议 | HTTP/HTTPS、FTP、SMTP、SSH | DNS、DHCP、视频流、VoIP |
| 典型场景 | 网页、文件传输、邮件 | 在线游戏、视频直播、语音通话 |
什么时候用 UDP? 对实时性要求高、能容忍少量丢包的场景。比如视频通话中丢一两帧画面问题不大,但如果用 TCP 等待重传,画面就会出现明显的卡顿和延迟。再比如 DNS 查询,就一个小数据包一问一答,建立 TCP 连接的开销比数据本身还大。
值得一提的是,UDP 也可以在应用层实现可靠传输。Google 的 QUIC 协议就是基于 UDP 实现的,它把 TCP 的可靠性特性搬到了应用层,既保留了 UDP 的灵活性(绕开了 TCP 的"协议僵化"问题),又实现了可靠传输。HTTP/3 就是基于 QUIC 的。
五、常见面试题精选
5.1 ARP 和 RARP 的区别
ARP(地址解析协议)把 IP 地址翻译成 MAC 地址。工作方式是在局域网内广播一个 ARP 请求:"谁是 192.168.1.100?请告诉我你的 MAC 地址"。所有主机都能收到这个广播,但只有目标主机会单播回复自己的 MAC 地址。查询结果会被缓存几分钟,下次就不用再广播了。
RARP 则反过来,把 MAC 地址翻译成 IP 地址。早期无盘工作站启动时用它来获取自己的 IP。但由于 DHCP 的出现,RARP 已经基本不用了。
简记:ARP 是"我知道你的 IP,告诉我你的 MAC";RARP 是"我知道自己的 MAC,告诉我我的 IP"。
5.2 Cookie、Session、Token 的区别
HTTP 协议是无状态的,每次请求都是独立的。服务器默认不知道两次请求是不是同一个人发的。为了让服务器记住"你是谁",就发明了这三种技术。
Cookie 是服务器发给浏览器的小文本文件,存在客户端。浏览器后续请求会自动带上 Cookie,服务器读取 Cookie 来识别用户。Cookie 绑定单一域名,不能跨域。每个域名下 Cookie 的数量和单个 Cookie 的大小都有限制。
Session 是存在服务端的会话数据。服务端生成一个唯一的 Session ID,通常通过 Cookie 发给客户端保存。后续请求中客户端带上 Session ID,服务端根据 ID 查找对应的会话数据。Session 更安全(数据在服务端),但在分布式部署时需要做 Session 共享(比如存到 Redis),增加了复杂度。
Token 是一种令牌机制。用户登录成功后,服务端生成一个 Token 返回给客户端。客户端把 Token 放在请求头(通常是 Authorization 字段)中发送。服务端每次验证 Token 的有效性即可,不需要在服务端存储任何会话状态。常见的实现有 JWT(JSON Web Token)。
| 项目 | 存储位置 | 跨域支持 | 有状态/无状态 | 典型场景 |
|---|---|---|---|---|
| Cookie | 客户端浏览器 | 不支持 | - | 记住登录状态、用户偏好 |
| Session | 服务端 | 不支持 | 有状态 | 传统 Web 应用 |
| Token | 客户端(Header 或 localStorage) | 支持 | 无状态 | 前后端分离、移动端、微服务 API |
Token 的优势在于天然无状态,不需要服务端存储,天然支持分布式和跨域。这也是现在前后端分离架构普遍使用 Token 的原因。
5.3 TCP 的粘包和拆包
TCP 是面向字节流的协议,它不知道也不关心上层的"消息"边界在哪里。对 TCP 来说,数据就是一连串的字节,它只管可靠地搬运过去。所以可能出现两种情况:
- 粘包:发送方连续发了两个消息 "hello" 和 "world",TCP 把它们合成一个包 "helloworld" 发出去了。接收方收到后分不清 "hello" 在哪里结束。
- 拆包:发送方发了一个大消息,TCP 把它拆成两个包分别发出去。接收方先收到前半部分,误以为是一条完整消息。
注意:UDP 不存在粘包问题,因为 UDP 是面向数据报的,每个数据报都是独立的,有明确的边界。
常见解决方案:
- 固定长度:每个消息固定 N 字节,不足的用特定字符填充
- 分隔符:在消息末尾加
\n、\r\n等分隔符 - 长度字段(最常用):在消息头部加一个 length 字段标明消息体长度。HTTP 就是用 Content-Length 来做的
- 自定义协议:消息分为 header 和 body,header 中包含 body 的长度
5.4 正向代理和反向代理
正向代理 代理的是客户端。客户端明确知道代理的存在,主动通过代理去访问目标服务器。目标服务器不知道真正的客户端是谁。典型场景:科学上网、公司内网通过代理访问外网。
反向代理 代理的是服务器。客户端完全不知道代理的存在,以为自己直接在访问真实服务器。实际上请求先到代理服务器,代理再根据负载均衡策略转发给后端某一台真实服务器。典型场景:Nginx 负载均衡。
核心区别一句话:正向代理隐藏客户端,反向代理隐藏服务器。
两者的部署方也不同:正向代理一般由客户端(用户)自己搭建,反向代理一般由服务端运维搭建。
5.5 CDN 为什么能加速
CDN(Content Delivery Network,内容分发网络)在全球部署大量边缘节点,把静态资源(图片、JS、CSS、视频)缓存到离用户最近的节点。
当用户请求某个资源时,DNS 会把域名解析到离用户最近的 CDN 节点 IP(通常通过智能 DNS 或 Anycast 技术实现)。用户直接从边缘节点获取资源,不需要每次都跑到远在天边的源服务器。
CDN 加速的本质就两个字:就近。减少了物理距离带来的网络延迟和带宽消耗,同时也分担了源服务器的压力。
首次访问时 CDN 节点上没有缓存,会从源服务器回源拉取并缓存。后续相同地区的用户访问相同资源时,CDN 直接返回缓存副本。缓存有过期时间,过期后会重新回源。
5.6 ping 的原理和为什么不需要端口
ping 基于 ICMP 协议工作,用于测试目标主机的网络连通性和响应时间。
工作过程:主机 A 发送一个 ICMP "echo request"(回显请求)报文给主机 B,主机 B 正确接收后发回 "echo reply"(回显应答)报文。通过对方是否回复以及回复的耗时,就能判断网络是否通畅以及延迟多大。
端口号是传输层(TCP/UDP)的概念,用于区分同一台机器上的不同服务。而 ping 使用的 ICMP 是网络层协议,它直接由应用层调用,完全不经过传输层。所以 ping 不需要也没有端口号。
ping 是一个典型的"应用层直接使用网络层协议"的例子。我们平时用的 HTTP 是"应用层 → 传输层(TCP) → 网络层(IP)",而 ping 是"应用层 → 网络层(ICMP)",跳过了传输层。
小结
计算机网络的核心设计思想是分层解耦。每一层只做自己的事情,通过标准接口和上下层协作。这种设计让我们可以独立替换任何一层的实现而不影响其他层。
TCP 牺牲速度换可靠性(三次握手、确认重传、滑动窗口、拥塞控制),UDP 牺牲可靠性换速度和灵活性。没有绝对的好坏,只有适合的场景。
理解了 TCP/IP 分层模型、"输入 URL 到页面展示"的全流程、TCP 的三次握手/四次挥手和可靠传输机制、以及 TCP 与 UDP 的对比,计算机网络基础这一关就算稳稳地过了。后续在学习 HTTP、HTTPS、网络安全等内容时,都会以这些基础知识为地基。
附录 A:IPv4 与 IPv6
A.1 IPv4 地址
IPv4 使用 32 位(4 字节)地址,理论上最多提供约 42.9 亿个地址。采用点分十进制表示法,格式为 nnn.nnn.nnn.nnn,每段范围 0-255。例如 192.168.1.100。
42.9 亿地址看似很多,但随着联网设备的爆炸式增长(手机、平板、智能家居、物联网设备...),IPv4 地址早在 2019 年就已经分配完毕了。
A.2 IPv6 地址
IPv6 使用 128 位(16 字节)地址,地址空间是 IPv4 的 2^96 倍。有人说 IPv6 的地址数比全世界的沙子还多,足够给地球上每一粒沙子都分配一个 IP。
IPv6 的表示法是用冒号分隔的 8 组十六进制数,例如:
2001:0db8:86a3:08d3:1319:8a2e:0370:7344可以省略每组的前导零,连续的全零段可以用 :: 简写(只能用一次)。
A.3 IPv4 和 IPv6 的主要区别
| 对比项 | IPv4 | IPv6 |
|---|---|---|
| 地址长度 | 32 位 | 128 位 |
| 地址数量 | 约 42.9 亿 | 约 3.4 x 10^38 |
| 表示法 | 点分十进制 | 冒号分隔十六进制 |
| 首部长度 | 可变(20-60 字节) | 固定 40 字节 |
| 地址解析 | ARP 协议 | ICMPv6 邻居发现 |
| 安全性 | IPSec 可选 | IPSec 内建 |
| 路由表 | 较大 | 较小(聚类编址) |
A.4 IPv4 向 IPv6 的过渡
IPv6 不是 IPv4 的升级版,而是一个全新的协议,两者不能直接互通。过渡期间主要有三种技术方案:
双栈技术:同一台设备同时运行 IPv4 和 IPv6 两套协议栈,根据对方的地址类型选择使用哪个协议。优点是处理效率高、无信息丢失;缺点是资源占用多,改造成本高。
隧道技术:把 IPv6 数据包封装在 IPv4 数据包中传输(反之亦然),在 IPv4 网络中建立一条 IPv6 的"隧道"。优点是实现简单;缺点是封装解封装有性能开销。
协议转换(NAT-PT):在 IPv4 和 IPv6 网络之间放一个转换器,进行地址和协议的实时转换。优点是不需要改造端设备;缺点是转换器本身是性能瓶颈。
附录 B:更多网络概念
B.1 跨域访问问题
跨域是浏览器的同源策略引起的安全限制。同源要求三个条件完全相同:协议、域名、端口。只要有一个不同就算跨域。
比如从 http://a.com 的页面请求 http://b.com 的接口,或者从 http://a.com 请求 https://a.com 的资源,都会触发跨域限制。
常见解决方案:
- CORS(推荐):服务端在响应头中设置
Access-Control-Allow-Origin,告诉浏览器"允许这个来源的请求"。这是最标准的解决方案。
@CrossOrigin(origins = "*")
public class MyController {
// ...
}JSONP:利用
<script>标签不受同源策略限制的特性,动态插入 script 标签加载跨域资源。只支持 GET 请求,已逐渐被 CORS 替代。代理服务器:前端请求发给同源的代理服务器,由代理服务器转发到目标服务器。绕过浏览器的同源限制(同源策略只是浏览器的行为,服务器之间没有这个限制)。
B.2 长连接与短连接
短连接:每次请求都新建一个 TCP 连接,响应结束后立即关闭。HTTP/1.0 默认就是短连接。每次都要经历三次握手和四次挥手,开销大。
长连接:TCP 连接建立后保持不关闭,多次请求复用同一个连接。HTTP/1.1 默认使用长连接(Connection: Keep-Alive)。减少了频繁建立和关闭连接的开销。
| 对比 | 短连接 | 长连接 |
|---|---|---|
| 连接生命周期 | 一次请求后关闭 | 保持打开,多次复用 |
| 建连开销 | 每次都要三次握手 | 只需建立一次 |
| 服务器资源 | 低(连接用完就释放) | 高(需要维护大量连接) |
| 适用场景 | 低频请求、无状态 API | 即时通讯、游戏、流媒体 |
长连接需要通过心跳机制(定期发送小数据包证明"我还活着")来维持。如果长时间没有数据交互也没有心跳,连接会被超时断开。
B.3 网络分区与脑裂
网络分区 是指分布式系统中,由于网络故障导致系统被划分为两个或多个互不通信的区域。每个区域内部可以正常工作,但区域之间无法通信。
这就是 CAP 定理中 P(Partition Tolerance,分区容错性)的来源。CAP 定理指出,一致性(C)、可用性(A)、分区容错性(P)三者不可兼得,在网络分区发生时必须在 C 和 A 之间做选择。
脑裂 是网络分区的一种严重后果。在集群系统中,网络分区可能导致每个分区都认为自己是"主节点",各自独立处理请求,造成数据不一致。常见的防范手段包括引入仲裁节点、奇数节点数投票等。
B.4 DNS 污染与 DNS 劫持
DNS 污染:某些中间服务器监控 DNS 查询请求,一旦发现目标域名在黑名单中,就伪装成 DNS 服务器返回错误的 IP 地址。它利用了 UDP 协议无连接不可靠的特性。DNS 污染并没有入侵真正的 DNS 服务器,而是"冒充"了一个。
DNS 劫持:攻击者通过非法手段获取了 DNS 服务器的控制权,直接修改 DNS 记录,让域名指向错误的 IP。某些运营商也会做 DNS 劫持,比如把用户访问的页面重定向到广告页面。
两者的区别:DNS 污染是"冒充"DNS 服务器;DNS 劫持是"控制"了真正的 DNS 服务器。结果都是让用户访问到错误的地址。
防范手段包括:使用可靠的 DNS 服务商、启用 DNSSEC(DNS 安全扩展)、使用 DNS over HTTPS(DoH)、使用 VPN 等。
B.5 OSI 七层模型详细说明
面试中有时会问到完整的 OSI 七层模型,这里详细列出每层的职责和典型协议:
物理层:定义物理设备标准(网线、光纤、无线电波的规格),负责比特流的传输。编码方式有曼彻斯特编码、不归零编码等。传输模式有单工、半双工、全双工。
数据链路层:在物理层的基础上,将比特流组织成帧(Frame),提供差错检测(CRC 校验)和可靠传输。MAC 地址就在这一层。主要协议有以太网、PPP。
网络层:处理数据包的路由和寻址,核心协议是 IP。还包括 ICMP(ping 的基础)、ARP(IP 转 MAC)、路由协议(RIP、OSPF、BGP)等。
传输层:建立端到端的连接,保证数据的完整性和可靠性。核心协议是 TCP 和 UDP。
会话层:管理通信会话的建立、维护和结束。实际中很少单独出现,通常被应用层吸收。
表示层:负责数据格式转换、加密解密、压缩解压。比如 JPEG 图片编码、SSL/TLS 加密。
应用层:直接为用户提供服务。HTTP、FTP、SMTP、DNS、Telnet 等。
实践中,OSI 的第 5-7 层通常被合并为 TCP/IP 的应用层。面试时说哪种都行,但要能解释它们之间的对应关系。
B.6 TCP 重传率:线上排查利器
TCP 重传率是衡量网络质量的重要指标。正常情况下重传率应该非常低(低于 0.1%),如果重传率异常升高,通常意味着网络存在丢包、拥塞或链路质量问题。
在 Linux 系统中,可以通过以下方式查看:
# 查看 TCP 统计信息中的重传数据
netstat -s | grep -i retrans
# 示例输出:
# 63570 segments retransmitted -- 总共重传了多少个 TCP 段
# TCPSynRetrans: 24056 -- SYN 重传次数(可能是连接超时或 SYN 洪水攻击)
# 13197 fast retransmits -- 快重传次数也可以用 ss -ti 查看每个 TCP 连接的详细状态,包括重传次数。
当线上服务出现网络性能问题(超时、延迟增大)时,先看 TCP 重传率是一个很好的排查切入点。
B.7 网络抓包基础
排查网络问题最直接的手段就是抓包。常用工具有两个:
tcpdump(命令行工具,适合在 Linux 服务器上使用):
# 抓取 eth0 网卡上目标端口为 80 的 TCP 包,保存到文件
tcpdump -i eth0 -nn tcp and port 80 -w capture.pcap
# -i eth0: 指定网卡
# -nn: 不解析域名和端口(直接显示 IP 和数字)
# -w: 写入文件Wireshark(图形界面工具,适合在本地分析抓包文件):
可以打开 tcpdump 保存的 .pcap 文件,提供强大的过滤和分析功能。常用过滤语法:
tcp.port == 443:过滤 443 端口的 TCP 包ip.addr == 192.168.1.1:过滤指定 IP 的包http contains 'keyword':过滤包含特定关键词的 HTTP 包tcp.analysis.retransmission:只看重传的包
抓包在以下场景特别有用:
- 新接手的系统出问题,分析请求参数和响应结果
- 调用第三方接口异常,确认请求是否正确发出
- 网络延迟抖动,分析 TCP 握手和数据传输耗时
- 深入理解某个通信协议的工作原理
一个重要的细节:在 Linux 中,tcpdump 在收包场景中位于 iptables 之前,所以能抓到被 iptables INPUT 链拦截的包。但在发包场景中位于 iptables 之后,所以抓不到被 OUTPUT 链拦截的包。
B.8 对称加密与非对称加密
这两种加密方式是网络安全的基石,理解它们对理解 HTTPS 至关重要。
对称加密:加密和解密使用同一把钥匙。就像你和朋友约定了一套暗号,双方都用同一套规则来编码和解码。
优点是速度快,适合加密大量数据。缺点是密钥分发困难——你怎么安全地把密钥送到对方手里?如果在传输过程中被截获,加密就形同虚设了。
常见算法:AES、DES、3DES、SM4(国密)。
非对称加密:使用一对钥匙——公钥和私钥。公钥加密的数据只能用私钥解密,反之亦然。公钥可以公开给任何人,私钥自己保管。
优点是解决了密钥分发问题。缺点是速度比对称加密慢得多。
常见算法:RSA、ECC、SM2(国密)。
实际应用(HTTPS):两者结合使用。先用非对称加密安全地交换一个对称密钥,然后用对称密钥加密后续的所有通信数据。这样既解决了密钥分发问题,又保证了传输效率。
还有一类叫哈希算法(如 MD5、SHA-256、SM3),严格来说不算加密算法(因为不可逆),主要用于数据完整性校验和数字签名。
加密和加签的区别:
- 加密/解密:保护数据的隐私性,防止数据被窃取后泄露明文
- 加签/验签:保护数据的完整性和来源真实性,防止数据被篡改或冒充
B.9 "墙"的工作原理与正向代理
"墙"是一种网络屏蔽机制,通过控制互联网流量来阻止用户访问特定的外部网站。主要手段有两个:
- IP 封锁:直接屏蔽目标服务器的 IP 地址
- DNS 污染:拦截 DNS 查询请求,返回错误的 IP 地址
绕过屏蔽的核心技术是正向代理。用户不直接访问目标网站,而是通过一台可以正常访问目标网站的代理服务器来中转请求。对于"墙"来说,用户只是在访问代理服务器(合法),而代理服务器去访问目标网站(不在监控范围内)。
这和上面讲的正向代理概念是完全一样的。正向代理的本质就是"代理服务器替客户端去做事"。
附录 C:经典面试问答补充
C.1 HTTP 301 和 302 的区别
301 和 302 都是 HTTP 重定向状态码,都会把请求引导到新的 URL。区别在于:
301(永久重定向):资源已经永久搬到了新地址。浏览器和搜索引擎会缓存这个重定向,下次直接访问新地址。适合网站域名变更、页面永久迁移。
302(临时重定向):资源临时在新地址,以后可能还会回来。浏览器不会缓存,每次都先访问旧地址再跳转。适合活动页面临时跳转、维护期间的替代页面。
C.2 什么是 TCP 的 TIME_WAIT 过多问题
高并发服务器上如果 TIME_WAIT 状态的连接过多,会大量占用端口和内存资源。每个 TIME_WAIT 连接都要等 2MSL(通常 60 秒)才释放。
常见解决方案:
- 开启
tcp_tw_reuse:允许复用 TIME_WAIT 状态的连接 - 开启
tcp_tw_recycle(已废弃):快速回收 TIME_WAIT 连接 - 使用长连接减少连接的创建和关闭次数
- 调小
tcp_fin_timeout参数
C.3 交换机和路由器的区别
| 对比项 | 交换机 | 路由器 |
|---|---|---|
| 工作层级 | 数据链路层(二层) | 网络层(三层) |
| 依据的地址 | MAC 地址 | IP 地址 |
| 转发范围 | 局域网内 | 不同网络之间 |
| 核心功能 | 帧转发 | 路由选择、数据包转发 |
| 是否修改帧 | 不修改 | 更新 MAC 头部 |
简记:交换机是局域网内的"邮递员",路由器是不同网络之间的"导航员"。
C.4 浏览器输入 www.taobao.com 后发生了什么(简洁版)
- URL 解析:浏览器解析 URL,检查本地缓存
- DNS 查询:层层查询(浏览器缓存 → OS 缓存 → hosts → 本地 DNS → 根/顶级/权威 DNS),拿到 IP 地址
- TCP 三次握手:与服务器建立 TCP 连接
- TLS 握手(如果是 HTTPS):建立加密通道
- 发送 HTTP 请求:请求经过传输层、网络层、网络接口层层层封装
- 网络传输:数据包经过交换机、路由器等设备转发到达服务器
- 服务器处理:Nginx 接收请求 → 负载均衡 → 转发给应用服务器 → 业务处理 → 生成响应
- 返回响应:响应数据原路返回
- 浏览器渲染:解析 HTML/CSS/JS → 构建 DOM 树 → 渲染页面
- TCP 四次挥手:断开连接