08 - 计算机网络基础 · 高频面试考题背诵指南


目录


一、TCP

Q1:TCP 三次握手过程?为什么是三次而不是两次或四次?

核心考点:握手的目的(同步序列号 + 验证双方收发能力)决定必须是三次。

背诵话术

三次握手的过程

  1. 客户端 → 服务端:SYN = 1, seq = x(客户端进入 SYN-SENT 状态)
  2. 服务端 → 客户端:SYN = 1, ACK = 1, seq = y, ack = x + 1(服务端进入 SYN-RCVD 状态)
  3. 客户端 → 服务端:ACK = 1, seq = x + 1, ack = y + 1(双方进入 ESTABLISHED 状态)

为什么是三次?

  • 核心目的:三次握手的本质是让双方确认自己的发送能力和对方的接收能力都正常
    • 第一次:服务端确认客户端的发送能力正常。
    • 第二次:客户端确认服务端的收发能力都正常。
    • 第三次:服务端确认客户端的接收能力正常(服务端的发送能力被客户端收到并回应了)。
  • 为什么不是两次:两次握手只能确认客户端的发送和服务端的接收,但服务端不知道客户端的接收是否正常,也无法确认客户端是否还活着。如果客户端已关闭,服务端单方面建立连接会造成资源浪费。
  • 为什么不是四次:第二次握手中 SYN + ACK 可以合并为一个报文发送,没必要分开发,三次已经是最优解。

Q2:TCP 四次挥手过程?为什么是四次?TIME_WAIT 为什么是 2MSL?

核心考点:全双工通信决定了挥手必须是四次;TIME_WAIT 保证最后的 ACK 能重传。

背诵话术

四次挥手的过程

  1. 主动关闭方 → 被动方:FIN = 1, seq = u(主动方进入 FIN-WAIT-1)
  2. 被动方 → 主动方:ACK = 1, seq = v, ack = u + 1(被动方进入 CLOSE-WAIT,主动方进入 FIN-WAIT-2)
  3. 被动方 → 主动方:FIN = 1, ACK = 1, seq = w, ack = u + 1(被动方进入 LAST-ACK)
  4. 主动方 → 被动方:ACK = 1, seq = u + 1, ack = w + 1(主动方进入 TIME-WAIT,2MSL 后关闭;被动方收到后直接关闭)

为什么是四次? TCP 是全双工通信,每个方向的数据通道需要独立关闭。被动方收到 FIN 后,可能还有数据要发送(ACKFIN 不能合并),所以需要四次。如果被动方没有数据要发,可以合并步骤 2 和 3(变成三次挥手),HTTP 长连接关闭时常见。

TIME_WAIT 为什么是 2MSL(Maximum Segment Lifetime,最大报文生存时间)?

  • MSL 是 TCP 报文在网络上最长的存活时间(通常 30s~2 分钟)。
  • 2MSL ≈ 一去一回的最大时间:确保主动方最后发送的 ACK 要么被对方收到(正常关闭),要么对方超时重传 FIN 时主动方能再次响应 —— 如果主动方直接关闭,重传的 FIN 无人响应,被动方无法正常关闭。
  • 让旧连接的所有报文都在网络中彻底消失,防止旧连接的报文被新连接(相同四元组)误收。在有大量的短连接的服务器上,过多的 TIME_WAIT(netstat -an | grep TIME_WAIT 特别多)会消耗端口资源,可通过内核参数 tcp_tw_reuse 复用或调优。

Q3:TCP 如何保证可靠传输?

核心考点:校验和 + 序列号与确认 + 重传超时 + 滑动窗口(流量控制)+ 拥塞控制,五大机制。

背诵话术

