计算机网络面试题
OSI 七层模型与 TCP/IP 四层模型
面试官您好,关于 OSI 七层模型和 TCP/IP 四层模型,我从核心定义、分层职责、对比差异和实际应用四个方面来回答。
开门见山
OSI 七层模型是理论上的“理想国”,TCP/IP 四层模型才是工业界的“活化石”。我们写 Java 网络程序,直接打交道的就是 TCP/IP 协议栈,但 OSI 的思想无处不在。
下面这张图能让你一眼看清两者的对应关系:
OSI 七层模型(理论标准)📚
OSI 是国际标准化组织制定的理论上的网络通信标准,将网络通信划分为 7 个层次,每层各司其职,下层为上层提供服务。
速记口诀:应表会传网链物(从上到下)✅
TCP/IP 四层模型(实际工业标准)💻
TCP/IP 是互联网实际使用的标准,它将 OSI 七层模型进行了简化合并,更符合实际工程需求。
🎯 逐层拆解(重点记忆这几点)
1. 应用层 —— 程序员的直接战场
OSI 把它拆成了三层(应用层、表示层、会话层),但在 TCP/IP 里统统合并成一个应用层。
我们写的 HttpURLConnection、RestTemplate,以及 Nginx 的反向代理,都工作在这一层。
常用协议:HTTP、HTTPS、DNS、FTP。
表示层的数据加密(SSL/TLS)和编码转换,在 Java 里实际上由 javax.net.ssl 包和序列化机制承担,这恰好是 OSI 思想落地的例子。
2. 传输层 —— 端到端可靠性的基石
OSI 和 TCP/IP 对这一层的定义几乎一致。
TCP 提供可靠传输(三次握手、四次挥手、滑动窗口),UDP 则只管发包不管死活。
Java 中 Socket 和 ServerSocket 就是基于 TCP,DatagramSocket 基于 UDP。
面试时你要能脱口而出:TCP 面向连接、字节流;UDP 面向报文、无连接。这也是 Netty 这类框架底层优化的核心。
3. 网络层 —— 寻址与路由
TCP/IP 里叫网际层,核心就是 IP 协议。
路由器根据 IP 地址选择转发路径,就像快递根据城市分拣。
ARP 协议把 IP 转成 MAC 地址,ICMP 协议负责 ping 通不通。
Java 里 InetAddress 类就是对这一层的封装。
4. 数据链路层 & 物理层 —— 硬件的地盘
TCP/IP 将其统一为网络接口层,因为软件工程师很少直接控制网卡或电压信号。
数据链路层的 MAC 地址、交换机转发,物理层的双绞线、光纤,都属于这里。
我们写 Java 时完全感知不到它们的存在,但排查网络问题时 ifconfig 看到的 ether 地址就是 MAC 地址。
核心对比表 📊
| 层级 | OSI 七层模型 | TCP/IP 四层模型 | 核心协议 | 数据单位 |
|---|---|---|---|---|
| 7 | 应用层 | 应用层 | HTTP、FTP、DNS、SMTP | 报文 |
| 6 | 表示层 | 应用层 | SSL/TLS、JPEG、ASCII | 报文 |
| 5 | 会话层 | 应用层 | RPC、NetBIOS | 报文 |
| 4 | 传输层 | 传输层 | TCP、UDP | 段 (TCP)/ 数据报 (UDP) |
| 3 | 网络层 | 网际层 | IP、ICMP、ARP | 分组 / 包 |
| 2 | 数据链路层 | 网络接口层 | Ethernet、PPP | 帧 |
| 1 | 物理层 | 网络接口层 | 无协议,硬件标准 | 比特 |
面试必答关键点 🔑
- 本质区别:OSI 是理论模型,TCP/IP 是实际实现,互联网基于 TCP/IP 运行
- 分层思想:核心是解耦,每层只关心自己的职责,便于开发和维护
- 数据传输过程:发送方从上到下逐层封装,接收方从下到上逐层解封装
- 易错点:TCP 和 UDP 工作在传输层,IP 工作在网际层,MAC 地址工作在数据链路层
Java 开发中的实际应用 🚀
- 写 HTTP 接口时,我们工作在应用层
- 使用 Socket 编程时,我们直接操作传输层的 TCP/UDP
- 配置服务器防火墙时,需要了解网络层的 IP 和端口
- 排查网络问题时,会用到 ping (ICMP)、traceroute 等工具
💡 为什么 Java 开发者必须懂这个?
| 场景 | 对应层级 | 典型现象 |
|---|---|---|
| 调用第三方接口超时 | 应用层 | HTTP 状态码 504、连接池耗尽 |
| 线上频繁 Full GC 导致连接断开 | 传输层 | TCP 四次挥手异常,出现大量 TIME_WAIT |
| 服务器 ping 通但 telnet 端口不通 | 网络层/传输层 | IP 可达,但目标端口未监听 |
| 物理机网卡灯不亮 | 物理层 | 直接换网线 😂 |
你如果用 Netty 写高并发网关,就不得不处理 TCP 粘包/拆包(传输层),自定义协议时要定义魔数、序列化方式(表示层/应用层)。这些都是分层思想在代码里的映射。
🎁 一句话总结(面试话术)
“OSI 七层是学术标准,把表示层、会话层单独抽象出来,便于教学;TCP/IP 四层是工业标准,把上三层合并成应用层,下两层合并成网络接口层,更贴合实际协议栈。我做 Java 开发时,HTTP 和 TCP 层接触最多,理解分层模型能让我快速定位是代码逻辑问题、网络策略问题还是基础设施问题。”
TCP 三次握手与四次挥手
面试官您好,我来系统回答一下这个问题。TCP 是面向连接、可靠、全双工的传输层协议,三次握手和四次挥手就是 TCP 为了实现 "可靠建立连接" 和 "优雅关闭连接" 而设计的核心通信机制 🔑
这个问题我结合状态机和实际网络编程来聊,理解会更深。
TCP为了保证可靠传输,在收发数据之前必须建立连接、结束后必须释放连接。
建立连接就是“三次握手”,释放连接就是“四次挥手”。
TCP 三次握手:建立可靠连接
1. 核心目的
双方互相确认对方的发送能力和接收能力都正常,并协商初始序列号 ISN(Initial Sequence Number)
2. 完整流程
3. 关键细节
- 第 1 次握手:客户端发 SYN,服务器知道客户端能发
- 第 2 次握手:服务器发 SYN+ACK,客户端知道服务器能收能发
- 第 3 次握手:客户端发 ACK,服务器知道客户端能收
- 💡 初始序列号 ISN 不是从 0 开始,而是随机生成的,防止序列号预测攻击
4. 灵魂拷问:为什么是 3 次?不是 2 次或 4 次?
✅ 为什么不是 2 次?
核心是为了防止已失效的连接请求突然又传到服务器。
如果只有 2 次,服务器发完 SYN+ACK 后就认为连接建立了,但客户端可能根本没收到这个报文。服务器会一直等客户端发数据,浪费服务器资源。更严重的是,过期的 SYN 报文会导致服务器建立无效连接。
✅ 为什么不是 4 次?
因为服务器的 SYN(同步序列号)和 ACK(确认客户端的 SYN)可以合并在同一个报文里发送,所以 3 次就够了,4 次是多余的。
TCP 四次挥手:优雅关闭连接
1. 核心目的
因为 TCP 是全双工的,双方都可以独立发送和接收数据,所以关闭连接时需要双方各自关闭自己的发送通道(接收通道仍然可以工作,直到对方也关闭)
2. 完整流程
关键细节
- 第 1 次挥手:主动方说 "我发完了,要关闭我的发送通道了"
- 第 2 次挥手:被动方说 "我收到了,等我发完剩下的数据再关"
- 第 3 次挥手:被动方说 "我也发完了,要关闭我的发送通道了"
- 第 4 次挥手:主动方说 "我收到了,你可以关了"
4. 灵魂拷问:为什么是 4 次?不是 3 次?
✅ 因为被动方收到 FIN 时,可能还有未发送完的数据,不能立即回复 FIN。只能先回复 ACK 确认,等自己的数据全部发送完毕后,再发送 FIN 通知主动方自己也准备关闭了。所以这两步不能合并,必须分开,因此是 4 次。
高频追问必答(加分项💯)
1. TIME_WAIT 状态为什么要等 2MSL?
- MSL(Maximum Segment Lifetime):报文最大生存时间,通常是 2 分钟
- 作用 1:保证最后一个 ACK 报文能到达对方。如果被动方没收到第 4 次挥手的 ACK,会重发 FIN,主动方在 2MSL 内还能收到并重发 ACK
- 作用 2:防止旧连接的报文干扰新连接。2MSL 时间足够让本次连接的所有报文都从网络中消失,避免新连接收到旧连接的过期报文
2. SYN 攻击是什么?怎么解决?
SYN 攻击:攻击者伪造大量不存在的 IP 地址,向服务器发送 SYN 报文。服务器回复 SYN+ACK 后,等待客户端的 ACK,但客户端不会回复。服务器会一直重试,直到超时,占用大量服务器资源,导致正常连接无法建立
解决方法:
- 缩短 SYN 超时时间
- 使用 SYN Cookie 技术(不保存半连接状态,而是将信息加密在 ACK 的序列号中)
- 防火墙过滤恶意 IP
☕ 结合 Java 实际开发
在 Java 里,这些状态转换是很重要的调试点 👇:
- 客户端调
Socket.connect()→ 触发三次握手,成功则进入ESTABLISHED。 - 调
Socket.close()→ 触发四次挥手的第一个FIN,进入FIN_WAIT_1。 - 服务端
ServerSocket.accept()返回的Socket关闭时,如果应用没调close(),会卡在CLOSE_WAIT,这是常见的连接泄露 🔴。 - 大量
TIME_WAIT会导致端口资源被占用,我们经常通过SO_REUSEADDR让端口可以快速复用,或者在系统层面调小tcp_tw_reuse等参数。
面试总结
三次握手是“请求 → 确认 → 确认的确认”,防止旧连接重复建立;四次挥手是“主动关 → 被动方应答+被动方关闭”,因为全双工需要两方各发一次 FIN。再结合 Java 的 close() 和 netstat 状态监控,就能把理论和实践串起来了。
面试官,以上就是我对 TCP 三次握手和四次挥手的理解。核心就是:三次握手是为了确认双方的收发能力,四次挥手是因为 TCP 全双工需要各自关闭发送通道。如果您还有更深入的问题,我可以继续解答 😊
TCP 流量控制与拥塞控制
面试官您好,我来回答一下这个问题。首先这两个机制都是 TCP 保证可靠传输的核心,但解决的问题完全不同:流量控制解决的是端到端的速度匹配问题,防止接收方被淹没;拥塞控制解决的是全局网络的拥堵问题,防止中间链路被压垮。
流量控制:滑动窗口协议 ✅
1. 核心原理
接收方在 ACK 报文中携带自己的接收窗口大小 rwnd,发送方的发送窗口上限 = min (拥塞窗口 cwnd, 接收窗口 rwnd),保证发送的数据量永远不会超过接收方的缓冲区容量。
TCP 报文头部有个 窗口字段(Window),接收方通过它告诉发送方:“我的缓冲区还剩多少空间,你悠着点发。”
+-------------------+ TCP 报文(ACK) +-------------------+
| 发送方 | ◄─────── 窗口大小=4096 ──────── | 接收方 |
| 发送窗口 = min(cwnd, rwnd) | | 缓冲区已用 6144 |
| 实际可用 = rwnd | | 剩余 rwnd = 4096 |
+-------------------+ +-------------------+2. 滑动窗口工作示意图
3. 关键细节(面试必考点)
- 零窗口:当接收方缓冲区满时,会发送 rwnd=0 的 ACK,发送方立即停止发送
- 持续计时器:防止零窗口后接收方的新 ACK 丢失,发送方每隔 500ms 发送 1 字节的探测报文,获取最新的 rwnd
- rwnd 动态变化:接收方应用读出数据,窗口就变大;来不及读,窗口就变小甚至为 0。
- 糊涂窗口综合征(Silly Window Syndrome):接收方每次只腾出 1 字节就通告窗口,发送方就发 1 字节,效率极低。解法:接收方用 Clark 方案(窗口增大到 MSS 或一半缓冲区才通告),发送方用 Nagle 算法(小数据攒到 MSS 或收到 ACK 再发)。
📝 状态示意图(发送方视角):
拥塞控制:四大核心算法 🚀
网络中间设备(路由器)队列溢出导致丢包,TCP 必须感知并降速。核心就是维护 拥塞窗口 cwnd,并根据丢包信号调整。
1. 核心概念
- 拥塞窗口 cwnd:发送方维护的状态变量,根据网络拥塞程度动态调整
- 慢启动阈值 ssthresh:cwnd 增长方式的分界点,决定是指数增长还是线性增长
2. 四大算法完整执行流程
3. 各算法核心要点
| 算法 | 触发条件 | 执行逻辑 | 核心目的 |
|---|---|---|---|
| 慢开始 | 连接建立 / 超时重传后 | cwnd 初始 = 1,每收到 1 个 ACK,cwnd 翻倍 | 快速探测网络可用带宽 |
| 拥塞避免 | cwnd 达到 ssthresh | 每经过 1 个 RTT,cwnd+1(线性增长) | 缓慢增加,避免拥塞 |
| 快重传 | 连续收到 3 个重复 ACK | 不等超时计时器到期,立即重传丢失报文 | 减少重传等待时间 |
| 快恢复 | 快重传之后 | ssthresh=cwnd/2,cwnd=ssthresh,直接进入拥塞避免 | 避免慢开始导致的传输骤降 |
整体状态机如下,这样看非常清晰:
💡 为什么快恢复不回到慢启动?
收到 3 个 dup ACK 说明网络还能通,只是偶发丢包,没必要让 cwnd 直接降到 1,降低吞吐量。超时才是真拥塞。
4. cwnd 变化趋势图
流量控制 vs 拥塞控制 对比表 📊
| 对比维度 | 流量控制 | 拥塞控制 |
|---|---|---|
| 解决问题 | 接收方处理能力不足 | 网络中间链路转发能力不足 |
| 作用范围 | 端到端(仅发送方和接收方) | 全局网络(所有主机 + 路由器) |
| 核心依据 | 接收窗口 rwnd | 拥塞窗口 cwnd |
| 控制手段 | 滑动窗口协议 | 慢开始、拥塞避免、快重传、快恢复 |
| 最终目标 | 保证接收方不被数据淹没 | 保证网络整体稳定,避免全局崩溃 |
| 实现机制 | 滑动窗口 + 窗口通告 | 慢启动/拥塞避免/快重传/快恢复 |
一句话:流量控制是“别把对方撑死”,拥塞控制是“别把路堵死”。 🚦
现代拥塞控制演进 —— 从 Reno 到 BBR 🚀
传统 Reno 丢包驱动,在高带宽长肥网络(中国到美国专线)效率很差,因为丢包才降速,有时丢包到来之前路由器队列已经很长(Bufferbloat),延迟剧烈抖动。
Google 提出 BBR,带宽、延迟双驱动,周期探测最大 BW 和最小 RTT,不再依赖丢包信号,特别适合高 BDP 网络。性能对比:
| 算法 | 敏感信号 | 适用场景 |
|---|---|---|
| Reno | 丢包 | 低延迟、低带宽 |
| CUBIC | 丢包,三次函数调整 | Linux 默认,比 Reno 高速更友好 |
| BBR | 带宽 + RTT | 高延迟、高带宽、轻微丢包环境 |
面试加分项 ⭐
1. 为什么快恢复不直接回到慢开始?
因为收到 3 个重复 ACK 说明网络只是丢了个别包,不是严重拥塞,不需要从头开始探测带宽,能大幅提升传输效率。
2. 实际开发中的优化点
- 关闭 Nagle 算法(TCP_NODELAY):适合小数据包频繁传输的场景(如游戏、即时通讯)
- 调整接收窗口大小:大带宽高延迟网络(如跨洋传输)需要增大 rwnd
- 启用 BBR 拥塞控制算法:比传统的 CUBIC 算法更适合现代高带宽网络
最后我画个图总结发送方实际如何选取窗口:
实际发送窗口 = min(rwnd, cwnd),两个约束同时起作用,确保既尊重接收方能力,也尊重网络容量。
TCP 与 UDP 区别及适用场景
面试官您好,我从核心定位、关键区别、适用场景三个维度来回答这个问题👇
基础概念铺垫 📚
TCP(传输控制协议)和 UDP(用户数据报协议)都是OSI 模型传输层的核心协议,负责端到端的数据传输:
- TCP:面向连接的、可靠的字节流服务
- UDP:无连接的、不可靠的数据报服务
核心区别对比表 ✅
| 对比维度 | TCP | UDP |
|---|---|---|
| 连接性🔗 | 面向连接(三次握手建立,四次挥手断开)❶ | 无连接(无需建立 / 释放连接) |
| 可靠性✅ | 绝对可靠(确认重传、序列号、校验和、流量控制、拥塞控制) | 尽最大努力交付,不保证可靠 |
| 拥塞控制📶 | 有(慢启动、拥塞避免、快重传、快恢复) | 无(网络拥塞不影响发送速率) |
| 传输方式⚖️ | 面向字节流(无边界,会拆包 / 粘包) | 面向数据报(有边界,不会拆包 / 粘包) |
| 首部开销 | 20~60 字节(可变选项) | 固定 8 字节 |
| 传输速度🧱 | 较慢(连接建立 + 可靠性机制开销) | 极快(无额外开销)🚀 |
| 实时性📏 | 差(延迟高,重传会导致卡顿) | 极好(低延迟,丢包不影响后续数据) |
| 流量控制 | 有(滑动窗口机制) | 无 |
| 支持特性 | 全双工 | 全双工 |
| 典型特征 | 准确、完整、可靠 | 快、轻量、实时 |
直观对比图 📊
📡 图文理解:连接与交付模型
TCP 就像先握手再说话,还要对方逐句确认
UDP 则像发快递,贴上面单直接扔,不管对方收没收到 📦
典型适用场景 🎯
🔹 TCP 适用场景(必须可靠的场景)
- 文件传输:FTP、SFTP、HTTP/1.1、HTTP/2 📁
- 邮件传输:SMTP、POP3、IMAP 📧
- 远程登录:SSH、Telnet 💻
- 金融交易:支付、订单系统 💰
🔹 UDP 适用场景(追求低延迟的场景)
- 实时音视频:直播、视频会议、语音通话 🎥
- 网络游戏:MOBA、FPS 等实时对战游戏 🎮
- 域名解析:DNS 查询 🔍
- 广播 / 多播:DHCP、NTP(网络时间协议)📡
- 物联网:传感器数据上报 🤖
🎯 适用场景脑图
场景选择的口诀:要准用 TCP,要快用 UDP。
如果允许少量丢包但极度在意延迟(如直播、游戏),UDP 就是首选;如果数据一个字节都不能错(如支付、文件),那必须 TCP。
☕ Java 研发中的体现
我们写 Java 后台,实际会这样碰到它们:
TCP 王者:
Socket/ServerSocket、Netty 的NioSocketChannel。- RPC 框架(Dubbo、gRPC)、HTTP 调用、数据库连接池,底层全是 TCP,因为有超时重传、有序交付的保证。
UDP 轻骑兵:
DatagramSocket/DatagramPacket、Netty 的NioDatagramChannel。- 服务发现(组播心跳)、监控埋点上报、日志收集(部分允许丢失的场景)会用到 UDP,因为它开销极小,不建立连接,能够支撑高吞吐。
举个例子:订单支付的接口调用,必须走 TCP 的 HTTP/2,保证请求不丢;而微服务健康检查的组播心跳,用 UDP 既轻量又满足实时性。
🧠 生活化比喻,一秒记住
TCP ≈ 打电话 📞
你得先拨号接通,对方回应“喂”,你才说;每句话都希望对方听清,听不清会重说,还讲究顺序。
UDP ≈ 对讲机/发朋友圈 💬
直接喊话,不在意对方是否听到,可能顺序乱,也可能丢失,但胜在无拘无束、延迟极低。
面试加分小 Tips ⭐
如果能更进一步,提到 TCP 的拥塞控制有时反而会拖累实时音视频,因此催生了基于 UDP 的 QUIC(HTTP/3),实现 0-RTT 握手、多路复用无队头阻塞,那就非常亮眼了 👍。
面试官必追问 1:TCP 如何保证可靠性?
答:通过序列号 + 确认应答保证有序和不重复,超时重传处理丢包,校验和检测数据错误,滑动窗口实现流量控制,拥塞控制避免网络过载。
面试官必追问 2:UDP 能实现可靠传输吗?
答:可以,在应用层实现可靠性机制即可。例如 HTTP/3 底层的 QUIC 协议、实时音视频的 RTP 协议,都是基于 UDP 封装了确认重传、序列号、拥塞控制等能力,兼顾了 UDP 的低延迟和 TCP 的可靠性。
HTTP 1.0/1.1/2.0/3.0 演进与 QUIC 协议
面试官您好,关于 HTTP 各版本演进和 QUIC 协议,我从核心痛点→关键改进→遗留问题的逻辑线来梳理,保证直击考点:
这个问题我顺着一条主线来讲:从 “能用” 到 “好用” 到 “极致性能”,整个过程本质上是在解决延迟和连接效率的问题。
我会分四段:1.0 打地基、1.1 修修补补、2.0 重构传输、3.0 颠覆底层。最后点出 QUIC 的精髓。
演进总览流程图
四大版本核心特性对比表
| 特性维度 | HTTP 1.0 | HTTP 1.1 | HTTP 2.0 | HTTP 3.0 |
|---|---|---|---|---|
| 发布时间 | 1996 年 | 1999 年 | 2015 年 | 2022 年 (RFC 9114) |
| 底层传输 | TCP | TCP | TCP | UDP(QUIC) |
| 连接模型 | 短连接 (1 请求 1 连接) | 默认长连接 + 管道化 | 单 TCP 连接多路复用 | 单 QUIC 连接多独立流 |
| 队头阻塞 | ✅ 无 (但连接开销极大) | ❌ 应用层队头阻塞 | ❌ TCP 层队头阻塞 | ✅ 彻底解决所有队头阻塞 |
| 头部处理 | 无压缩 | 无压缩 | HPACK 头部压缩 | QPACK 头部压缩 |
| 协议格式 | 文本协议 | 文本协议 | 二进制协议 | 二进制协议 |
| 内置安全性 | 无 (需单独 HTTPS) | 无 (需单独 HTTPS) | 推荐 HTTPS | ✅ 强制内置 TLS 1.3 |
| 核心新增能力 | 基本请求响应 | Host 头 / 缓存控制 / 断点续传 | 服务器推送 / 流优先级 | 连接迁移 / 0-RTT 握手 |
分版本核心考点解析
1. HTTP 1.0 ✨
关键词:短连接、无状态、队头阻塞萌芽 🐢
- 核心改进:确立了 HTTP"请求 - 响应" 的基本模型,支持 GET/POST/HEAD 方法
- 致命痛点:
- 每个请求都要新建 TCP 连接(三次握手 + 四次挥手),开销巨大
- 无 Host 头,不支持虚拟主机(一个 IP 只能跑一个网站)
- 缓存机制极其简陋(只有 Expires)
最要命的是,TCP 连接开销让 延迟(RTT) 被放大到无法接受。
2. HTTP 1.1 📈(目前仍占主流)
关键词:Keep-Alive、管线化、队头阻塞依旧 🚶
- 核心改进:
- ✅ 默认长连接(
Connection: keep-alive),一个连接可发多个请求 - ✅ 新增 Host 头,支持虚拟主机(一个 IP 跑多个网站)
- ✅ 完善缓存体系(
Cache-Control/ETag/Last-Modified) - ✅ 支持断点续传(
Range头)和分块传输(Transfer-Encoding: chunked)
- ✅ 默认长连接(
- 遗留问题:
- ❌ 管道化有缺陷:请求必须串行响应,前一个慢会堵后面所有(应用层队头阻塞)
- ❌ 头部冗余严重:每次请求都带重复的 Cookie、User-Agent 等
- ❌ 不支持服务器主动推送
痛点就出来了:TCP 连接数爆炸,延迟依旧高。
3. HTTP 2.0 ⚡(大厂主流)
关键词:二进制分帧、多路复用、头部压缩、服务器推送 🚗
HTTP/2 没有改 TCP,但把 HTTP 语义和传输彻底分层:
- 核心改进(基于 Google SPDY):
- ✅ 二进制分帧:将请求拆成帧传输,解析更高效
- ✅ 多路复用:单 TCP 连接可并行传输多个流,互不干扰(解决应用层队头阻塞)
- ✅ HPACK 头部压缩:静态字典 + 哈夫曼编码,减少 70%+ 头部体积
- ✅ 服务器推送:主动给客户端发资源(如 HTML 里的 CSS/JS)
- ✅ 流优先级:重要请求(如页面主内容)优先传输
- 致命遗留问题:
- ❌ 底层还是 TCP,TCP 层队头阻塞依然存在:一个数据包丢失,整个连接的所有流都要等重传
- ❌ TCP+TLS 握手延迟高:至少 2-RTT 才能建立连接
❌ 但躺枪的是 TCP 层: HTTP/2 把所有流量压到一个 TCP 连接上。底层 TCP 是字节流协议,丢一个包,整个 TCP 连接的所有流都必须等这个包重传——这就是 TCP 层的队头阻塞。在弱网下,HTTP/2 性能可能还不如 1.1 的多连接。
4. HTTP 3.0 🔝(未来趋势)
关键词:QUIC、0-RTT、无队头阻塞、连接迁移 🚀
- 革命性变化:抛弃 TCP,基于QUIC 协议传输
- 核心优势:
- ✅ 彻底解决队头阻塞:流之间完全独立,一个流丢包不影响其他流
- ✅ 0-RTT/1-RTT 握手:首次连接 1-RTT,重连 0-RTT(比 TCP+TLS 快 3 倍)
- ✅ 连接迁移:切换网络(WiFi→4G)不用重建连接,体验丝滑
- ✅ 内置 TLS 1.3:安全性更高,握手更快
- ✅ 可定制的拥塞控制:比 TCP 更灵活,适合不同网络环境
0-RTT连接建立
🧩 QUIC 精髓用一张图对比(协议栈示意)
QUIC 协议核心考点 🔒
QUIC 是 Google 开发的基于 UDP 的可靠传输协议,本质是 "在 UDP 上实现了 TCP+TLS+HTTP/2 的所有优点,并解决了它们的缺点"
- 核心设计亮点:
- 流级别的拥塞控制:每个流有独立的拥塞窗口,互不影响
- 连接 ID:用 64 位 ID 标识连接,IP / 端口变了只要 ID 不变,连接就不断
- 前向纠错 FEC:丢包时可通过冗余包恢复,不用等重传
- 0-RTT 数据传输:重连时可直接带应用数据,延迟极低
- 注意:0-RTT 存在重放攻击风险,只能用于幂等请求(GET/HEAD)
面试高频追问与加分点 💯
1.必问:HTTP 1.1 和 HTTP 2.0 的队头阻塞有什么区别?
- 1.1:应用层队头阻塞,请求必须串行响应
- 2.0:TCP 层队头阻塞,所有流共享一个 TCP 连接,一个丢包全堵
2.加分:为什么 HTTP 3.0 不用 TCP 而用 UDP?
- TCP 的队头阻塞是协议本身的设计缺陷,无法在应用层解决
- TCP 的握手延迟高,且不支持连接迁移
- UDP 是无连接的,可灵活定制传输逻辑
3.加分:HTTP 3.0 现在的应用情况?
- 谷歌、Facebook、字节跳动等大厂已大规模部署
- Chrome、Firefox、Safari 等主流浏览器均已支持
- CDN 厂商基本都提供 HTTP 3.0 加速服务
总结 📝
HTTP 的演进主线非常清晰:不断降低延迟,提高吞吐量,优化移动网络体验
- 1.0→1.1:解决连接复用问题
- 1.1→2.0:解决应用层队头阻塞和头部冗余问题
- 2.0→3.0:解决 TCP 本身的缺陷,彻底释放传输性能
HTTPS 加密原理:对称加密 + 非对称加密 + CA 证书
面试官您好,HTTPS 本质上是在 HTTP 和 TCP 之间加了一层TLS/SSL 协议,核心就是用「对称加密 + 非对称加密 + CA 证书」的组合拳,完美解决了 HTTP 明文传输的三大致命问题:窃听、篡改、冒充 🔐
🧩 先记住一个比喻:寄快递
想象你要给远方的朋友寄一台手机(数据),但你担心两件事:
- 快递员偷看里面的情书 💌(隐私性)
- 朋友收到的是被人调包的模型机 📦(完整性 / 身份验证)
HTTPS 就是解决这两个问题的完美快递方案,它用了三样法宝:
1️⃣ 对称加密 —— 那个又快又好的快递箱
对称加密就像你和朋友提前约定好,用一个只有你俩知道的密码锁箱子。你用钥匙锁上,朋友用同一把钥匙打开。速度快,效率高,适合搬运大量货物(数据)。
💡 问题来了:钥匙怎么给朋友?
如果直接邮寄钥匙,快递员拿到就能开箱了,等于白锁。🤦
这就是对称加密的死穴:密钥传输不安全。
于是,我们需要第二样法宝。
2️⃣ 非对称加密 —— 那把不用传递的“公钥锁”
非对称加密生来就是解决密钥传递问题。它有两把钥匙:
- 公钥:像一把打开的锁🔓,可以满大街发,谁拿到都能锁箱子。
- 私钥:像唯一能开这把锁的钥匙🔑,服务器死死攥在手里。
具体流程:
- 服务器把“公钥锁”光明正大扔给你(不怕被偷看)。
- 你随机生成一把临时对称密钥,用这把“公钥锁”锁好,发给服务器。
- 只有服务器能用私钥打开,拿到里面的对称密钥。
这样,对称密钥就安全到手了,后续你们就用它加密聊天,又快又安全。🔐
灵魂画手上线,看这张图就懂了:
3️⃣ 中间人攻击 —— 快递站李鬼出现了
上面流程看似完美,但有个惊天漏洞。😈
假设有个坏蛋中间人截胡了你的网络请求:
- 他冒充服务器把自己的“假公钥”给你。
- 你用假公钥锁住对称密钥,坏蛋用他私钥解开,偷看/篡改完,再用真服务器公钥锁上转发。
- 你和服务器完全蒙在鼓里,还以为在直接对话。
这时候,你需要一个绝对可信的权威机构来证明:这个公钥确实是真实服务器的,不是中间人的。
4️⃣ CA 证书 —— 那枚不可伪造的防伪公章 📜
CA(数字证书认证中心) 就是互联网世界的公安局。它的核心操作是:
- 服务器把自己的公钥 + 身份信息(域名、组织等)拿去 CA 盖个章。
- 这个章就是 CA 用自己的私钥对上述信息做的数字签名。
- 结果打包成一个叫 CA 证书(数字证书) 的东西。
浏览器/操作系统里预装了一堆受信任的 CA 机构的根公钥。当你拿到服务器的证书时:
- 用内置的 CA 公钥去验证证书的数字签名,发现没问题 👉 说明证书确实是 CA 签发的,没被篡改。
- 核对证书上的域名和你访问的域名一致 👉 说明没给别家站顶包。
- 信任链条确立!从证书里掏出服务器的真实公钥,安全使用。
最终完整流程图来了,一目了然:
为什么不能只用一种加密?(核心痛点)
1. 只用对称加密:密钥分发是死穴
对称加密就像你和朋友共用一把钥匙🔑,加密解密都用同一把,速度极快🚀,适合加密大量业务数据。
但最大的问题是:密钥怎么安全分发?
如果直接在网络上明文发密钥,中间人截获后就能解密所有数据,等于没加密 ⚠️
2. 只用非对称加密:性能扛不住
非对称加密有一对「公钥 + 私钥」:公钥可以全网公开,私钥只有自己持有。用公钥加密的数据只有私钥能解,反之亦然。
但它的致命缺点是:速度极慢🐢(比对称加密慢几百上千倍),根本没法用来加密整个 HTTP 请求的海量数据。
三者结合的核心思路(黄金组合)
✅ 非对称加密:只用来传输对称密钥(解决密钥分发安全问题)
✅ 对称加密:用来传输所有业务数据(解决性能问题)
✅ CA 证书:解决「公钥被中间人篡改」的终极问题(防止中间人攻击)
对称加密 vs 非对称加密 核心对比
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 加密速度 | 极快 🚀 | 极慢 🐢 |
| 密钥数量 | 双方共用 1 把 | 一对(公钥 + 私钥) |
| 核心风险 | 密钥明文传输被截获 | 公钥被中间人替换 |
| 适用场景 | 加密大量业务数据 | 加密对称密钥、数字签名 |
| 典型算法 | AES、ChaCha20 | RSA、ECC |
完整 TLS 1.2 握手流程(图解)
这是面试必问的核心流程,我用时序图给您梳理一下:
CA 证书的核心作用(防中间人攻击)
CA 证书就像服务器的官方身份证🪪,由全球信任的证书颁发机构签发。证书里包含:
- 服务器的域名
- 服务器的公钥
- CA 机构的数字签名
- 证书有效期等信息
验证逻辑:客户端操作系统 / 浏览器内置了所有权威 CA 的根证书公钥。拿到服务器证书后,会用根证书公钥验证证书上的 CA 签名。如果验证通过,就说明这个公钥确实是目标服务器的,没有被中间人篡改。
面试加分项 ✨
- 现在主流大厂都已经全面切换到TLS 1.3,它把原来的 2 次 RTT(往返时间)压缩到了 1 次 RTT,甚至支持 0-RTT 恢复,性能提升非常明显
- ECC 椭圆曲线加密算法比 RSA 更安全、密钥更短、速度更快,现在已经成为主流
- 证书链验证:服务器通常返回的是证书链,客户端需要逐级验证直到根证书
也可以直接这样讲:
“HTTPS 用的是混合加密体系。首先,通过非对称加密安全传递一个临时的对称密钥,后续数据交互全用这个对称密钥加密,兼顾安全和性能。
但为了防止中间人伪造公钥,服务器不能裸发公钥,而是发一个由权威 CA 机构私钥签名的数字证书。客户端用内置的 CA 根公钥验签,确认证书合法后取出服务器公钥,再执行密钥协商。整个过程中,CA 证书起到了身份认证和防篡改的作用。”
WebSocket 原理与应用
面试官您好,我从背景痛点→核心原理→技术对比→Java 实现→应用场景→生产踩坑6 个维度来回答这个问题,尽量讲透关键点:
🧠 一句话理解
WebSocket 是 “让客户端和服务端可以互相主动发消息的全双工协议”,它基于 TCP,但借用了 HTTP 完成初次握手,之后升级为独立协议,解决了 HTTP 一问一答的“半双工 + 高延迟”痛点。
为什么需要 WebSocket?🔌
传统 HTTP 是半双工、请求 - 响应模式,只能客户端主动发请求,服务端无法主动推送数据。
- 早期实现 "实时" 只能用轮询 / 长轮询:频繁建立关闭 TCP 连接,开销极大,延迟高
- Comet 技术本质还是 HTTP hack,存在连接数限制、代理服务器兼容问题
- WebSocket 就是为了解决全双工实时通信而生的标准协议(RFC 6455)
核心原理 🤝
2.1 握手流程(最核心考点)
WebSocket 复用 HTTP 的 80/443 端口,通过一次 HTTP 握手升级为 WebSocket 协议
这是 WebSocket 最巧妙的地方——用 HTTP 的 Upgrade 机制完成协议切换,避免额外端口,也方便穿越防火墙和代理。
客户端发起一个普通的 GET 请求,但携带了两个特殊的 Upgrade 头:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== ← 随机 Base64 值
Sec-WebSocket-Version: 13服务端收到后,如果支持 WebSocket,会返回 101 Switching Protocols:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= ← 根据客户端 Key 算出来的值这里 Sec-WebSocket-Accept 是通过 SHA-1 对“客户端 Key + 固定魔数 258EAFA5-E914-47DA-95CA-C5AB0DC85B11”计算得来,目的是:
- 防止客户端误连接;
- 保证这是一个真正的 WebSocket 握手,而非缓存或意外重放。
🖼️ 用序列图看握手过程非常直观:
至此,一条底层仍是 TCP 连接的 WebSocket 通道就建立了,后续通信不再有 HTTP 头部的负担。
关键细节:
Sec-WebSocket-Accept= Base64(SHA1(Sec-WebSocket-Key+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))- 握手成功后,TCP 连接保持,后续数据以WebSocket 帧格式传输,不再有 HTTP 头开销
2.2 数据帧格式 📦
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+握手之后,消息以 帧 (frame) 为单位传输。一个帧的结构我简化一下:
| 字段 | 大小 | 说明 |
|---|---|---|
| FIN | 1 bit | 是否最后一帧 |
| RSV1-3 | 3 bit | 保留位,扩展用 |
| opcode | 4 bit | 帧类型:文本/二进制/关闭/ping/pong |
| MASK | 1 bit | 客户端发帧必须掩码(安全考虑) |
| payload len | 7/7+16/7+64 bit | 变长长度描述 |
| Masking-key | 0 or 4 bytes | 客户端发时必带 |
| Payload Data | len | 实际业务数据 |
相比 HTTP 每次请求都带大量 header,WebSocket 帧头最小仅 2 字节(掩码时 6 字节),极大减少即时通讯场景下的带宽浪费。😎
必记字段:
FIN:1 表示最后一帧,0 表示还有后续帧Opcode:帧类型(0x01 文本帧、0x02 二进制帧、0x08 关闭帧、0x09 心跳帧、0x0A 心跳响应)MASK:客户端发往服务端的帧必须掩码,服务端发往客户端的帧不能掩码
2.3 核心特性
- ✅ 全双工通信:客户端和服务端可同时收发数据
- ✅ 低开销:握手后每个帧只有 2-14 字节的头部
- ✅ 无同源限制:但服务端可通过Origin头做安全校验
- ✅ 支持二进制和文本数据传输
💓 心跳保活:Ping/Pong 帧
长连接最怕“假死”。WebSocket 协议内置了 Ping/Pong 控制帧 (opcode=0x9/0xA):
- 服务端可以发一个 Ping 帧;
- 客户端收到后必须尽快回一个 Pong 帧,实际数据可以原样返回。
这样:
- 双方都能感知对方是否存活;
- 可以顺便做 空闲保活,避免 NAT 路由器或代理因超时切断连接。
在面试中,面试官常常会追问“WebSocket 断了怎么重连”,这里我顺带提一下:通常会在 onclose 事件中实现指数退避重连策略,比如先隔 1s,再 2s、4s……上限封顶,保障服务恢复后能快速恢复通信。🔁
WebSocket vs HTTP 核心对比 ⚖️
| 对比维度 | HTTP/1.1 | WebSocket |
|---|---|---|
| 连接方式 | 短连接 / 长连接(Keep-Alive) | 持久长连接 |
| 通信模式 | 半双工(请求 - 响应) | 全双工 |
| 头部开销 | 每次请求几十到几百字节 | 最小 2 字节 |
| 推送能力 | 无(只能轮询模拟) | 原生支持服务端主动推送 |
| 端口 | 80/443 | 80/443(复用 HTTP 端口) |
| 适用场景 | 静态资源、接口请求 | 实时通信、数据推送 |
一句话:需要 高频、双向、低延迟 的场景,果断选 WebSocket。
Java 生态主流实现 🛠️
1.Tomcat/Jetty 原生实现:
Servlet 3.1+ 内置支持,适合简单场景
// 核心注解
@ServerEndpoint("/ws/{userId}")
public class ChatEndpoint {
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {}
@OnMessage
public void onMessage(String message, Session session) {}
@OnClose
public void onClose(Session session) {}
@OnError
public void onError(Session session, Throwable error) {}
}2. Spring Boot WebSocket:
最常用,集成简单,支持 STOMP 子协议
- 依赖:
spring-boot-starter-websocket - 核心:
@EnableWebSocketMessageBroker+@MessageMapping+@SendTo
3. Netty WebSocket:
高性能首选,支持百万级连接,适合互联网大厂高并发场景
- 核心处理器:
WebSocketServerProtocolHandler - 优势:零拷贝、Reactor 模型、自定义帧处理
典型应用场景 🎯
- 即时通讯:在线聊天、客服系统、弹幕
- 实时数据:股票行情、体育直播比分、监控大屏
- 协同工具:在线文档、白板、代码协作
- 游戏:多人在线游戏、实时对战
- IoT:设备状态上报、远程控制
- 实时推送 🔔:股票行情、体育比分、订单状态变更。
- 物联网 📡:设备状态实时上传和服务端指令下发。
这些场景里,如果还让客户端不断地“问”,延迟和服务器压力都是灾难级的。
生产环境常见坑与优化 ⚠️
- 心跳保活:Nginx、防火墙会自动断开空闲连接,必须实现心跳机制(客户端发 ping 帧,服务端回 pong 帧)
- 断线重连:客户端必须实现指数退避重连逻辑,避免服务器雪崩
- 连接数限制:单机最大连接数受文件句柄限制,需调整 Linux 内核参数(ulimit -n)
- 粘包拆包:Netty 中使用WebSocketFrameDecoder自动处理,原生实现需手动处理
- 安全问题:使用 wss 加密传输,校验 Origin 头,实现鉴权(JWT 放在握手请求头)
- 消息积压:服务端需实现消息队列,避免客户端断开时消息丢失
后简单提一下工程落地时的要点:
- 使用 WSS (WebSocket Secure),即基于 TLS 加密,类似 HTTPS。
- 校验
Origin头防止跨站 WebSocket 劫持。 - 鉴权可以在握手时通过 cookie 或 URL 参数携带 token,服务端在握手阶段校验,不合规直接拒绝升级。
- 做好 心跳、重连、消息序列化(如 Protobuf/JSON)、消息ACK确认等周边机制。
总结 📝
WebSocket 是实时通信的标准解决方案,核心是通过 HTTP 握手升级为全双工 TCP 连接。在 Java 项目中,简单场景用 Spring Boot 集成,高并发场景用 Netty 实现。生产环境一定要重点关注心跳保活、断线重连和安全鉴权这三个问题。
