当前位置: 首页 > news >正文

网络编程协议面试经典

1. 引言

在技术面试中,网络基础与缓存算法是两大必问方向。本文将围绕三个高频考点展开:TCP 三次握手与四次挥手从输入 URL 到页面展示的完整过程、以及LRU 缓存淘汰策略的实现与应用。这三者看似独立,实则共同构成了现代 Web 应用在数据传输、资源加载和性能优化层面的核心知识体系。

2. TCP 三次握手

三次握手是 TCP 协议建立可靠连接的过程,发生在客户端与服务器之间。其核心目的是同步双方的序列号、确认双方的收发能力正常。

2.1 三次握手过程

  • 第一次握手——SYN:客户端发送一个 SYN(同步)报文给服务端,其中包含一个随机生成的初始序列号 seq=x,客户端进入SYN_SENT状态。
  • 第二次握手——SYN+ACK:服务端收到 SYN 报文后,确认客户端的发送能力和自己的接收能力均正常。服务端发送一个 SYN+ACK 报文,ack=x+1 表示确认已收到客户端的数据,seq=y 为服务端的初始序列号。此时服务端进入SYN_RCVD状态。
  • 第三次握手——ACK:客户端收到 SYN+ACK 后发送 ACK 报文,ack=y+1,seq=x+1。此时客户端进入ESTABLISHED状态。服务端收到 ACK 后也进入ESTABLISHED状态,至此双向连接建立。

2.2 关键问题

  • 为什么不是两次握手?两次握手只能确认客户端的发送能力和服务端的接收能力,无法确认服务端的发送能力和客户端的接收能力。如果只有两次握手,服务端在收到 SYN 后就会认为连接已建立,无法保证客户端是否收到了自己的回复,也无法防止旧的重复连接请求导致的资源浪费。
  • 为什么不是四次握手?第二次握手时服务端可以将 SYN 和 ACK 合并到一起发送,效率更高。分开发送会增加一次不必要的往返,没有实际意义。
  • SYN 泛洪攻击:攻击者伪造大量不存在 IP 的 SYN 报文,服务端在SYN_RCVD状态分配大量资源后得不到回复,最终耗尽半连接队列。防御方式包括 SYN Cookie、减少超时重传次数、增加半连接队列容量等。

3. TCP 四次挥手

四次挥手是 TCP 连接断开的过程,由于 TCP 的连接是全双工的,每个方向都需要单独关闭,因此需要四次报文交互。

3.1 四次挥手过程

  • 第一次挥手——FIN:主动关闭方发送 FIN 报文(finish),seq=u,表示本端数据已发送完毕,主动方进入FIN_WAIT_1状态。
  • 第二次挥手——ACK:被动关闭方收到 FIN 后回复 ACK,ack=u+1,seq=v。被动方进入CLOSE_WAIT状态,主动方收到 ACK 后进入FIN_WAIT_2状态。此时半关闭状态:主动方不能再发送数据,但仍可接收被动方发来的数据。
  • 第三次挥手——FIN:被动关闭方数据发送完毕后,也发送 FIN 报文,seq=w,ack 仍为 u+1。被动方进入LAST_ACK状态。
  • 第四次挥手——ACK:主动方收到 FIN 后回复 ACK,ack=w+1,seq=u+1,并进入TIME_WAIT状态。等待 2MSL(最大报文生存时间的两倍)后进入CLOSED状态。被动方收到 ACK 后立即进入CLOSED状态。

3.2 关键问题

  • 为什么要四次挥手?TCP 是全双工的,当一方完成数据发送时可以发送 FIN 告知对方,但对方可能还有未发送完的数据,因此需要先确认收到 FIN,等自身数据也发送完毕后再发送 FIN。这两个阶段分开执行,共需四次报文。
  • TIME_WAIT 为什么要等待 2MSL?一是确保最后一个 ACK 能被被动方接收到,如果 ACK 丢失,被动方会重发 FIN,主动方可以重新回复 ACK;二是让本次连接的所有报文从网络中消失,防止旧连接的报文被新连接误收。2MSL 保证了两个方向上的报文都失效。
  • 大量 TIME_WAIT 状态如何解决?常见手段包括:开启tcp_tw_reuse复用 TIME_WAIT 连接、调整tcp_tw_recycle、缩短tcp_fin_timeout、使用长连接或连接池减少频繁建连断连。

3.3 三次挥手与四次挥手的对比

维度三次握手四次挥手
阶段连接建立连接释放
报文数量3 个4 个
触发方客户端主动发起任意一方均可发起
可否合并ACK 与 SYN 合并ACK 与 FIN 分开发送
特殊状态SYN_SENT、SYN_RCVDTIME_WAIT、CLOSE_WAIT