TCP 通过以下五大机制保证可靠传输:

  1. 校验和(Checksum):TCP 报文头部包含 16 位的校验和字段,接收方会重新计算校验和并与报文头中的对比,若不一致说明数据在传输中损坏,丢弃该报文并触发重传。
  1. 序列号与确认应答(ACK):每个 TCP 报文携带一个头部序列号(seq),接收方收到后会返回一个 ACK 应答信号(ack = seq + 数据长度),告诉发送方”这些数据我收到了,请发下一批”。发送方发完后如果没在规定时间内收到 ACK,会触发超时重传。
  1. 超时重传(RTO):TCP 每发送一个报文后启动一个计时器,如果超时(RTO)内未收到 ACK,就重传该报文。RTO 通过 RTT(Round Trip Time,往返时间)动态计算(使用 Jacobson 公式),比固定值更适应网络变化。
  1. 滑动窗口/流量控制:接收方通过”窗口”大小告诉发送方自己还有多少缓冲区空间可用,发送方最多发送窗口大小内的数据而不等 ACK,发送窗口 = min(拥塞窗口, 接收窗口)。防止发送方塞满接收方的缓冲区。
  1. 拥塞控制(详见 Q4):防止发送方向网络注入过多数据,引发网络拥塞(路由器缓冲区溢出丢包)。

Q4:TCP 拥塞控制的四个阶段

核心考点:慢启动 → 拥塞避免 → 快重传 → 快恢复。

背诵话术

TCP 的拥塞控制通过拥塞窗口(cwnd) 动态调整发送速率,分四个阶段:

  1. 慢启动(Slow Start):连接刚建立时 cwnd = 1 个 MSS(最大报文段大小),每收到一个 ACK,cwnd 翻倍(指数增长),直到达到 ssthresh(慢启动阈值)或发生丢包。
  1. 拥塞避免(Congestion Avoidance):cwnd ≥ ssthresh 后,每收到一个 ACK,cwnd += 1(线性增长),慢慢试探网络的承受上限。
  1. 快重传(Fast Retransmit):如果发送方连续收到 3 个相同的 ACK(Duplicate ACK),说明中间有一个报文丢失了(后面的都到了但缺这一个),不等 RTO 超时,立刻重传该丢失的报文。
  1. 快恢复(Fast Recovery):发生快重传后,不等 RTO,将 ssthresh 设为当前 cwnd/2,cwnd = ssthresh + 3,然后进入拥塞避免阶段线性增长。相比 RTO 后 cwnd 直接从 1 重新慢启动,快恢复大幅缩减了丢包后的吞吐恢复时间。

如果没有快重传/快恢复:发生丢包后只能等 RTO 超时(几百毫秒),期间暂停发送数据,吞吐严重下降。


二、HTTP

Q5:HTTP 1.0 / 1.1 / 2.0 / 3.0 的核心区别

核心考点:从短连接→长连接→多路复用→QUIC 的演进路线。

背诵话术

  • HTTP 1.0:每次请求都要建立一个新的 TCP 连接(短连接),请求结束后立即关闭。连接建立和释放的开销很大(三次握手 + 四次挥手)。每个请求/响应都必须按顺序一一对应。

  • HTTP 1.1:引入了长连接(Keep-Alive),一个 TCP 连接可以复用多次请求/响应,减少了大量握手开销。支持管线化(Pipelining),一次发送多个请求不等响应(但响应必须按顺序返回,队头阻塞(Head-of-Line Blocking)问题依然严重)。增加了 Host 头,支持一台服务器托管多个虚拟主机。

  • HTTP 2.0(基于二进制帧,不是文本):

    • 多路复用(Multiplexing):在一个 TCP 连接上同时发送多个请求的帧(Stream),通过 Stream ID 区分,响应可以交错返回,真正解决了 HTTP 层的队头阻塞。
    • 头部压缩(HPACK):使用哈夫曼编码和静态/动态表压缩请求头。
    • 服务器推送(Server Push):服务端可主动向客户端推送资源(如 HTML 中的 CSS 文件),减少额外请求。
    • 仍然有 TCP 层的队头阻塞问题:因为 TCP 是面向字节流的,如果底层 TCP 有一个报文丢失,后面所有的 HTTP Stream 都得等待这个丢失包重传,即使它们的帧已到达也无法使用(HTTP 层的队头阻塞解决了,但 TCP 层的队头阻塞仍在)。
  • HTTP 3.0(基于 QUIC,底层为 UDP)

    • QUIC 协议替代了 TCP,底层基于 UDP,在 QUIC 层实现了可靠传输(类似 TCP 的确认/重传)。
    • 彻底解决了队头阻塞:QUIC 的多个 Stream 之间完全独立,一个 Stream 丢包只影响该 Stream,不影响其他 Stream 的交付。
    • 0-RTT 快速握手:QUIC 的握手只需要 0~1 次 RTT(往返时间),大幅降低连接建立延迟。
    • 连接迁移:QUIC 使用 Connection ID 标识连接,切换网络(WIFI → 4G)不需要重新建连。

