计网八股速记:TCP握手挥手、拥塞控制与HTTP/HTTPS主线

发布时间:2026/10/27 22:01:57
计网八股速记:TCP握手挥手、拥塞控制与HTTP/HTTPS主线 面试季一到后台问计网八股的人明显多了起来。很多人不是不懂而是讲得出名词、串不成线三次握手能背一问为什么不能两次就卡住拥塞控制能说出慢启动一问 ssthresh 怎么变就开始含糊。这篇是计网八股速记的第二篇接着上一篇的链路往下走把 TCP 连接管理、可靠传输、拥塞控制、HTTP 协议族、HTTPS 与 DNS 这条主线重新捋一遍重点放在速记线索和追问应答上而不是把教科书目录抄一遍。适合正在准备面试的应届生、工作两三年想补基础的同学也适合平时写业务代码但没系统看过协议栈的人。全篇用的都是我自己整理的口诀和串讲方式照着记、照着讲比死背结论稳得多。1. 先把速记这件事想明白1.1 计网八股为什么越背越乱我见过太多人背计网的方式是按章节平推今天背 OSI 七层明天背 TCP 三次握手后天背 HTTP 状态码背完一圈发现脑子里是一堆互不相干的抽屉面试官随便换个问法就露馅。问题的根子在于计算机网络本身是一条数据从应用层一路封装下去、再一路解封装上来的流水线你把流水线上的每一站拆开单独背当然串不起来。举个最常见的例子。面试官问浏览器输入 URL 到页面展示发生了什么这题几乎能把整本书考一遍。如果你是按章节背的可能只会答DNS 解析、TCP 连接、发 HTTP 请求、渲染页面这四句话再往下问就没了。但如果你脑子里有流水线的概念就会自然想到DNS 本身要走 UDP 还是 TCP、解析过程是递归还是迭代、拿到 IP 之后 ARP 怎么找 MAC、TCP 握手要几轮、TLS 握手多几轮、HTTP 请求带哪些头、响应回来是强缓存还是协商缓存、连接是复用还是新建。每一环都能继续往下钻三层。所以我做速记的第一条原则是不按章节记按一次请求的生命周期记。所有知识点挂在这条时间线上挂不上去的说明你还没理解它为什么存在。第二条原则是每个结论必须配一个为什么。八股之所以叫八股是因为很多人只背了结论。但面试官的追问几乎全部落在为什么上为什么三次不是两次、为什么 TIME_WAIT 要等 2MSL、为什么 HTTP/2 还有队头阻塞、为什么 HTTPS 要非对称加密加对称加密两套。结论是死的理由是活的记住理由结论自己就能推出来。1.2 我用的三层记忆法状态机、时序、关键词具体到操作层面我整理知识点用的是一套三层结构这里直接摊开讲你可以套用到任何协议上。第一层是状态机。凡是涉及连接、握手、关闭的协议本质都是状态机。TCP 有 CLOSED、LISTEN、SYN_SENT、SYN_RCVD、ESTABLISHED、FIN_WAIT_1、FIN_WAIT_2、CLOSE_WAIT、LAST_ACK、TIME_WAIT、CLOSING 十一个状态。你不用背全但必须知道服务端和客户端各自在什么状态下等什么报文。状态机的好处是只要你知道我现在在哪个状态、我在等谁,报文的方向和序号就都能推出来,不需要死背。第二层是时序。把报文按时间轴画出来,标上 seq、ack、flag,比看文字描述清楚十倍。三次握手就是三行,四次挥手就是四行,画一遍胜过读十遍。我在纸上画过不下一百遍,画到闭着眼能默写,这一步没有捷径。第三层是关键词。给每个知识点配一个能钩住整段内容的关键词或者口诀。比如 TIME_WAIT 我记的是晚走两分钟,替人收快递——晚走是因为要等 2MSL 让旧报文消散,替人收快递是因为最后一个 ACK 丢了对方会重发 FIN,我得还在原地接住。一个口诀钩住两个理由,考场上想起这句就全出来了。这三层配合起来的效果是:状态机保证你不漏步骤,时序保证你说得清顺序,关键词保证你在紧张的时候能瞬间调取。三者缺一不可。1.3 这一篇覆盖的边界先说清楚这篇的范围,免得你按图索骥发现少东西。上一篇主要铺的是分层模型、物理层到传输层的基础概念、IP 与子网划分这些地基内容。这一篇从 TCP 的连接管理开始,一路讲到 HTTP/3 和 HTTPS 握手,再带上 DNS、ARP 这条解析链路,最后给一张高频追问的速查表。没覆盖的主要是:路由协议细节(OSPF、BGP 那些)、网络安全攻防的完整体系、以及 HTTP 之外的各类应用层协议(SMTP、FTP 之类)。这些要么面试问得浅,要么属于另一个专题,硬塞进来会把主线冲散。还有个提醒:这篇里的口诀和类比是我自己顺手的说法,不是标准术语。你完全可以改成自己更顺口的版本,记忆这东西,自己编的永远比抄来的牢。2. TCP 连接管理:把握手挥手拆成状态机2.1 三次握手每个报文的真实作用先把原始过程摆出来,这是所有追问的起点。第一次握手:客户端发SYN1, seqx。此时客户端进入 SYN_SENT,意思是我想连你,我的初始序号是 x。第二次握手:服务端回SYN1, ACK1, seqy, ackx1。服务端进入 SYN_RCVD,意思是我收到了你的 x,我这边初始序号是 y,我同意连。第三次握手:客户端发ACK1, seqx1, acky1。双方进入 ESTABLISHED。速记口诀:我要连、你可以、我开始。三个动作对应三个报文,一句话就串完了。但真正值钱的是每个报文在干什么。第一个报文的核心作用不只是请求连接,它还携带了客户端的初始序号 ISN。这个序号不是从 0 开始的,也不是随便取的,现代实现一般用时钟加四元组哈希算出来,目的是降低历史报文串进来的概率。第二个报文同时做了两件事:确认对方的序号(ackx1),并且告知自己的初始序号(seqy)。这就是所谓SYN 和 ACK 合并发送。第三个报文确认服务端的序号。所以三次握手的本质是:双方各自发送自己的初始序号,并各自确认对方的初始序号。四件事,两来两往,最少需要三次报文才能完成。这个视角比建立连接四个字有用得多,后面所有为什么不能两次的追问都从这里推。还有一个容易被忽略的点:第三次握手的 ACK 报文是可以携带数据的。也就是说客户端在发出 ACK 的同时就可以把 HTTP 请求的第一个字节捎上,这在某些实现里能省掉一次往返。但服务端不行,服务端在 SYN_RCVD 状态下不会把数据交给应用层,必须等到 ESTABLISHED。这个细节被问到过一次,当时答不上来,后来专门查了才记住。2.2 为什么不能两次握手这是 TCP 章节出现频率最高的追问,没有之一。标准答案是防止已失效的连接请求报文突然又传到服务端,但光说这句不够,得把场景讲出来。设想一个场景:客户端发出第一个 SYN,因为网络拥堵这个报文在路上卡了很久,客户端超时重传,重传的 SYN 顺利到达,连接建立、通信、关闭,一切正常。过了一段时间,那个卡住的老 SYN 终于晃悠到了服务端。如果只有两次握手,服务端收到这个老 SYN 就会直接认为连接建立,分配资源、进入 ESTABLISHED,然后傻等客户端发数据——可客户端根本没想连,它那边是 CLOSED 状态。服务端就这么被一个历史报文白嫖了一份连接资源,多了就是资源耗尽。三次握手能解决这个问题,是因为第三个 ACK 必须由客户端主动发出。客户端收到那个来路不明的 SYNACK 时,会发现 ack 号对不上自己当前的任何一次连接尝试,于是直接回一个 RST 拒绝掉。服务端收不到第三个 ACK,就不会真正建立连接,过一段时间自己回收资源。除了这个经典理由,还有两个补充角度值得一起说,能让回答显得更完整。一是同步双方初始序号:两次握手只能保证客户端确认了服务端的序号,服务端没法确认客户端的序号,后续的可靠传输基础就不牢。二是确认双方的收发能力:三次报文刚好能验证客户端能发能收、服务端能发能收这四件事,两次只能验证三件。面试时我一般先讲历史报文场景,再补序号同步,两句话说完,基本不会被继续追。2.3 四次挥手与 TIME_WAIT 的两个理由挥手比握手多一次,原因很直白:TCP 是全双工的,两个方向要各自关闭。A 方向 B 说我没数据要发了(FIN),但 B 可能还有数据没发完,所以 B 先回一个 ACK 表示知道了,等自己数据发完了再发一个 FIN,A 再回 ACK。中间那个等数据发完的间隔,就是多出来的那一次。过程是这样的:A 发FIN1, sequ,进入 FIN_WAIT_1。B 回ACK1, acku1,进入 CLOSE_WAIT;A 收到后进入 FIN_WAIT_2。B 数据发完,发FIN1, ACK1, seqw, acku1,进入 LAST_ACK。A 回ACK1, ackw1,进入 TIME_WAIT,等 2MSL 后进入 CLOSED。B 收到后进入 CLOSED。口诀:我要关、你收到、我关完、你确认。注意第二步和第三步之间是可以隔很久的,这就是半关闭状态——A 不再发数据了,但还能收 B 的数据。HTTP 里客户端发完请求就半关闭等待响应,就是这个机制在支撑。现在说 TIME_WAIT。主动关闭方在发完最后一个 ACK 之后,不能立刻进入 CLOSED,而要等 2MSL(MSL 是报文最大生存时间,常见实现取 30 秒到 2 分钟不等,所以 2MSL 就是 1 到 4 分钟)。两个理由:理由一:保证最后一个 ACK 能到达对方。如果这个 ACK 丢了,B 会超时重发 FIN。此时 A 如果已经 CLOSED,收到一个不认识的 FIN 只能回 RST,B 就会认为连接异常终止。A 留在 TIME_WAIT 就能重新发一次 ACK。等 2MSL 是因为一个报文来回最长就是一个 MSL,加上对方重发的时间,2MSL 足够覆盖所有重传。理由二:让本次连接产生的所有报文在网络中消散。如果不等待,立刻用相同的四元组建立新连接,旧连接里迷路的报文可能被新连接误收,造成数据错乱。等 2MSL,旧报文要么到达要么被丢弃,网络里干净了。速记就是那句晚走两分钟,替人收快递。前半句对应理由二,后半句对应理由一。顺带说一个工程上常见的问题:高并发短连接的服务器上会堆积大量 TIME_WAIT,导致本地端口耗尽。这时候很多人第一反应是调net.ipv4.tcp_tw_reuse,但要注意这个参数只对主动发起连接的一方有效,而且是复用,不是消除。真正治本的办法是改用长连接、连接池,或者让客户端主动关闭,把 TIME_WAIT 转移出去。这个思路在面试里讲出来,比单纯背参数名有分量得多。2.4 半连接队列与 SYN 洪泛的基本机制这一节讲的是握手过程中的工程细节,属于加分项。服务端在 LISTEN 状态下维护两个队列:半连接队列(SYN queue)和全连接队列(accept queue)。收到 SYN 之后,服务端把连接放进半连接队列,回 SYNACK,等对方发来第三个 ACK,再把它移到全连接队列,等应用层调用 accept 取走。这两个队列都有容量上限,全连接队列的大小由 listen 的 backlog 参数和系统参数共同决定。如果队列满了,新来的连接会被丢弃或者回 RST,表现就是客户端连不上、超时。线上排查服务端明明没挂但就是连不上,先看这两个队列的溢出计数,这是基本功。关于 SYN 洪泛,这里只讲机制不讲攻防:攻击方大量伪造源地址发 SYN,服务端每个都回 SYNACK 并占一个半连接队列位置,但第三个 ACK 永远不来,队列被占满,正常用户连不进来。应对思路是 SYN cookie——服务端不回 SYNACK 时不立刻分配资源,而是把连接信息编码进 seq 号里,等第三个 ACK 回来再校验,校验通过才分配。这样半连接队列就不会被撑爆。这个知识点面试问得不深,但如果你能说清两个队列 backlog 的实际含义,比只会说三次握手要高一档。3. 可靠传输与流量控制:序号、确认、窗口3.1 序号与确认号的计算规则可靠传输的地基是序号。TCP 把应用层交下来的字节流切成段,给每个字节编号,注意是字节不是段。seq 表示我这个段第一个字节的编号,ack 表示我期望收到你的下一个字节编号,也就是你 seq 到 ack-1 为止的字节我都收到了。举个具体的:客户端发seq1000, len200,那么这段数据覆盖字节 1000 到 1199。服务端回ack1200,表示 1200 之前的全收到了。下一次客户端发就从seq1200开始。这里有个高频陷阱:SYN 和 FIN 各占一个序号。所以三次握手里,客户端发seqx,服务端回ackx1,那个 1 就是 SYN 占的。同理 FIN 也占一个,四次挥手里acku1也是这么来的。很多人算 ack 时忘了加 1,一问就错。还有一点:初始序号 ISN 是随机的。原因和前面说的一样,防止历史连接的数据被误认。RFC 里建议用一个基于时钟的算法,每 4 微秒加一,这样序号在 32 位空间里绕一圈需要很长时间,基本不会重复。速记:seq 是我发的第一个字节,ack 是我想要的下一个字节,握手挥手的 1 是控制位占的坑。3.2 滑动窗口与累计确认光有序号还不够,如果每发一个段就停下来等 ACK,效率低得没法看。所以 TCP 引入了窗口。接收方在 ACK 里带一个窗口字段,告诉发送方我这边还能收多少字节。发送方维护一个发送窗口,窗口内的数据可以连续发出去不用等确认,窗口左边是已发送已确认,中间是已发送未确认,右边是还不能发的。窗口会随着 ACK 回来而向右滑动,这就是滑动窗口。它同时干了两件事:一是流量控制,防止发送方发太快把接收方缓冲区撑爆;二是提高吞吐,让发送方能在等确认的同时继续发数据。TCP 用的是累计确认:ack1200 的意思是 1200 之前的所有字节都收到了,不区分是哪几个段。好处是简单、抗丢包(丢一个中间段,后面的 ACK 会重复,但最终一个 ACK 能确认一大片);坏处是丢一个段会导致后面所有数据都要重传。窗口大小为 0 的时候要特别处理。接收方缓冲区满了会发零窗口通告,发送方就停止发送,但要定期发窗口探测报文,防止窗口更新通知丢失导致双方死锁。这个细节线上真会碰到,某次压测时消费端处理慢,连接就卡在零窗口上,查了半天才定位到。3.3 快速重传、超时重传与 SACK丢包之后怎么补,有三种机制。超时重传最朴素:发出去的数据启动一个定时器,超时没收到 ACK 就重发。超时时间 RTO 是根据 RTT 动态算的,不是固定值。但超时重传的问题是等待时间长,而且一超时就会触发拥塞控制的剧烈反应(这个下一节讲)。快速重传是对超时的优化。如果发送方连续收到三个针对同一个序号的重复 ACK,就认为那个段丢了,不等超时立刻重传。为什么是三个?因为一个两个可能是乱序到达造成的,三个基本可以确定是丢了。这个三次重复 ACK是必背数字。SACK(选择性确认)是更进一步的优化。在 TCP 选项里携带我已经收到了哪些区间的信息,发送方就能精准地只重传真正丢的那一段,而不是把丢包之后的所有数据都重发。没有 SACK 的时候,一个段丢了可能导致后面几十个段全部白传。这三个机制的关系是递进的:超时重传是兜底,快速重传是常态优化,SACK 是精准打击。面试被问到TCP 怎么保证可靠,按这个顺序讲,再补上校验和、序号、确认,一条完整的链就出来了。顺便说一句,TCP 的校验和只能发现错误,不能纠正,发现错了就丢弃等重传。而且校验和是 16 位的,理论上存在极小概率的漏检,所以应用层该有的校验还是要有。3.4 Nagle 算法与延迟确认的经典坑这一节讲一个真实会咬人的问题,属于面试不一定问但工作一定遇得到的类型。Nagle 算法的思路是:如果还有未确认的小数据,就先攒着,等凑够一个 MSS 或者等到 ACK 回来再发。目的是减少网络上的小包数量。延迟确认的思路是:收到数据不立刻回 ACK,等一小会儿(常见 40ms,最长 200ms),看能不能捎带在反方向的数据上一起发。单独看,两个都是优化。合在一起就是灾难:发送方攒着小包等 ACK,接收方压着 ACK 等数据,双方互相等,一次交互硬生生多出几十毫秒。某些请求-响应模式的应用跑在局域网上还好,跨机房就是肉眼可见的卡顿。解决办法是:对延迟敏感的场景关掉 Nagle(TCP_NODELAY),或者让应用层自己攒够一批再发。这个坑我在做实时通信的时候踩过一次,单次请求莫名多 40ms,查了两天才定位到是这两个机制叠加。这种经验比背十条协议细节都值钱,面试里讲出来也很有说服力。提示:排查延迟问题时,先确认是不是小包加延迟确认导致的,再去看应用层逻辑,顺序反了会浪费大量时间。4. 拥塞控制:从慢启动到 BBR 的思路转变4.1 四个阶段的参数变化流量控制管的是接收方能不能收得下,拥塞控制管的是网络能不能扛得住。前者靠接收窗口 rwnd,后者靠拥塞窗口 cwnd,实际发送窗口取两者最小值。拥塞控制有四个阶段,核心变量就两个:cwnd 和 ssthresh(慢启动阈值)。速记口诀:慢启动翻倍涨,到阈值改线性;丢包腰斩,超时归零。慢启动:cwnd 从 1 个 MSS 开始,每收到一个 ACK 就加 1 个 MSS,效果是每个 RTT 翻一倍,指数增长。叫慢启动不是因为它慢,而是因为起点低,增长其实很快。涨到 ssthresh 就停。拥塞避免:cwnd 超过 ssthresh 后,改成每个 RTT 只加 1 个 MSS,线性增长。这个阶段是保守试探,看网络还能不能扛。快重传 快恢复:收到三个重复 ACK 时,说明网络还有响应能力(数据还在流动,只是某段丢了),这时候不用把 cwnd 打回 1。做法是 ssthresh 设为当前 cwnd 的一半,cwnd 设为 ssthresh(有的实现是 ssthresh3),然后直接进入拥塞避免。这样避免了从零开始爬坡。超时重传:如果连重复 ACK 都没有,直接超时了,说明网络状况很差。这时 ssthresh 设为 cwnd 的一半,cwnd 直接归 1,重新慢启动。这是最重的惩罚。把四个阶段的 cwnd 变化画成曲线,就是一条指数上升、拐点转线性、丢包处下降、再爬升的锯齿线。脑子里有这条线,任何追问都能顺着答。4.2 快恢复里那个减半到底减的是什么这里有个很多人答不清楚的点:cwnd 减半,减的依据是什么?答案是乘性减、加性增,也就是 AIMD。线性增长(加性增)是为了慢慢试探带宽上限,一旦发现拥塞就砍一半(乘性减),这样多个连接共享带宽时能收敛到相对公平的状态。如果减得太少,大家都不退让,网络就崩了;如果减得太多,带宽利用率又上不去。减半是理论和实践折中的结果。还有个常被问的细节:快恢复和快重传是什么关系?快重传是检测到丢包的动作(收到三个重复 ACK 就重发),快恢复是检测到丢包之后的窗口调整策略(不打回 1,直接减半继续)。两者是配套的,不是一回事。如果面试官继续追那如果丢的是多个包呢,可以答:传统实现一次只处理一个丢包事件,多个包丢失时会反复触发,效率低。现代实现有改进算法专门处理这种情况,思路是区分拥塞丢包和随机丢包,避免把无线网络的误码当成拥塞来惩罚。4.3 吞吐量估算:一个能算给人看的例子这部分是我觉得最实用的加分项,因为大部分人只会背阶段名字,不会算。经验公式:吞吐量 ≈ cwnd / RTT。假设 cwnd 是 64 个 MSS,MSS 取 1460 字节,RTT 是 40ms。那么每个 RTT 能发 64 × 1460 ≈ 93KB,一秒有 25 个 RTT,吞吐约 2.3MB/s,换算成带宽大概 18Mbps。这个公式能解释很多现象:为什么跨洋链路明明带宽很大,单条 TCP 连接却传不快?因为 RTT 大,cwnd 又受丢包限制涨不上去,吞吐被卡死。这就是所谓的长肥管道问题。解决办法要么增大窗口(需要窗口缩放选项支持),要么用多连接,要么换拥塞控制算法。这个计算在面试里讲出来非常加分,因为它证明你不是背书,而是真的理解变量之间的关系。我第一次被问到为什么我的服务器带宽跑不满时就是靠这个公式定位到是 RTT 加窗口的问题。4.4 BBR 的思路:不把丢包当唯一信号传统拥塞控制(包括前面讲的几个阶段)有个隐含假设:丢包就等于拥塞。这个假设在有线网络基本成立,但在无线网络、长距离链路上经常误判——包丢了可能只是信号不好,网络其实很空闲,结果算法把窗口砍半,带宽白白浪费。BBR 换了个思路:不去猜丢包,而是主动测量。它持续估计两个值:瓶颈带宽(观测到的最大交付速率)和最小 RTT(观测到的最小往返时延),然后把发送速率控制在带宽 × RTT附近,同时周期性探测带宽有没有增长。这种模型化的方法在高丢包、大 RTT 的链路上优势明显,能把带宽吃得更满,排队时延也更低。代价是它对公平性的处理和传统算法不一样,混布环境下可能出现抢占。所以实际部署时要不要开,得看业务场景,不能一刀切。面试里被问到了解哪些拥塞控制算法,答完传统四阶段之后提一句 BBR 的思路,点到为止即可,不用展开公式。知道从丢包驱动转向模型驱动这个转变,就已经比大多数人强了。5. HTTP 协议族:版本演进与缓存机制5.1 四个版本的核心差异一次讲透速记口诀:1.0 短、1.1 长、2.0 多路、3.0 换轨。HTTP/1.0默认短连接,每个请求都要新建 TCP 连接,请求完就关。性能极差,但协议简单。可以通过Connection: keep-alive手动开启复用,但不是默认行为。HTTP/1.1把持久连接变成默认,一个 TCP 连接可以串行发多个请求。同时引入了几个关键东西:Host 头(支持一台服务器托管多个域名)、分块传输编码(响应可以边生成边发,不用等长度确定)、管道化(理论上可以连发多个请求不用等响应)。但管道化在实践中基本没人用,因为响应必须按请求顺序返回,前一个慢了后面全堵着,这就是著名的队头阻塞。HTTP/2的核心是二进制分帧加多路复用。把请求和响应拆成帧,每个帧带流 ID,同一个连接上多个流可以交错传输,彻底解决了 HTTP 层的队头阻塞。头部用 HPACK 压缩,减少了大量重复头部的开销。还支持服务器推送(实际用得少)和流优先级。但注意,HTTP/2 解决的是 HTTP 层的队头阻塞,TCP 层的队头阻塞还在——一个 TCP 包丢了,整条连接上所有流都得等重传。HTTP/3干脆把传输层换成基于 UDP 的 QUIC。每个流独立,一个流丢包不影响其他流,彻底解决了 TCP 层的队头阻塞。同时把 TLS 握手合并进去,建连更快,还支持连接迁移(换网络不断连)。代价是 UDP 在某些网络环境下会被限速或拦截,部署时需要考虑这点。有一张对比表我建议直接记下来:版本传输层连接复用队头阻塞头部压缩1.0TCP默认关闭有无1.1TCP默认开启HTTP 层有无2.0TCP单连接多流TCP 层仍有HPACK3.0UDP(QUIC)单连接多流基本消除QPACK5.2 强缓存与协商缓存:两组头的配合缓存这块,很多人能背Cache-Control,但说不清强缓存和协商缓存怎么配合。其实逻辑很简单:先在本地找,能用就直接用(强缓存);不能用就去问服务器(协商缓存);服务器说没变就继续用本地的,变了就传新的。强缓存由Cache-Control控制,常用值有max-age秒数(相对时间)、no-cache(可以缓存但每次必须验证)、no-store(完全不许缓存)。老版本还有Expires指定绝对时间,现在基本被max-age取代,因为前者依赖客户端时钟。协商缓存靠两组头:一组是ETag和If-None-Match,另一组是Last-Modified和If-Modified-Since。流程是:服务端第一次响应带上 ETag(内容指纹),浏览器下次请求带上 If-None-Match,服务端比对,没变就返回 304 不带响应体,浏览器继续用本地副本。两组头选哪个?优先级上 ETag 更高,因为它精确到内容,不像 Last-Modified 只精确到秒,而且文件内容没变但修改时间变了的情况会误判。但 ETag 的计算要消耗服务端资源,大文件频繁变更时可能有压力。实践中一般两个都带,让客户端优先用 ETag。有个坑值得单独说:no-cache和no-store名字看着像,实际完全不同。no-cache是可以存但用了之前必须验证,本质是走协商缓存;no-store才是完全不许存。我见过有项目为了不缓存直接写no-store,结果所有静态资源每次都全量下载,白白浪费带宽。要禁强缓存但保留协商缓存,正确的是no-cache。5.3 方法、状态码与常见追问方法这块,GET 和 POST 的区别被问烂了,但很多人答的还是一个在 URL 里一个在 body 里这种表面区别。真正值得说的是:GET 是幂等的、可缓存的、可被浏览器历史记录的,POST 都不是。所以刷新一个 POST 页面会弹是否重新提交,刷新 GET 页面不会。幂等的意思是执行一次和执行多次结果相同,这是设计重试机制的基础。状态码按段记:2xx 成功(200 正常、204 无内容、206 断点续传)、3xx 重定向(301 永久、302 临时、304 未修改、307 和 308 保留方法)、4xx 客户端错(400 参数错、401 未认证、403 无权限、404 找不到、429 限流)、5xx 服务端错(500 内部错、502 网关收到无效响应、503 服务不可用、504 网关超时)。302 和 307 的区别值得单独提:302 在历史上很多浏览器会把 POST 改成 GET,307 明确要求保持原方法不变。做接口重定向时如果涉及 POST,用 307 更安全。502 和 504 的区别也是高频题:502 是上游服务返回了无法解析的响应(通常是上游挂了或者协议不对),504 是上游服务超时没响应。线上排查时看这两个码能快速定位是上游死了还是上游慢。6. HTTPS 与解析链路:证书、握手、DNS、ARP6.1 TLS 握手的核心:两套加密各管一段HTTPS 等于 HTTP 加 TLS。它要解决三个问题:机密性(内容不被看到)、完整性(内容不被篡改)、身份认证(对面真的是它)。做法是非对称加密协商密钥,对称加密传输数据。为什么不用一套?因为非对称加密运算慢,适合做密钥交换这种一次性的事;对称加密快,适合传大量数据。两套配合,各取所长。TLS 1.2 的完整握手大致是:客户端发 ClientHello(支持的版本、加密套件、随机数 A),服务端回 ServerHello(选定套件、随机数 B)和证书,客户端验证证书、生成预主密钥用服务端公钥加密发过去,双方用 A、B、预主密钥算出会话密钥,然后互发 Finished 验证。这个过程需要两个 RTT。TLS 1.3 把流程压缩到一个 RTT,主要手段是把密钥交换提前,客户端在 ClientHello 里就带上密钥共享,服务端可以直接算密钥。还砍掉了一堆不安全的加密套件,只保留前向安全的。另外支持 0-RTT 恢复(用之前会话的票据直接发数据),但 0-RTT 有重放风险,不能用于非幂等请求。速记:1.2 两个来回,1.3 一个来回,0-RTT 有重放风险只适合读。6.2 证书校验到底校了什么这部分是很多人含糊的地方。客户端拿到证书后,会做几件事:第一,验证证书链。证书是分层的,服务端证书由中间 CA 签发,中间 CA 由根 CA 签发,根 CA 的证书内置在操作系统或浏览器里。客户端顺着链往上验,每一级都用上一级的公钥验签名。第二,验证有效期。过期证书直接拒。第三,验证域名。证书里的 SAN 字段要包含你访问的域名,不然就是证书与域名不匹配。第四,检查吊销状态。通过 CRL 或 OCSP 查这个证书有没有被提前吊销。全部通过才继续握手。这里有个容易被问的点:自签证书为什么浏览器报警?不是因为它不加密,加密照样加密,而是因为无法验证身份——中间人可以拿一张自己签的证书冒充,你没法分辨。这就是为什么必须依赖受信任的根证书体系。6.3 DNS 解析:递归与迭代的分工DNS 解析这块,最容易被问混的是递归查询和迭代查询。递归查询是你帮我查到底。客户端问本地 DNS 服务器,本地服务器负责把最终结果拿回来,客户端只等一个答案。客户端到本地 DNS 之间通常是递归。迭代查询是你告诉我下一步问谁。本地 DNS 服务器去问根服务器,根说我不知道具体 IP,但 .com 的服务器地址是 X,你去问它;本地服务器再问 .com 服务器,一路问下去,直到拿到结果。本地 DNS 到各级权威服务器之间是迭代。速记:客户端偷懒用递归,服务器跑腿用迭代。常见记录类型:A 是 IPv4 地址,AAAA 是 IPv6,CNAME 是别名,MX 是邮件服务器,NS 是域名服务器,TXT 是文本(常用于各种验证)。TTL 控制缓存时长,设太短会增加解析压力,设太长变更生效慢,是运维权衡的经典问题。还有个细节:DNS 主要走 UDP 53 端口,因为查询应答通常很短,一次往返就够。但如果响应超过限制(比如 512 字节或者协商后的更大值),会切换到 TCP。区域传送这种大批量传输则强制用 TCP。6.4 ARP:IP 到 MAC 的最后一跳数据要发出去,最终得知道下一跳的 MAC 地址,ARP 就是干这个的。过程很简单:主机在本地广播谁是 192.168.1.1,把你的 MAC 告诉我,目标主机单播回应我是,MAC 是 XX。其他主机看到不是问自己就忽略。结果存进 ARP 缓存,带过期时间。被问到的点通常是:ARP 是网络层还是链路层?严格说它工作在两者之间,报文封装在以太网帧里,但解决的是网络层地址到链路层地址的映射。还有 ARP 欺骗的机制(伪造 ARP 响应把流量引到自己这里),这部分只讲原理不展开。到这里,一条完整的链路就串起来了:输域名 → DNS 解析出 IP → ARP 找到网关 MAC → 建立 TCP → TLS 握手 → 发 HTTP 请求 → 缓存判断 → 响应回来 → 渲染。这条线能从头讲到尾,计网八股基本就没有死角了。7. 高频追问速查与排查实录7.1 一张表把追问都接住下面这张表是我自己整理的,面试前扫一遍,基本能覆盖 TCP 和 HTTP 的追问。追问一句话答案为什么三次不是两次防止历史重复 SYN 建立无效连接,同时双向同步初始序号为什么四次不是三次全双工,一个方向 FIN 后另一方向可能还有数据要发TIME_WAIT 为什么 2MSL保证最后 ACK 到达 让旧报文消散大量 CLOSE_WAIT 说明什么应用层没调 close,代码漏了,查自己的程序大量 TIME_WAIT 说明什么主动关闭方连接太多,考虑长连接或连接池快速重传触发条件连续收到三个重复 ACK拥塞控制四个阶段慢启动、拥塞避免、快重传、快恢复流量控制和拥塞控制区别前者看接收方窗口,后者看网络承载能力HTTP/2 解决了什么HTTP 层队头阻塞,多路复用、头部压缩HTTP/3 解决了什么TCP 层队头阻塞,改用 QUIC强缓存和协商缓存前者不发请求,后者发请求但可能返回 304no-cache 和 no-store前者可存但必验,后者完全不许存502 和 504上游响应无效 vs 上游超时7.2 实际排查用什么命令光会背不够,得知道怎么在机器上验证。下面这几个命令我几乎每周都会敲。# 看连接状态分布,快速判断 TIME_WAIT / CLOSE_WAIT 是否异常 ss -tan | awk {print $1} | sort | uniq -c | sort -rn # 看两个队列的溢出情况,判断是不是 backlog 小了 ss -lnt # 抓包看握手挥手细节,-i 指定网卡,host 过滤目标 tcpdump -i eth0 -nn tcp port 443 -w handshake.pcap # 看 TCP 重传和拥塞窗口,需要内核支持 ss -ti第一个命令是排查连接类问题的第一站。CLOSE_WAIT多,百分之九十是代码问题,连接用完没关;TIME_WAIT多,看是不是短连接太频繁,要么改长连接,要么调整内核参数但要谨慎。第二个命令看Recv-Q和Send-Q,LISTEN 状态下这两个值分别对应全连接队列的当前长度和上限。当前值接近上限,说明 accept 处理不过来或者 backlog 设小了。第三个命令适合定位疑难问题,比如握手超时到底是哪一步丢的包。抓下来看三次握手有没有完整、谁先发的 RST,比在日志里猜快得多。第四个命令能看每条连接的cwnd、rtt、重传次数,压测时用它判断瓶颈在网络还是在应用。注意:ss -ti的输出在不同内核版本上格式有差异,字段含义要对着内核文档确认,别硬记。7.3 我踩过的三个坑第一个坑是把 Nagle 和延迟确认的锅算到业务代码头上。前面提过,单次请求莫名多几十毫秒,查了两天。后来用 tcpdump 抓包才看到是两边互相等。教训是:遇到固定量级的延迟,先怀疑协议层,别急着改代码。第二个坑是只调大 backlog 但还是连不上。全连接队列上限实际受三个地方约束:listen 的 backlog 参数、net.core.somaxconn、以及net.ipv4.tcp_max_syn_backlog(这个是半连接队列)。只改一个没用,得一起看。而且 backlog 改太大之后,突然的大量连接会让 accept 处理不过来,反而拉高尾延迟,需要配合限流。第三个坑是HTTP/2 部署后反而变慢。原因是服务端和客户端之间的多路复用跑在一条 TCP 上,一旦丢包,所有流一起等重传。在高丢包链路上,单连接多流反而不如多条连接。后来针对这种情况做了调整,把部分流量切回 HTTP/1.1 的多连接模式才恢复。这件事让我明白一个道理:协议版本不是越新越好,得看链路特征。7.4 复习节奏上的个人做法最后说点方法层面的。我准备这类面试从来不做一次背完的打算,而是按三遍法走。第一遍只求串起来,把一次请求的生命周期画成一张图,所有知识点往图上挂,挂不上的先标个记号。这一遍花两三天,不求细节。第二遍专攻标记出来的地方,每个都去查原始文档或者抓包验证。这一遍最慢,但收获最大,因为是你自己搞懂的,不是别人喂的。第三遍是模拟输出,合上资料,对着空气把每个知识点讲一遍,讲不顺畅的就说明没真懂。我一般会挑五六个高频题,限时两分钟讲完,练表达。这三遍下来,知识点就从看过变成了讲过,面试时不会因为紧张而断片。速记的价值也就在这里——它不替代理解,只是帮你在有理解的基础上,把东西快速调出来。如果这篇里的某个口诀你用着别扭,直接改,换成你熟悉的场景和词。记忆这件事,自己加工过的才留得住。下一篇准备把 HTTPS 的证书链和 QUIC 的细节单独展开讲一层,那条线往深了挖还有不少东西。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询