4. 从输入 URL 到页面展示的完整过程

这是一道经典的综合性面试题,考察候选人对网络协议、浏览器原理、操作系统和前端渲染的全链路理解。整个过程可分为以下几个阶段:

4.1 URL 解析

浏览器首先解析用户输入的 URL,判断是搜索关键词还是合法 URL。如果输入的是非 URL 文本,浏览器会使用默认搜索引擎进行搜索;如果是合法 URL,则拆分出协议(如https)、域名(如www.example.com)、端口(默认 443 或 80)、路径和查询参数等。

4.2 DNS 域名解析

浏览器按以下顺序查找域名的 IP 地址:浏览器 DNS 缓存操作系统 hosts 文件操作系统 DNS 缓存本地 DNS 服务器。如果本地 DNS 服务器未命中,则进行递归查询,从根域名服务器逐级向下解析,最终获取目标 IP 地址。

DNS 解析优化手段包括:DNS 预解析(<link rel="dns-prefetch">)、CDN 就近解析、减少域名数量等。

4.3 建立 TCP 连接

获取到目标服务器的 IP 地址后,浏览器通过三次握手与服务器建立 TCP 连接。如果需要 HTTPS,则先通过 TCP 连接进行 SSL/TLS 四次握手:客户端发送 ClientHello → 服务端回复 ServerHello 和证书 → 客户端验证证书并交换密钥 → 双方确认加密通信开始。

4.4 发送 HTTP 请求

连接建立后,浏览器发送 HTTP 请求报文,包含请求行(方法、路径、协议版本)、请求头(Cookie、User-Agent、Accept 等)和请求体(如果是 POST/PUT 请求)。HTTP/2 和 HTTP/3 对这部分有进一步优化,如多路复用、头部压缩等。

4.5 服务器处理请求并响应

请求经过负载均衡、反向代理到达后端服务器。服务器解析请求,执行业务逻辑(查询数据库、调用微服务等),生成 HTTP 响应报文返回给浏览器。响应报文包含状态行(如200 OK301 Moved Permanently)、响应头(Content-Type、Cache-Control、Set-Cookie 等)和响应体(HTML、JSON 等)。

4.6 浏览器解析与渲染

浏览器收到 HTML 后启动渲染流程:

  1. 构建 DOM 树:解析 HTML 字节流,构建文档对象模型树。
  2. 构建 CSSOM 树:解析 CSS 样式表,构建 CSS 对象模型树。
  3. 执行 JavaScript:遇到<script>标签时暂停 DOM 解析,下载并执行 JS 代码(asyncdefer可改变执行时机)。JS 可能会修改 DOM 和 CSSOM。
  4. 构建渲染树:合并 DOM 树和 CSSOM 树,剔除不可见元素(如display:none),生成渲染树。
  5. 布局:计算每个渲染树节点的几何位置和尺寸。
  6. 绘制:将各节点绘制到屏幕上,按图层分别进行光栅化,最终合成显示。

4.7 四次挥手断开连接

页面数据加载完毕后,如果使用 HTTP/1.0 的短连接,此时会进行四次挥手断连;HTTP/1.1 默认使用长连接(Connection: keep-alive),在一定时间内保持 TCP 连接以复用。

5. LRU 缓存淘汰算法

LRU(Least Recently Used,最近最少使用)是一种经典的缓存淘汰策略:当缓存空间满时,优先淘汰最近最少被访问的数据。其核心思想是认为近期被访问过的数据在未来也更有可能被访问,而长期未被访问的数据在未来被访问的概率较低。

5.1 核心实现:哈希表 + 双向链表

LRU 要求在O(1)时间复杂度内完成getput操作。哈希表负责快速定位节点,双向链表负责维护访问顺序。每次访问或插入时将节点移到链表头部,淘汰时从链表尾部删除。

5.2 Java 实现示例

public class LRUCache { private Map<Integer, Node> map; private Node head, tail; private int capacity; static class Node { int key, value; Node prev, next; Node(int key, int value) { this.key = key; this.value = value; } } public LRUCache(int capacity) { this.capacity = capacity; map = new HashMap<>(); head = new Node(0, 0); tail = new Node(0, 0); head.next = tail; tail.prev = head; } public int get(int key) { Node node = map.get(key); if (node == null) return -1; moveToHead(node); return node.value; } public void put(int key, int value) { Node node = map.get(key); if (node != null) { node.value = value; moveToHead(node); return; } Node newNode = new Node(key, value); map.put(key, newNode); addToHead(newNode); if (map.size() > capacity) { Node removed = removeTail(); map.remove(removed.key); } } private void addToHead(Node node) { node.next = head.next; head.next.prev = node; head.next = node; node.prev = head; } private void removeNode(Node node) { node.prev.next = node.next; node.next.prev = node.prev; } private void moveToHead(Node node) { removeNode(node); addToHead(node); } private Node removeTail() { Node node = tail.prev; removeNode(node); return node; } }