Q6:HTTPS 的握手过程?(TLS 1.2 / 1.3)

核心考点:非对称加密传递对称密钥 → 对称加密传输数据,CA 证书防中间人攻击。

背诵话术

HTTPS = HTTP + TLS/SSL,核心解决 HTTP 的三大问题:加密防窃听(机密性)、验证防伪装(身份认证)、摘要防篡改(完整性)

TLS 1.2 握手(4 次 RTT 包括 TCP 握手)

  1. Client Hello:客户端发送支持的 TLS 版本、加密套件(Cipher Suite)列表、随机数 1。
  2. Server Hello:服务端选择一个加密套件(如 ECDHE_RSA_AES128_GCM_SHA256),返回证书(包含公钥)、随机数 2。
  3. 客户端验证:CA 验证证书合法性 → 用 ECDHE 算法结合随机数 1 和随机数 2 生成预主密钥(Premaster Secret),用服务端公钥加密后发给服务端 → 双方各自用 HMAC 算法基于预主密钥 + 两个随机数计算出同一个会话密钥(Session Key)
  4. 服务端解密:用自己的私钥解出预主密钥,同样计算出会话密钥 → 后续所有数据用会话密钥对称加密传输。

TLS 1.3 的改进:握手仅需 1-RTT(总 2-RTT 含 TCP),简化了加密套件(去掉 RSA 密钥交换、只保留 ECDHE 等前向安全套件),去掉了 CBC 等不安全算法。支持 0-RTT 恢复(之前连过的客户端用 PSK 直接发加密数据,适合 Web 页面重复访问)。

中间人攻击如何防止:攻击者可以截获并转发通信,但无法通过 CA 证书验证——攻击者伪造的证书不受客户端信任的 CA 签发。浏览器/操作系统内置了可信 CA 的公钥列表,任何不被这些 CA 签发或过期的证书都会被浏览器拦截。


三、IO 模型

Q7:BIO / NIO / AIO 的区别?

核心考点:阻塞、非阻塞(多路复用)、异步,Java 中的实现。

背诵话术

  • BIO(Blocking IO,同步阻塞):每个客户端连接分配一个线程,accept()read() 都会阻塞等待。当连接数增多时,线程数膨胀,大量线程被阻塞 IO 操作占用,线程切换开销巨大,CPU 大部分时间在切线程而非处理业务。Tomcat 默认的连接模型在早期版本是 BIO。

  • NIO(Non-Blocking IO,同步非阻塞 + IO 多路复用):一个线程通过 Selector(多路复用器) 管理多个 SocketChannel,Selector 轮询所有 Channel,只处理就绪的(有数据可读/可写)。相比 BIO 大幅减少了线程数(一个线程可管理上万个连接)。Java NIO 底层在 Linux 上通过 epoll 实现(Windows 用 IOCP)。Tomcat 8 起默认使用 NIO。

  • AIO(Asynchronous IO,异步非阻塞):用户线程发起 IO 请求后立即返回,内核完成 IO 操作后通过回调或 Future 通知用户线程。Java AIO 底层在 Linux 上仍是 epoll 模拟(真正的异步 IO 需要 io_uring,Linux 5.1+ 支持,吞吐更高,延迟更低)。

面试加分表述:”NIO 的高性能本质是用一个 Selector 线程 + 少数 Worker 线程,替代了 BIO 的每个连接一个线程的模式,通过 epoll 的事件驱动机制减少线程切换开销。”


Q8:select / poll / epoll 区别?

核心考点:epoll 是 Linux 高性能网络编程(Nginx、Redis、Netty)的基石。

背诵话术

