面试追问地图
| 主问题 | 必讲关键点 | 下一层追问 |
|---|---|---|
| OSI/TCP-IP | 分层职责、封装与解封装 | 一次 HTTP 请求各层发生什么 |
| TCP 三次握手 | 序列号、能力确认、状态迁移 | 报文丢失、SYN Flood、accept 队列 |
| TCP 四次挥手 | 半关闭、TIME_WAIT、2MSL | 大量 TIME_WAIT/CLOSE_WAIT |
| TCP 可靠性 | ACK、重传、滑动窗口、排序 | RTO、快速重传、流量/拥塞控制 |
| 粘包拆包 | TCP 字节流无消息边界 | 固定长度、分隔符、长度字段 |
| HTTP/1.1/2/3 | 连接复用、帧、多路复用 | 队头阻塞、QUIC 为什么基于 UDP |
| HTTPS | 证书、密钥交换、对称加密 | 中间人攻击、会话复用、TLS 1.3 为何 1-RTT、0-RTT 重放 |
| 证书链 | 逐级验签、信任锚、域名校验 | 中间 CA 意义、吊销机制、mTLS 与抓包原理 |
| 服务端推送 | 长轮询/SSE/WebSocket 对比 | 网关缓冲与超时的坑、百万连接架构、LLM 为何用 SSE |
| DNS | 递归/迭代、缓存、记录类型 | UDP/TCP、缓存污染、负载均衡 |
| Cookie/Session/JWT | 状态位置和传递方式 | XSS、CSRF、撤销和集群部署 |
| 网络排障 | DNS、连接、TLS、代理、应用 | ping 通/不通与 HTTP 是否可用 |
| HTTP 协议基础 | 状态码、GET/POST、keep-alive | 幂等性、缓存控制、RESTful 规范 |
| 网络安全攻击 | XSS、CSRF、SQL 注入、DDoS | 防护手段、HTTPS 必要性 |
| HTTP 缓存 | 强缓存、协商缓存、Cache-Control | ETag vs Last-Modified、no-cache vs no-store |
| WebSocket | Upgrade 握手、帧格式、全双工 | 心跳机制、与 HTTP 长连接的区别 |
| IP/MAC/ARP | 逻辑地址 vs 物理地址、ARP 广播 | ARP 欺骗、为什么需要两套地址 |
| NAT | 地址转换表、端口映射 | NAT 穿透、STUN/TURN |
| CDN | 边缘节点、DNS 调度、回源 | 缓存刷新、预热、与浏览器缓存的关系 |
网络题应能画状态迁移或报文顺序,并回答任意一个报文丢失后双方会发生什么。
一、计算机网络
OSI 七层模型和 TCP/IP 四层模型
| OSI 七层 | TCP/IP 四层 | 职责 |
|---|---|---|
| 应用层 | 应用层 | 给应用程序提供接口(HTTP、DNS、FTP) |
| 表示层 | 数据格式转换 | |
| 会话层 | 建立、管理、终止通信会话 | |
| 传输层 | 传输层 | 端到端数据传输(TCP、UDP) |
| 网络层 | 网络层 | 路由、转发、分片(IP) |
| 数据链路层 | 网络接口层 | 封帧、差错检测、MAC 寻址 |
| 物理层 | 物理介质传输比特流 |
TCP 和 UDP 区别
- 连接 :TCP 面向连接(三次握手),UDP 无连接
- 可靠性 :TCP 可靠传输(序列号、确认应答、超时重传),UDP 不可靠
- 流量/拥塞控制 :TCP 有,UDP 无
- 服务对象 :TCP 一对一,UDP 支持一对一/一对多/多对多
- 首部开销 :TCP 20 字节起,UDP 固定 8 字节
- 传输方式 :TCP 流式无边界,UDP 有边界
TCP 三次握手
- 客户端发送 SYN(seq=client_isn),进入 SYN-SENT
- 服务端回复 SYN+ACK(seq=server_isn, ack=client_isn+1),进入 SYN-RCVD
- 客户端回复 ACK(ack=server_isn+1),双方进入 ESTABLISHED
为什么是三次不是两次 :
- 防止历史重复连接造成混乱(主要原因)
- 同步双方初始序列号
- 避免资源浪费
第三次握手丢失 :服务端未收到 ACK 会触发超时重传 SYN-ACK,直到最大重传次数后断开。ACK 本身不重传,由对方重传对应报文。
半连接队列/全连接队列 :收到 SYN 后连接存入半连接队列,三次握手完成后移入全连接队列(accept 队列),等待 accept 取出。
SYN Flood 攻击与半连接队列/全连接队列
频次 ★★★★ · 难度 🟡 · 高频:阿里/字节/美团
队列机制:
客户端 SYN → 半连接队列(syn backlog,未完成握手) → 握手完成 → 全连接队列(accept backlog,等待应用层 accept)
| 队列 | 存储内容 | 状态 | 大小控制 | 溢出影响 |
|---|---|---|---|---|
| 半连接队列(SYN Queue) | 收到 SYN 但未完成握手的连接 | SYN-RCVD | net.ipv4.tcp_max_syn_backlog(默认 128~1024) | 服务端丢弃新 SYN,客户端收不到 SYN-ACK |
| 全连接队列(Accept Queue) | 完成三次握手待应用 accept 的连接 | ESTABLISHED | min(backlog, somaxconn),backlog 由 listen(fd, backlog) 指定,net.core.somaxconn 上限 | 服务端发送 RST 或保留连接(取决于 tcp_abort_on_overflow) |
面试追问:
- SYN Flood 攻击原理?→ 攻击者发送大量 SYN 但不回复 ACK,填满服务端半连接队列,导致正常客户端的 SYN 被丢弃。本质是资源不对称攻击——攻击者消耗极少的本地资源就能耗尽服务端内存和队列。
- 如何防御 SYN Flood?
- SYN Cookie(最常用):不分配半连接结构体,将连接信息编码到 SYN-ACK 的 seq(
seq = hash(src_ip, src_port, dst_ip, dst_port, secret)),客户端回复 ACK 时服务端解码验证,通过才分配连接——将 O(内存) 变为 O(计算),是 SYN Flood 的”银弹” tcp_syncookies = 1:内核在检测到半连接队列满时自动启用,Linux 默认开启- 缩短 SYN-ACK 重传次数(
tcp_synack_retries从默认 5 降到 2),加速清理僵尸半连接 - 增大半连接队列(
tcp_max_syn_backlog) - 反向代理/防火墙在边缘层拦截攻击流量,不让攻击包打到业务服务器
- SYN Cookie(最常用):不分配半连接结构体,将连接信息编码到 SYN-ACK 的 seq(
- 全连接队列满的表现?→
ss -lnt查看Send-Q(当前 backlog),若Recv-Q> 0 表示队列有积压;netstat -s | grep overflowed查看溢出计数。应用层应尽快accept或使用非阻塞模型(如 Netty 的 eventloop 异步处理)。 tcp_abort_on_overflow的作用?→ 设置为 0(默认),全连接队列满时内核丢弃 ACK 并重发 SYN-ACK(客户端重传 ACK 时可能 enter);设置为 1,直接发 RST 断开(客户端收到connection reset by peer)。生产环境推荐保持 0。
TCP 四次挥手
- 主动方发送 FIN,进入 FIN-WAIT-1
- 被动方回复 ACK,进入 CLOSE-WAIT(此时被动方可能还有数据要发送)
- 被动方发完数据后发送 FIN,进入 LAST-ACK
- 主动方回复 ACK,进入 TIME-WAIT,等待 2MSL 后关闭
为什么不能合并中间两次 :被动方收到 FIN 时,应用层可能还有数据要发送,所以 ACK 和 FIN 通常分开发送。但如果被动方没有数据要发送且开启了延迟确认,第二、三次可以合并。
为什么等待 2MSL :确保最后一个 ACK 能到达被动方;让网络中残留的数据包消亡。
TCP 重传机制
| 机制 | 触发条件 | 特点 |
|---|---|---|
| 超时重传 | RTO 内没收到 ACK | RTO 基于 RTT 动态计算(加权平均 + 波动量);每次重传 RTO 翻倍(指数退避) |
| 快速重传 | 收到 3 个重复 ACK | 不等 RTO 直接重传,解决”超时太久”的问题 |
| SACK | 选择性确认(TCP 选项) | ACK 里带上已收到的不连续块,只重传真正丢的段 |
| D-SACK | 重复 SACK | 告诉发送方”这段我收到过两次”,用于区分是包丢了还是 ACK 丢了 |
常见追问:快速重传解决了超时时间的问题,但发送方不知道该重传一个还是全部 → SACK 补上这块信息。
TCP 流量控制 vs 拥塞控制
- 流量控制(滑动窗口):控制的是接收方的处理能力。接收方在 ACK 里通告接收窗口(rwnd = 接收缓冲区剩余空间),发送方未确认的数据量不能超过 rwnd。窗口为 0 时发送方停发,靠零窗口探测报文避免死等。
- 拥塞控制(cwnd):控制的是网络的承载能力,防止把网络打垮(见下节)。
- 发送窗口 = min(rwnd, cwnd)——两个约束同时生效,一个保护对端,一个保护网络。
通用概念:接收窗口是最典型的背压:把下游的真实状况逆着数据流传回上游;而拥塞控制面向的是网络而非对端。
TCP keepalive vs HTTP keep-alive
| TCP keepalive | HTTP keep-alive | |
|---|---|---|
| 层级 | 传输层,内核实现 | 应用层,HTTP 头 |
| 作用 | 探测死连接(默认 2 小时无数据才开始探测) | 连接复用,一个 TCP 连接发多个请求 |
| 打开方式 | SO_KEEPALIVE 套接字选项 | HTTP/1.1 默认开启,Connection: close 关闭 |
常见追问:长连接推送服务为什么还要应用层心跳?→ TCP keepalive 周期太长且中间设备(NAT/LB)可能提前掐断空闲连接,应用层心跳(如每 30s)既保活又能探测应用假死。
TCP 拥塞控制
四个阶段:
- 慢启动 :cwnd 从 1 开始,每收到一个 ACK 就 +1,指数增长。当 cwnd >= ssthresh 时进入拥塞避免
- 拥塞避免 :每收到一个 ACK,cwnd += 1/cwnd,线性增长
- 拥塞发生(超时重传) :ssthresh = cwnd/2,cwnd 重置为 1,重新慢启动
- 快速恢复(快速重传) :收到 3 个重复 ACK 时,cwnd = cwnd/2,ssthresh = cwnd,进入快速恢复而非慢启动
TCP 粘包/拆包如何解决?
是什么:TCP 是字节流协议,没有消息边界——发送方连续两次 send 可能被接收方一次 read 收全(粘包),也可能一次 send 被拆成多次 read(拆包)。应用层必须自己划定消息边界,常见三种方式:
- 固定长度消息
- 特殊字符边界(如早期文本协议用
\r\n分隔) - 自定义消息结构(包头固定长度 + 数据长度字段),Netty 对应
LengthFieldBasedFrameDecoder,见Netty与RPC”编解码与 TCP 边界”
HTTP 是怎么解决的:HTTP/1.1 靠 Content-Length 头字段显式告知 body 长度(不知道长度时用 Transfer-Encoding: chunked 分块,每块自带长度前缀);HTTP/2 把消息拆成自带长度字段的二进制帧(Frame),边界问题在协议层面天然消失,不再需要应用层额外处理。
HTTP 常用状态码
- 2xx :200 成功
- 3xx :301 永久重定向,302 临时重定向
- 4xx :404 资源不存在,405 方法不支持
- 5xx :500 服务器内部错误,502 网关收到无效响应,504 网关超时
GET 和 POST 区别
- 语义 :GET 是安全且幂等的(只读),POST 会修改资源
- 参数位置 :GET 在 URL 中(仅 ASCII,浏览器限制长度),POST 在 body 中(任意格式,无限制)
- 缓存 :GET 可缓存,POST 一般不缓存
HTTP 和 HTTPS 区别
- HTTPS 在 TCP 和 HTTP 之间加入 SSL/TLS 加密
- HTTP 默认端口 80,HTTPS 默认 443
- HTTPS 需要 CA 证书
- HTTP 明文有窃听、篡改、冒充风险;HTTPS 解决这些问题
HTTPS 握手过程(TLS 1.2)
- ClientHello:客户端发送协议版本、随机数、密码套件列表
- ServerHello:服务端确认版本、随机数、密码套件、返回数字证书
- 客户端验证证书,用公钥加密 pre-master key 发给服务端
- 双方用三个随机数计算会话秘钥,互相通知加密算法切换
注意上述是 RSA 密钥交换流程,完整握手需要 2-RTT;TLS 1.2 用 ECDHE 也仍需 2-RTT(多一次往返协商 DH 参数)。TLS 1.3 如何压到 1-RTT 见下节。
TLS 1.3 握手:为什么只要 1-RTT?
是什么:TLS 1.3(RFC 8446,2018)把握手从 2-RTT 压缩到 1-RTT,核心一招:客户端不等协商结果,在 ClientHello 里直接带上 DH 公钥(key_share 扩展,赌服务端支持主流曲线如 x25519),服务端在 ServerHello 里回自己的 DH 公钥——一个往返双方就算出了会话密钥。
TLS 1.2 (ECDHE) TLS 1.3
C → S: ClientHello C → S: ClientHello + key_share ★
C ← S: ServerHello, Certificate, C ← S: ServerHello + key_share,
ServerKeyExchange {Certificate, Finished}(已加密)
C → S: ClientKeyExchange, Finished C → S: {Finished}, 直接发应用数据 ★
C ← S: Finished
------ 2-RTT 后才能发数据 ------ ------ 1-RTT 后即可发数据 ------为什么能砍掉一个 RTT(三个配套设计,答题按这个顺序说):
- 砍掉 RSA 密钥交换:只保留 (EC)DHE,强制前向安全(服务端私钥泄露也解不开历史流量;RSA 交换模式下私钥一漏全部历史密文可解)
- 密码套件从 300+ 精简到 5 个:没有协商空间,客户端”猜中”概率极高;猜错服务端回 HelloRetryRequest 退化为 2-RTT
- 证书也加密传输:ServerHello 之后的所有握手消息都用刚算出的密钥加密,中间人连服务端证书都看不到(TLS 1.2 证书是明文)
0-RTT(会话恢复):曾经连接过的客户端持有服务端发的 PSK(session ticket),重连时 ClientHello 里直接附带早期数据(early data)——第 0 个 RTT 就把 HTTP 请求发出去了。代价是重放攻击风险:攻击者截获 0-RTT 报文原样重发,服务端无法区分,所以 0-RTT 数据只能承载幂等请求(GET),网关(如 Nginx ssl_early_data)默认关闭。
与 QUIC 的衔接:QUIC(HTTP/3 底层)把 TLS 1.3 直接内建进传输层,传输握手和加密握手合并在同一个 1-RTT 里完成(TCP+TLS1.3 = 1 RTT 建 TCP + 1 RTT 握 TLS = 2-RTT,QUIC 只要 1-RTT,会话恢复时 0-RTT)——这是”QUIC 建连快”的真正来源,见下文 HTTP/2 与队头阻塞相关小节。
常见追问:
- TLS 1.3 还需要证书吗?→ 需要。DH 交换只保证密钥安全,不证明”对端是谁”,身份认证仍靠证书签名(防中间人)
- 0-RTT 为什么防不了重放?→ early data 用的是上次会话的 PSK 派生密钥,服务端处理它时握手尚未完成,没有本次的新鲜随机数参与,无法识别”这是不是重发的旧报文”
- 会话复用 TLS 1.2 和 1.3 有什么区别?→ 1.2 用 session ID(服务端存状态)或 session ticket;1.3 统一为基于 PSK 的 ticket 机制,且每次恢复会重新做 DH(保持前向安全)
证书链怎么验证?中间人为什么伪造不出有效证书?
频次 ★★★ · 难度 🟡
是什么:握手中服务端返回的不是一张证书而是证书链:站点证书(leaf)← 中间 CA 证书 ← 根 CA。每张证书 = 明文部分(域名 SAN、公钥、有效期、签发者)+ 签发者用自己私钥对明文摘要做的签名。根 CA 证书预装在操作系统/浏览器的信任库里,是自签名的信任锚。
逐级验签流程:
1. 取站点证书,按"签发者"字段找到中间 CA 证书
2. 用中间 CA 的公钥解开站点证书上的签名得到摘要,
与自己对证书明文重新计算的摘要比对 —— 一致则此级可信
3. 同法用根 CA 公钥验中间 CA 证书
4. 根 CA 是否在本地信任库中?在 → 信任链闭合
5. 附加检查:域名匹配(SAN)、有效期、吊销状态(CRL/OCSP)为什么中间人拿不到有效证书(答题核心,逐条堵死它的路):
- 原样转发真证书?→ 证书里公钥对应的私钥在真服务器手里,中间人解不开后续密钥交换,链路建不起来
- 自己伪造一张?→ 签名需要某个受信 CA 的私钥,拿不到;用自己私钥签,第 4 步找不到信任锚直接断链
- 骗 CA 给它签目标域名的证书?→ CA 签发前要做域名所有权校验(DNS 记录/文件验证);CA 违规乱签会被**证书透明度日志(CT)**公开曝光、被浏览器整体除名(DigiNotar 因此破产)
所以 Charles/Fiddler 抓 HTTPS 包必须先让你手动安装它的根证书——本质是自愿把中间人加进信任锚;App 侧对抗手段是证书锁定(certificate pinning),把预期证书/公钥硬编码进客户端,不认系统信任库里的其他链。
自签名与 mTLS 场景:
| 场景 | 做法 | 信任来源 |
|---|---|---|
| 公网网站 | 公共 CA(Let’s Encrypt/DigiCert) | 系统内置信任库 |
| 企业内网 | 自建根 CA 签发 | 运维把根证书推送到所有机器信任库 |
| 服务间双向 TLS(mTLS) | 客户端也出示证书,双方互验 | 服务网格(Istio)自动签发并轮换短期证书 |
普通 TLS 只验服务端身份;mTLS 让服务端也要求并校验客户端证书,微服务零信任架构里用它替代”IP 白名单”做服务间身份认证。
常见追问:
- 为什么要有中间 CA,根 CA 直接签不行吗?→ 根私钥太贵重,泄露 = 整个信任体系崩塌,所以离线冷存;中间 CA 承担日常签发,出事可单独吊销,不动摇根
- 证书被吊销了客户端怎么知道?→ CRL(全量吊销列表,大而更新慢)→ OCSP(在线实时查,有隐私和可用性问题)→ OCSP Stapling(服务端代查,把带 CA 签名的结果钉进握手,主流方案)
- 证书校验和加密是什么关系?→ 独立的两件事:DH/RSA 解决”密钥不被窃听”,证书解决”对端是谁”——只加密不验身份,加密通道可能直通中间人(呼应上一节 TLS 1.3 追问)
通用概念:证书链是信任的逐级传递——用”已信任者的签名”把信任从锚点延伸到陌生实体,验证方只需预置极少量的锚。同类:JWT 签名验证(服务端只信自己的签名密钥,见安全认证)、软件包签名(Maven/APT 的 GPG 校验,见构建与依赖管理)。
HTTP/1.1 和 HTTP/2.0 区别
- 头部压缩 :HPACK 算法,消除重复头部
- 二进制格式 :统称为帧(Headers Frame + Data Frame)
- 并发传输 :多 Stream 复用一条 TCP 连接,解决队头阻塞
- 服务器推送 :服务端主动推送资源(现代浏览器已逐步弃用)
HTTP/3(QUIC)解决了什么问题?
频次 ★★★ · 难度 🟡 · 高频:字节/腾讯
HTTP/3 的最大变化:将传输层从 TCP 更换为 QUIC(基于 UDP)。
| 问题 | HTTP/2 的痛点 | HTTP/3(QUIC)的解法 |
|---|---|---|
| 队头阻塞 | TCP 丢包阻塞整条连接(TCP 必须按序交付),即使只丢一个包,后面的 HTTP/2 Stream 都得等重传 | QUIC 在 UDP 上自建多路复用,丢包只影响单个 Stream,其他 Stream 不受影响 |
| 连接建立延迟 | TCP 三次握手(1-RTT)+ TLS 1.3 握手(1-RTT)→ 至少 2-RTT 才能发数据 | QUIC 支持 0-RTT(首次 1-RTT,后续可 0-RTT 发数据),握手 + 数据传输合并 |
| 连接迁移 | TCP 连接靠四元组(src_ip:port, dst_ip:port)标识,切换网络(WiFi → 4G)IP 变 → 连接重建 | QUIC 用 Connection ID 标识连接,IP/端口变化连接不中断,移动场景无需重连 |
| 拥塞控制 | 依赖内核 TCP 栈,升级需要修改 OS 参数 | QUIC 在用户态实现拥塞控制,可快速迭代(如 BBR、Cubic 切换),不像 TCP 绑定内核版本 |
QUIC 的核心设计:
- 基于 UDP,在用户态实现可靠传输(自建 ACK/重传/流控),避免内核升级依赖
- 内置 TLS 1.3,加密默认强制(不像 TCP 可选),头部和载荷全部加密
- 单连接内多个 Stream 独立有序,一个 Stream 丢包不影响其他 Stream
面试追问:
- QUIC 的 0-RTT 不安全吗?→ 0-RTT 存在重放攻击风险——攻击者截获 0-RTT 请求重复发送,服务端无法区分。QUIC 禁止在 0-RTT 中发送幂等操作,或通过时间窗口限制。
- QUIC 当前部署情况?→ 谷歌全系产品(Chrome/Youtube/搜索)使用 QUIC 超过十年;Cloudflare、Facebook 广泛部署;国内字节跳动大规模使用。HTTP/3 标准已发布(RFC 9114),主流浏览器和服务端(nginx、Caddy、Envoy)均已支持。
服务端怎么把消息推给浏览器?长轮询、SSE、WebSocket 怎么选?
频次 ★★★ · 难度 🟡
是什么:HTTP 是请求-响应模型,服务端不能主动开口,“推送”都是绕出来的:
| 维度 | 短轮询 | 长轮询 | SSE | WebSocket |
|---|---|---|---|---|
| 原理 | 定时重复请求 | 请求挂起,有数据才响应,响应完立刻再发下一个 | 一次请求,响应不结束,流式吐事件 | HTTP 101 Upgrade 后升级为全双工 |
| 方向 | 拉 | 伪推(拉的变体) | 服务端 → 客户端单向 | 双向 |
| 协议 | HTTP | HTTP | HTTP(text/event-stream) | 独立 ws:// 协议 |
| 实时性 | 差(取决于间隔) | 好 | 好 | 最好 |
| 断线续传 | 无所谓 | 天然 | 协议内建:自动重连 + Last-Event-ID | 手动实现心跳/重连/补偿 |
| 服务端成本 | 大量无效请求 | 挂起连接 | 一个长连接 | 一个长连接 + 帧协议处理 |
SSE 长什么样:响应头 Content-Type: text/event-stream,服务端往同一个响应里持续写:
id: 42
event: tick
data: {"price": 100.5}
data: 第二条消息浏览器 EventSource API 自动解析事件、断线后自动带 Last-Event-ID 头重连——续传是协议内建的,这是它相对 WebSocket 的最大优势。ChatGPT 式打字机输出就是 SSE,它已是 LLM 流式响应的事实标准。
怎么选(先问方向,再问频率):
- 只要服务端→客户端:通知、行情、AI 流式输出、任务进度 → SSE,完整保留 HTTP 语义(标准认证头、经过网关/CDN 不用特殊处理)
- 双向高频交互:IM、协同编辑、多人游戏 → WebSocket
- 低频或兼容性兜底 → 长轮询,任何网络环境都能过
与网关/LB 配合的坑(区分背题和实战的部分):
- 代理缓冲:Nginx 默认
proxy_buffering on会攒够缓冲区才转发,SSE 一条都吐不出来——要proxy_buffering off或响应头X-Accel-Buffering: no - 空闲超时:LB/网关的 idle timeout(常见 60s)会掐掉没有流量的长连接——SSE 定期发注释行
: keepalive,WebSocket 用 ping/pong 帧保活 - 有状态连接:长连接绑定具体节点,网关扩缩容/发布会掐断全部连接,要预估重连风暴(指数退避 + 抖动);WebSocket 过代理还需转发
Upgrade/Connection头 - 连接数上限:HTTP/1.1 下 SSE 独占一个连接,浏览器同域名 6 连接封顶,多标签页会耗尽——HTTP/2 多路复用解决(见上一节)
常见追问:
- 百万在线的推送系统怎么架构?→ 接入层(Netty 长连接网关)只管连接,业务层无状态;推送消息经 MQ 广播到网关节点,各节点查本地连接表下发——连接与业务解耦,见Netty与RPC
- SSE 能传二进制吗?→ 不能,只支持 UTF-8 文本;二进制场景用 WebSocket 或退化为 base64
- 为什么 LLM 接口用 SSE 而不是 WebSocket?→ 纯单向流,SSE 够用且成本低:认证沿用 HTTP header、网关/LB 不需要特殊改造、断线续传内建;WebSocket 的双向能力用不上,还得自建心跳重连
通用概念:推 vs 拉的取舍贯穿系统设计——推实时但要管背压,拉自控速但有延迟:Kafka 消费端选拉(见消息队列”Kafka 是推模式还是拉模式”)、Feed 流推拉结合(见系统设计)。
DNS 解析流程
客户端 -> 本地 DNS -> 根 DNS -> 顶级域 DNS -> 权威 DNS -> 返回 IP
DNS 使用 UDP 还是 TCP :都用,端口 53。查询一般用 UDP(低延迟),区域传送或响应超过 512 字节时用 TCP。
Cookie 和 Session 区别
- 存储位置 :Cookie 在客户端,Session 在服务端
- 安全性 :Cookie 易受 XSS 攻击(可设置 HttpOnly 防护),Session 更安全但需防范 Session 劫持
- 容量 :Cookie 约 4KB,Session 理论上无限制
分布式场景的会话方案(Session 集群、JWT、SSO)详见安全认证。
Socket、TCP、HTTP 关系
- HTTP:应用层协议,定义数据格式
- TCP:传输层协议,建立可靠连接
- Socket:通信的端点抽象,提供网络编程接口
打开网页的完整网络过程
- URL 解析 -> 2. 缓存判断(浏览器/系统/路由器/ISP DNS)-> 3. DNS 解析 -> 4. ARP 获取 MAC -> 5. TCP 三次握手 -> 6. TLS 握手(HTTPS)-> 7. 发送 HTTP 请求 -> 8. 服务器处理并返回
常见网络攻击
- DDoS :分布式拒绝服务攻击,通过大量请求淹没服务器
- SQL 注入 :用户输入未经转义直接拼入 SQL,用参数化查询解决
- CSRF :跨站请求伪造,用 Token 验证、验证 Referer 解决
- XSS :跨站脚本攻击,输入验证、输出编码、CSP 策略解决
- DNS 劫持 :篡改 DNS 响应,使用 DNS over HTTPS/TLS 解决
XSS/CSRF 的攻击方式与防护细节、密码存储与认证体系详见安全认证。
二、HTTP 深入
HTTP 请求报文和响应报文是怎样的?
是什么:HTTP 报文 = 起始行 + 头部 + 空行 + 消息体(可选)。
请求报文:
GET /api/users?page=1 HTTP/1.1 ← 请求行(方法 URI 协议版本)
Host: example.com ← 请求头
Accept: application/json
User-Agent: Mozilla/5.0
← 空行(CRLF,头与体之间的分隔)
{"key":"value"} ← 消息体(POST/PUT 才有)响应报文:
HTTP/1.1 200 OK ← 状态行(协议版本 状态码 原因短语)
Content-Type: application/json ← 响应头
Content-Length: 123
Set-Cookie: session=abc123
← 空行
{"data": [...]} ← 消息体常见追问:请求头和响应头有什么区别?→ 请求头主要传客户端信息(Accept、User-Agent、Authorization)、请求条件(If-Modified-Since);响应头传服务器信息(Set-Cookie、Content-Type)、缓存指令(Cache-Control、ETag)。
什么是强缓存和协商缓存?它们的工作原理是什么?
频次 ★★★★ · 难度 🟡 · 高频:字节/美团
是什么:浏览器缓存的两级策略——强缓存直接复用以避免发请求,协商缓存发请求但让服务端判断是否可复用,节省传输。
强缓存(不发请求,状态码 200 OK from disk/memory cache):
Cache-Control: max-age=3600(HTTP/1.1,相对时间,优先级最高)Expires: Thu, 01 Dec 2025 16:00:00 GMT(HTTP/1.0,绝对时间,服务器时钟不准会出错)
协商缓存(发请求,服务端返回 304 Not Modified 表示”用你的缓存”):
ETag/If-None-Match:服务端生成资源内容的唯一标识(哈希/版本号),客户端下次请求带上,一致则返回 304Last-Modified/If-Modified-Since:服务端返回资源最后修改时间,客户端下次请求带上,未修改则返回 304
强缓存 vs 协商缓存决策流程:
浏览器请求资源
├── 有缓存 && 强缓存未过期 → 直接用缓存(不发请求)
├── 强缓存过期 → 发请求,带 If-None-Match / If-Modified-Since
│ ├── 304 → 用本地缓存(刷新过期时间)
│ └── 200 → 下载新资源,更新缓存
└── 无缓存 → 发请求,下载资源并缓存常见追问:
- ETag 和 Last-Modified 哪个优先级高?→ ETag 更高。ETag 能解决”1 秒内修改两次 Last-Modified 无法感知”的问题,以及”文件内容不变但 mtime 变了”的误判。
Cache-Control: no-cache和no-store的区别?→no-cache不是”不缓存”,而是每次用缓存前必须找服务端验证(走协商缓存);no-store才是真正的”完全不缓存,每次重新下载”。- 前端资源怎么设置缓存策略?→ HTML 文件设协商缓存(
no-cache),JS/CSS/图片等带 hash 文件名设强缓存 + 超长 max-age(max-age=31536000),内容变了文件名就变,天然失效。
HTTP 头部字段有哪些常见类型?各自作用是什么?
通用头部:Cache-Control(缓存策略)、Connection(连接管理)、Transfer-Encoding(传输编码,如 chunked)。
请求头部:
Host:目标主机(HTTP/1.1 必带,支持虚拟主机)User-Agent:客户端标识Accept/Accept-Encoding/Accept-Language:客户端能处理的格式Authorization:认证信息Referer:来源页面 URL,用于防盗链和统计分析Range: bytes=0-1023:断点续传,只请求文件的一部分If-None-Match/If-Modified-Since:协商缓存
响应头部:
Content-Type:响应体 MIME 类型(text/html、application/json、multipart/form-data)Content-Length:响应体字节数Set-Cookie:设置 CookieETag/Last-Modified:提供缓存验证信息Location:重定向目标(配合 301/302)Access-Control-Allow-Origin:CORS 跨域白名单
常见追问:Content-Type 常见取值有哪些?→ text/html、application/json、application/x-www-form-urlencoded(表单默认)、multipart/form-data(文件上传)、text/event-stream(SSE 流式推送)、application/octet-stream(二进制下载)。
HTTP 管道化是什么?它解决了什么问题?
是什么:HTTP/1.1 管道化允许客户端在一个 TCP 连接上连续发送多个请求而不等上一个响应返回(类似流水线),服务端按请求顺序返回响应。
为什么几乎没人用:队头阻塞(HOL Blocking)——第一个响应慢会把后面所有响应堵住,服务端必须按序返回。浏览器默认关闭管道化,实际用”多个并行 TCP 连接”(同域名 6 个连接)来弥补。HTTP/2 的多路复用从根本上解决了这个问题。
HTTP 多个 TCP 连接怎么实现?
是什么:浏览器对同一域名建立多个 TCP 连接(HTTP/1.1 规范建议 ≤2,实际浏览器默认 6 个),并行发送请求,每个连接的响应独立处理,互不阻塞。域名分片(sharding)可突破此限制——把资源分散到多个域名(static1.example.com、static2.example.com),每个域名独立享有 6 连接配额。
HTTP/2 的改进:多路复用——一个 TCP 连接上同时跑多个 Stream,每个 Stream 独立传输,彻底消除 HTTP/1.1 的队头阻塞(但 TCP 层面的队头阻塞仍存在,由 HTTP/3 的 QUIC 解决)。
三、网络层与数据链路层
网络分层模型的封装与解封装过程是怎样的?
是什么:数据从应用层往下传,每层加自己的头部(封装);到达目标后从下往上,每层去掉对应头部(解封装):
发送端(封装) 接收端(解封装)
应用层: HTTP 数据 → 应用层拿到 HTTP 数据
传输层: TCP 头 + HTTP 数据 → 传输层去 TCP 头
网络层: IP 头 + TCP 头 + HTTP 数据 → 网络层去 IP 头
链路层: MAC 头 + IP 头 + TCP 头 + HTTP + MAC 尾 → 链路层去 MAC 头尾常见追问:封装过程中源/目标 IP 和 MAC 如何变化?→ IP 地址端到端不变(除非经过 NAT),MAC 地址每经过一跳路由器就变——源 MAC 变成本跳的出口 MAC,目标 MAC 变成下一跳的入口 MAC。
数据链路层和网络层各有哪些主要协议?
数据链路层:
- ARP:IP 地址 → MAC 地址映射
- RARP:MAC 地址 → IP 地址(已基本被 DHCP 取代)
- 以太网(Ethernet):最主流的局域网协议,CSMA/CD 载波监听多路访问
网络层:
- IP:尽力而为的不可靠传输,负责寻址和路由
- ICMP:控制报文(ping 用的就是 ICMP Echo Request/Reply)
- IGMP:组播管理
- 路由协议:OSPF(内部网关)、BGP(外部网关,互联网骨干)
ARP 协议是什么?它在网络通信中有什么作用?
是什么:ARP(Address Resolution Protocol)在局域网内通过 IP 地址查找对应的 MAC 地址。主机发 ARP 广播请求”谁的 IP 是 192.168.1.5?“,目标主机单播回应”是我,我的 MAC 是 aa:bb:cc:dd:ee:ff”。查到的映射缓存到 ARP 表(arp -a 查看)。
ARP 欺骗/中间人攻击:攻击者伪造 ARP 响应,把网关 IP 映射到自己的 MAC,使受害者流量经过攻击者。防御:交换机端口绑定、ARP 防火墙。
IP 地址和 MAC 地址有什么区别?在网络通信中各自扮演什么角色?
| 维度 | IP 地址 | MAC 地址 |
|---|---|---|
| 层级 | 网络层 | 数据链路层 |
| 长度 | IPv4 32 位 / IPv6 128 位 | 48 位(如 aa:bb:cc:dd:ee:ff) |
| 作用 | 标识主机在网络中的位置,用于路由 | 标识网卡硬件,用于局域网内寻址 |
| 可变性 | 可配置、可变化 | 出厂烧录,唯一(可软件修改) |
| 类比 | 收货地址 | 身份证号 |
为什么需要两套地址:IP 是逻辑地址,支持网络拓扑和路由聚合;MAC 是物理地址,保证数据在最后一跳(局域网)到达正确的网卡。IP 负责”送到哪个网络”,MAC 负责”送到该网络中的哪台机器”。
NAT(网络地址转换)是什么?有什么作用?
是什么:NAT 让私有 IP 地址的主机通过一个公网 IP 访问互联网。路由器维护映射表,把内网 IP:端口 ↔ 公网 IP:端口 的转换关系记录下来。
作用:缓解 IPv4 地址枯竭(一个公网 IP 供整个家庭/公司上网);隐藏内网拓扑(外部只能看到 NAT 设备的公网 IP)。
NAT 穿透问题:NAT 对”由内向外发起”的连接天然支持,但”由外向内主动连接”会被 NAT 挡掉。P2P 应用(BT、视频通话)需要 NAT 穿透技术(STUN/TURN/ICE)。
四、WebSocket 与实时通信
WebSocket 是什么?它在网络通信中有什么作用?
频次 ★★★ · 难度 🟡
是什么:WebSocket 是基于 TCP 的全双工通信协议,通过 HTTP Upgrade 机制从 HTTP 升级而来。建立后客户端和服务端可以随时互相发送消息,无需每次请求-响应。
建立过程:
客户端请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务端响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=WebSocket 帧格式:头部最小 2 字节(opcode + 掩码位 + 长度),其后是掩码密钥 + 有效载荷。数据帧支持文本和二进制(blob/arraybuffer)。
与 HTTP 对比:HTTP 是请求-响应,客户端不问服务端不能主动发;WebSocket 是全双工,双方随时可发。WebSocket 建连后不再有 HTTP 头的开销,帧头最小仅 2 字节。详见上文”服务端推送选型”的四种方案对比。
常见追问:WebSocket 心跳怎么做?→ 发送 ping 帧(opcode=0x9),对端必须回复 pong 帧(opcode=0xA)。和 TCP keepalive 不同,WebSocket 的 ping/pong 是应用层帧,不依赖内核参数。
五、网络安全补充
TCP 连接建立过程中,SYN 洪泛攻击如何发生?如何防范?
频次 ★★ · 难度 🟡
是什么:攻击者伪造大量不存在的源 IP,向服务端发送 SYN 包。服务端回复 SYN-ACK 后进入 SYN-RCVD 状态,连接加入半连接队列,等待 ACK 到来。但源 IP 是伪造的,ACK 永远不会来——半连接队列被占满,合法用户无法建立新连接。
防范措施:
- SYN Cookie:不立即分配半连接资源,而是用客户端 IP/端口/时间戳等算出一个 cookie 作为 SYN-ACK 的 seq,只有收到合法 ACK(cookie 验证通过)才分配资源
- 缩短 SYN 超时重传时间:减少半连接占用时间
- 增大半连接队列:
tcp_max_syn_backlog参数 - 防火墙/负载均衡器限流:CDN/高防 IP 在攻击流量到达源站前清洗
什么是端口扫描?在网络安全中有什么作用?
是什么:端口扫描是逐端口发送探测包,根据响应判断端口是否开放及运行的服务。nmap 是最常用的端口扫描工具。
常见扫描类型:SYN 扫描(半开扫描,不发最后的 ACK,不建立完整连接,隐蔽)、TCP 全连接扫描(完整三次握手,日志会记录)、UDP 扫描(发 UDP 包,ICMP 端口不可达说明关闭)。
常见追问:防御端口扫描的方法?→ 防火墙限制连接频率;只开放必要端口;使用端口敲门(port knocking)隐藏敏感端口。
六、CDN 与内容分发
CDN 是什么?它在网络传输中有什么作用?
频次 ★★★★ · 难度 🟡 · 高频:字节/美团
是什么:CDN(内容分发网络)将静态资源缓存到遍布全球的边缘节点,用户请求就近响应,减少延迟和回源带宽。
工作流程:
- 用户请求
cdn.example.com/image.jpg - DNS 智能解析返回离用户最近的 CDN 节点 IP(基于 LDNS 出口 IP 判断地理位置)
- 边缘节点命中缓存 → 直接返回;缓存未命中 → 回源站拉取 → 缓存 → 返回
- 源站内容更新时,通过 CDN 刷新/预热接口主动失效缓存
DNS 调度 vs Anycast 调度:传统 CDN 用 DNS 把用户引导到最近的边缘节点,依赖用户使用的 DNS 服务器(LDNS)的 IP 做地理位置判断;大规模 CDN 则结合 Anycast(同一 IP 在多个节点同时广播,运营商路由自动找最近路径)。
常见追问:CDN 缓存和浏览器缓存的关系?→ 浏览器缓存是最靠近用户的一层,CDN 缓存是中间层,源站是最终数据源。CDN 缓存命中时,响应头里的 Cache-Control 照常传递给浏览器,让浏览器也缓存。两层缓存叠加,进一步减少源站压力。
通用概念:CDN 是”把数据推到离用户最近的地方”——和 DNS 缓存、Redis 缓存、CPU Cache 都遵循相同的”就近原则”,只是层级从芯片→内存→网络→全球逐级放大。