5.3 LRU 的变体与优化

  • LRU-K:记录每个数据最近 K 次访问的时间戳,淘汰最久远的一次。相比基础 LRU,更能抵抗偶发性访问对缓存页面的污染。
  • Two-Queue LRU:维护两个队列,数据先进入FIFO队列,被再次访问时移入LRU队列,淘汰时优先淘汰FIFO队列。
  • Redis 中的近似 LRU:Redis 默认采用近似 LRU 算法,随机采样若干个键,淘汰其中最久未访问的一个,既降低了实现复杂度,也在大多数场景下保持了较好的命中率。
  • LRU 在操作系统中的应用:操作系统使用近似 LRU(如时钟算法)进行页面置换,通过硬件支持的引用位来近似追踪页面访问情况,避免维护完整链表的开销。

5.4 LRU 在浏览器缓存中的应用

浏览器的内存缓存(Memory Cache)在一定程度上遵循 LRU 策略。当浏览器缓存达到上限时,会优先淘汰最近最少使用的资源(如图片、CSS、JS 文件)。结合 HTTP 缓存头(Cache-Control、ETag 等),可以对资源进行更精确的缓存控制。前端开发中可以通过合理设置缓存策略、减少缓存体积,以及利用 Service Worker 进一步实现对缓存淘汰的精细化控制。

6. 总结

TCP 的三次握手和四次挥手奠定了互联网数据传输的可靠性基础,从 URL 到页面的渲染过程则串联起了域名解析、网络传输、浏览器内核三大体系。LRU 缓存策略在操作系统、数据库、Redis 以及浏览器缓存中都有着广泛应用。这三个知识点相互关联,共同构成了后端与前端工程师必备的基础能力。建议在实际面试准备中做到:能画流程图讲清握手挥手过程、能举出 TIME_WAIT 和 CLOSE_WAIT 的真实场景、能手写 LRU 的 O(1) Java 实现、能从 URL 出发串联完整的网络与渲染链路。

http://www.cnnetsun.cn/news/3741521.html

相关文章:

  • stm32进入函数一直弹这个
  • React入门:从声明式UI到组件化开发的核心思维与实践
  • OpenResty为什么选择Lua
  • STM32 ADC与DMA高效数据采集:原理、配置与实战避坑指南
  • Kinect v2与Unity集成:从环境配置到骨骼追踪的完整开发指南
  • 思源宋体CN完全指南:为什么7种字重开源字体是中文排版的最佳选择?
  • RLVR(可验证奖励强化学习)深度解析:从 GRPO 到 DAPO 的大模型推理能力训练新范式
  • 2026论文分阶段工具排行榜|开题/写作/降重/查重/答辩全覆盖✅
  • 软考高项论文写作全攻略:从理论到实战的45分通关秘籍
  • Pandas DataFrame.info() 方法深度解析:从数据诊断到内存优化
  • Kimi K3 API 返回空 content,不一定是中转坏了:先检查 max_tokens
  • 从C到C++:面向对象、内存管理与STL的实战进化指南
  • 深入解析USB Hub驱动:Linux内核中设备热插拔与管理的核心机制
  • 图片视频一键制作GIF动图,简单又好用!
  • 北方苍鹰优化算法改进与MATLAB实现
  • uni-app与uni-app X深度对比:从Web跨端到原生性能的架构演进
  • 基于Carsim与Matlab的轮胎参数实时估计算法实现
  • Simulink仿真单相全桥逆变电路:从SPWM原理到工程调试全解析
  • AutoWareAuto框架:自动驾驶开发的核心技术解析
  • 一份提示词,五重否定:Claude Opus 5 如何用工程语言承认「我不是人」-龍德明宇
  • 办公自动化工具 OpenClaw 搭建教学,2.7.9 版本整合包解压部署全流程(含安装包)
  • Flutter开发鸿蒙手写字体生成器的实践与优化
  • SpringBoot公交调度系统:算法优化与实时数据处理实践
  • 嵌入式UI开发实战:LVGL移植从原理到性能调优全解析
  • UE4视角控制:Pawn、SpringArm与Camera组件深度解析与实战调优
  • 高频注入法:无感电机低速定位的核心原理与工程实践
  • Avatar骨骼映射:让虚拟角色“活“起来的幕后魔法
  • Next.js 在 Web3 中的角色演变:从简单 DApp 前端到全栈链上应用的架构变迁
  • Unity游戏上架Steam全流程指南:从打包到部署的实战避坑
  • Firefox 153.0.1发布:修复多类崩溃与使用问题,部分Windows用户更新仍有隐患