select poll epoll
数据结构 固定长度的 fd_set 位图 动态的 pollfd 数组(链表) 基于红黑树 + 就绪链表
fd 数量限制 默认 1024(FD_SETSIZE 无限制 无限制,受系统内存上限
扫描方式 每次调用全量扫描 fd_set 每次调用全量扫描 pollfd 数组 事件驱动,只扫描就绪 fd 链表
数据结构拷贝 每次调用 fd_set 从用户态拷贝到内核态 每次 pollfd 数组拷贝 红黑树在内核维护,只需添加一次 fd
时间复杂度 O(N) O(N) O(1)(就绪 fd 数量固定)

epoll 为什么快

  1. epoll 在内核中维护一棵红黑树来存储所有监听的 fd,添加/删除 fd 只需操作这棵树,避免了每次调用都复制整个 fd 集合到内核。
  2. 当某个 fd 就绪时,通过回调机制将其从红黑树移到就绪链表epoll_wait() 只处理这个链表,不需要遍历所有 fd。
  3. 支持 水平触发(LT,默认)边缘触发(ET)。ET 模式下只通知一次(直到变为未就绪再变为就绪),需要非阻塞 IO + 循环读写直到 EWOULDBLOCK,编程复杂度高但性能更好。

四、Cookie / Session / Token

Q9:Cookie、Session、JWT / Token 的区别?

核心考点:存储位置(客户端 vs 服务端)、无状态 vs 有状态。

背诵话术

  • Cookie:存储在浏览器端的小段文本(≤4KB),由服务端通过 Set-Cookie 响应头设置,浏览器后续请求自动携带在 Cookie 请求头中。可设置 HttpOnly(禁止 JS 读取,防 XSS)、Secure(仅 HTTPS 传输)、SameSite(防 CSRF)。
  • Session:存储在服务端的用户会话数据(通常放 Redis),浏览器只存一个 JSESSIONID Cookie。服务收到请求后通过 JSESSIONID 查找对应的 Session 对象获取用户信息。缺点:服务端需要维护 Session 状态,不适合分布式环境(需要 Redis 集中存储 Session),也不适合 App/小程序(没有浏览器 Cookie 机制)。
  • JWT(JSON Web Token)无状态自包含令牌,由 Header(指定算法 RS256/HS256)+ Payload(存储用户 ID、过期时间等非敏感信息)+ Signature(签名防篡改)三部分组成,Base64 编码后拼接。优点:服务端不需要存储 Token(没有状态),适合分布式微服务架构、移动端;缺点:无法主动撤销(即使封禁用户,Token 在过期前依然有效),Payload 只 Base64 编码(可解码查看,不能存敏感信息),体积比 Session ID 大。

选型建议:招行这类金融系统内网环境使用 Session + Redis 集中存储(可主动失效);对外开放 API 使用 AppKey + HMAC 签名(类似美团 OpenAPI);非敏感业务可使用 JWT 简化架构。


五、高频面试题

Q10:浏览器输入 URL 后发生了什么?

核心考点:DNS 解析 → TCP 连接 → HTTP 请求 → 服务端处理 → 渲染,考查全局理解。

背诵话术

  1. DNS 解析:浏览器先查本地缓存(浏览器 DNS → 操作系统 hosts → 路由器 → ISP DNS 服务器),递归解析域名(如 www.example.com)得到目标 IP 地址。

  2. TCP 连接(三次握手):浏览器向目标 IP 的 443(HTTPS)/ 80(HTTP)端口发起 TCP 连接。

  3. TLS 握手(HTTPS):如果是 HTTPS,进行 TLS 1.2/1.3 握手,协商加密套件和会话密钥。

  4. HTTP 请求:浏览器发送 HTTP 请求报文(含请求方法、URL、Header、Body 等)。

  5. 服务端处理:请求到达 Nginx(反向代理 + 负载均衡)→ 路由到后端微服务 → Spring MVC 处理(DispatcherServlet → Controller → Service → DAO → MySQL/Redis),生成响应报文。

  6. 浏览器接收并渲染:浏览器收到响应 → 解析 HTML 构建 DOM Tree → 解析 CSS 构建 CSSOM → 合并为 Render Tree → Layout(计算位置)→ Paint(绘制)→ 请求和加载 JS / 图片等外部资源。

有经验的面试官会追问”DNS 用的是 TCP 还是 UDP”,答案是:默认 UDP,响应超过 512 字节时切换到 TCP