TCP、UDP 与 WebSocket 校招面试题|计算机网络传输层
TCP、UDP 与 WebSocket 校招面试题|计算机网络传输层
本章适合计算机专业学生、应届生和 Java 后端校招求职者,覆盖 TCP 粘包拆包、握手挥手、滑动窗口、拥塞控制,并延伸到 UDP 与 WebSocket。
TCP
1. 请你说一下 TCP 的粘包和拆包?
面试题剖析
本题首先考察是否真正理解 TCP 的字节流语义。要用发送与读取边界不对应的例子解释现象,区分业务消息和 TCP 报文段,不把所谓粘包解释为 TCP 出错或数据乱序。
面试题解答
TCP 提供可靠、有序的字节流,不保留应用每次 write/send 的消息边界。所谓粘包、拆包,是应用按业务消息观察这个字节流时,对读取边界不一致的俗称。

图:字节内容与顺序不变,但发送调用和接收调用的边界可以不同。
- 什么是粘包
发送方依次写入 ABC 和 DE,接收方一次读取可能得到 ABCDE。如果程序误以为一次读取只对应一条业务消息,就会把两条消息当作一条处理。
- 什么是拆包
发送方一次写入 ABCDE,接收方可能先读到 AB,之后再读到 CDE。也可能一次读取既包含上一条的尾部,又包含下一条的开头。这里的“拆包”不等同于 IP 分片,也不保证与线上的 TCP 分段一一对应。
- 为什么会这样
应用写入大小、发送缓冲区、MSS、窗口、Nagle 算法、接收缓冲区和读取时机都会影响观察到的数据分组。发送很小的数据不代表立即生成独立报文,发送很大的数据也不代表一次完整读取。即使禁用 Nagle,也不能恢复业务消息边界。
- 应用应承担什么责任
应用协议定义消息长度或分隔规则,接收方维护解析缓冲区:收到多少先存多少,能够解析完整消息后才交给业务,不完整部分留待后续读取。不要用一次 recv 的返回值、PSH 标志或固定休眠时间判断消息结束。
面试时可以总结为:TCP 只保证字节流的可靠有序交付,不保证发送与读取次数对应。粘包、拆包是应用层缺少正确消息边界处理时暴露的问题,应通过应用协议的分帧规则解决。
可能追问的问题
关闭 Nagle 算法能彻底解决粘包吗?
一次 send 对应一次 recv 吗?
TCP 分段、IP 分片与业务拆包有什么区别?
PSH 能作为业务消息结束标记吗?
2. 为什么会产生粘包和拆包呢,如何解决呢?
面试题剖析
这一题侧重工程解决方案。需要能说明三种常见分帧方式的适用场景,并讲清部分头部、部分正文、一次读入多条消息及异常长度的处理,而不是仅列三个名词。
面试题解答
根本原因是 TCP 不提供业务消息边界。缓冲区与分段策略会改变一次接收获得多少字节,因此发送端和接收端必须共同遵守应用层分帧规则。

图:长度前缀本身也可能分多次到达;必须持续缓冲和解析。
- 定长消息
约定每条消息固定 N 字节,接收方积累到 N 字节就解析一条。简单、定位方便,适合固定格式记录;可变内容需要填充或拆成多条,可能浪费空间。
- 分隔符
通过换行或其他标记识别结束,适合文本行协议。如果正文可能包含同样的分隔符,必须定义转义或编码规则。单独选择一个“罕见字符”并不能保证它永远不会出现在正文中。
- 长度前缀
固定大小的消息头记录正文长度,例如“4 字节无符号大端长度 + 正文”。接收方先积累完整头部、解析并验证长度,再积累对应正文;完整消息交付后继续解析缓冲区内的下一条。
收到字节 → 追加缓冲区
→ 头部不完整:等待后续数据
→ 头部完整:校验长度上限与整数溢出
→ 正文不完整:保留数据继续等待
→ 正文完整:取出一条消息,继续解析剩余字节
实现时的边界条件
明确长度是否包含头部、字节序和允许的最大值,不能按不可信长度无限申请内存。
send可能只接受部分字节,调用方需记录进度;recv返回多少处理多少。在消息未完成前遇到正常关闭,应报告截断,不能把残缺数据当完整消息。
非阻塞模式遇到暂时不可读或不可写,等待相应事件后继续,而不是丢弃已有状态。
超时用于控制资源占用,不应作为消息边界。
MSS 与 MTU
MTU 是链路能承载的最大网络层包大小,MSS 限制 TCP 段中的数据部分。例如 MTU 1500、无选项 IPv4 首部 20 字节、TCP 首部 20 字节时,对应 1460 字节 TCP 数据。这个值不是应用消息长度上限,也不是每次都必须发满的大小。
面试时可以总结为:定长、分隔符和长度前缀都能定义消息边界,关键是让接收解析器处理任意大小的连续输入。长度校验、部分读写和关闭时的残缺消息处理同样不可缺少。
可能追问的问题
长度字段被分成两次读取,解析器怎么处理?
一次读取到三条消息和半条消息时怎么办?
为什么要限制消息长度并检查整数溢出?
MTU 1500 时 TCP 数据长度一定是 1460 吗?
3. 请说下三次握手的目的?
面试题剖析
本题侧重握手的设计目的。回答应以双向序列号同步和连接状态确认为主,结合历史连接说明第三次确认的价值;“验证收发能力”可以辅助理解,但不能替代协议层原因。
面试题解答
TCP 三次握手是为了在普通主动打开场景下,交换并确认双方的初始序列号,确认当前连接意图,并建立后续可靠字节流传输所需的状态。
- 双向同步序列号
TCP 是全双工的,两个发送方向使用独立序号空间。客户端发送自己的 ISN,服务端确认它并发送服务端 ISN,客户端再确认服务端 ISN。只有这样,两端才知道对方下一字节从哪里开始。
- 避免历史请求被直接当作新连接
网络可能延迟、复制旧报文。服务端收到 SYN 并发送 SYN+ACK 后,仍需要等客户端对这一次服务端序号的有效确认。如果客户端发现响应不属于自己的当前尝试,就不会正常确认该连接。两次握手若直接把服务端置为已建立,会让旧请求更容易创建无用状态。
- 建立并协商必要状态
握手报文通告窗口及 MSS 等信息,并可协商窗口扩大和 SACK 等能力,为可靠传输和流量控制准备状态。每端的通告方向和作用不同,不能简单说所有参数取双方相同值。
目的的边界
握手并不证明用户身份,也不保证网络以后不会中断,更不保证应用程序已经准备好处理业务。它只能建立当前连接的 TCP 状态;业务认证由 TLS 或应用层机制承担。三次握手仍会创建待确认状态,所以不能据此认为已消除 SYN Flood 风险。
面试时可以总结为:握手让两个方向的初始序列号得到确认,减少历史连接请求造成的错误建立,并通告连接参数。它是可靠传输的起点,后续可靠性仍靠序号、确认、重传和窗口等机制实现。
可能追问的问题
为什么 TCP 两个方向要分别维护序号?
三次握手是否等于身份认证?
握手完成后为什么还需要重传机制?
三次握手为什么不能直接消除 SYN Flood?
4. 请说下 TCP 三次握手过程
面试题剖析
这道题要求把报文标志、序列号、确认号和双方状态变化串起来。重点是两端各有独立的初始序列号,ACK 确认的是下一个期望收到的字节序号。以下以普通主动打开、没有握手数据的 TCP 连接为例。
面试题解答
TCP 三次握手用三个报文完成双方初始序列号的交换与确认。假设客户端初始序列号为 x,服务器初始序列号为 y。

图:SYN 各占一个序号;客户端发送第三次 ACK 后进入 ESTABLISHED,服务端收到有效 ACK 后进入该状态。
- 第一次握手:客户端发送 SYN
客户端发送 SYN=1, seq=x,从 CLOSED 进入 SYN-SENT。服务器此前已经处于 LISTEN 状态。此时客户端告诉服务器:“我的发送序号从 x 开始,希望建立连接。”
- 第二次握手:服务器发送 SYN + ACK
服务器回复 SYN=1, ACK=1, seq=y, ack=x+1,对应连接进入 SYN-RECEIVED。其中 ACK 确认客户端 SYN,SYN 告知服务器自己的初始序列号。监听 Socket 仍继续监听其他连接。
- 第三次握手:客户端发送 ACK
客户端校验响应后发送 ACK=1, seq=x+1, ack=y+1,进入 ESTABLISHED。服务器收到有效 ACK 后,该连接也进入 ESTABLISHED。
序号和异常情况
SYN 即使不带数据也消耗一个序号,纯 ACK 不消耗序号。第三次握手可以携带应用数据,如果携带 N 个字节,后续客户端发送位置就继续前进 N 个字节。
SYN 或 SYN+ACK 丢失时,相应一端会在超时后重传。第三次的纯 ACK 没有独立的重传定时器;如果它丢失且服务端尚未通过后续有效报文完成握手,服务端会重传 SYN+ACK,客户端收到后再次确认。
MSS、窗口扩大、SACK 支持等能力也会在 SYN 报文中通告或协商,但两个方向的 MSS 通告不必相同。状态与序号的详细定义见 RFC 9293。
面试时可以总结为:客户端用 SYN 告知自己的初始序列号,服务器用 SYN+ACK 同时确认客户端并发送自己的初始序列号,客户端再用 ACK 确认服务器。双方完成各自方向的序号同步后建立连接。
可能追问的问题
SYN 为什么占一个序号,纯 ACK 为什么不占?
第三次握手可以携带数据吗?
第三次 ACK 丢失后,谁负责触发重试?
客户端 connect 成功时,服务端应用一定已经 accept 了吗?
5. 三次握手中,第二次握手的时候为什么还要传回 SYN?
面试题剖析
本题考察能否区分 ACK 和 SYN 的职责。ACK 确认的是对方发送方向,SYN 通告的是自己发送方向;二者合并并不代表两个方向共用一个序号。
面试题解答
第二次握手携带 SYN,是因为服务端也需要把自己的初始序列号告诉客户端。客户端的 SYN 只建立了客户端发送方向的起点,不能替服务端选择序号。

图:第二条消息中的 ACK 确认客户端,SYN 同步服务端。
假设客户端发送 SYN, seq=x,服务端返回 SYN+ACK, seq=y, ack=x+1。其中:
ACK=1, ack=x+1:确认客户端的 SYN,表示下一步期待从 x+1 开始。SYN=1, seq=y:通告服务端初始序列号,要求客户端确认这一发送方向。
客户端随后用 ack=y+1 完成确认。如果第二次只发送普通 ACK,客户端就没有通过这一报文获得服务端 SYN 所表达的起始序号同步信息,还需要服务端另发 SYN,再进行确认。
因此,将“对客户端 SYN 的确认”和“服务端自己的 SYN”放进同一报文,可以减少普通连接建立的消息数。它不是额外创建一条反向 TCP 连接,而是在同一全双工连接内初始化另一个方向。
面试时可以总结为:ACK 表示“收到了你的起始序号”,SYN 表示“这是我的起始序号”。TCP 有两个独立发送方向,因此第二次握手需要同时完成这两件事。
可能追问的问题
服务端只发送 ACK 而不发送 SYN,会缺少什么信息?
客户端和服务端必须使用相同的 ISN 吗?
SYN+ACK 是否意味着建立两条 TCP 连接?
6. 为什么要三次握手,4 次握手可以吗?
面试题剖析
本题关注“足够”与“必要”的区别。需要用历史请求和双向序号确认解释两次的不足,再说明四次为何可以简化,避免背成脱离 TCP 场景的通信次数定理。
面试题解答
普通主动打开场景中,三次握手已经能完成双方初始序列号的交换与确认。四个报文也可以完成对应逻辑,但通常没有必要把可以一起发送的 SYN 与 ACK 拆开。

图:这里的 100 和 200 属于不同连接尝试;同一次 SYN 超时重传不会随意更换 ISN。
为什么两次不够
客户端发送 SYN 后,服务端通过 SYN+ACK 确认客户端并通告自己的序号。但在收到第三次有效确认前,服务端不能确认客户端已经接受了这次服务端序号,也不能仅凭到达的 SYN 判断它一定代表仍有效的连接意图。
例如客户端当前尝试的 ISN 为 200,网络中一个旧尝试的 SYN seq=100 延迟到达。服务端回应 ack=101,客户端可以根据当前连接上下文识别它不符合预期,按协议拒绝这次旧尝试。不要把这个例子写成“同一 SYN 重传时序号自动从 100 改成 200”。
四次为什么通常没必要
从逻辑上可以分为“客户端 SYN → 服务端 ACK → 服务端 SYN → 客户端 ACK”。服务端收到连接请求时通常能够立刻发出自己的 SYN,所以第二、第三项可合并成 SYN+ACK,形成三个报文。
TCP 还存在双方同时主动打开的情况,其报文交换形态可能不同。因此应限定为常见主动/被动打开场景,而不是宣称所有网络协议、所有异常重传都恰好只需三个报文。
面试时可以总结为:两次缺少客户端对服务端 SYN 的确认,难以可靠建立双方的当前序号状态;四次能表达同样的逻辑,但常规握手可以合并服务端的 ACK 和 SYN,所以三次足够。
可能追问的问题
同一 SYN 的超时重传会更换初始序列号吗?
延迟到达的旧 SYN 为什么可能造成问题?
TCP 同时打开时,握手形态有什么不同?
三次握手是否保证不产生任何资源浪费?
7. 为什么不使用「两次握手」和「四次握手」?
面试题剖析
这是一道简洁论证题,重点在于用双方分别掌握的信息推导握手次数。与上一题的异常实例相比,本题用“每一步确认了什么”形成清晰回答。
面试题解答
常见 TCP 主动打开时,两次不足,四次通常多余,三次能完成双向序列号确认。
两次就认定建立,会缺少最后一项信息,也无法让服务端通过最后确认排除一部分无效历史尝试。四次可以把服务端 ACK 与 SYN 拆开发送,但常规情况下两者可以合并,因此增加报文没有额外必要。
“确认收发能力”可以作为口语化理解,不过不能据此声称双方对未来通信一定成功。网络随时可能中断,丢包和重传也会使实际抓包数量超过三个。
面试时可以总结为:第三次补上客户端对服务端起始序号的确认;第四次通常只是把服务端本可合并的两个动作拆开,常规握手不需要。
可能追问的问题
第三次 ACK 到达前,服务端为什么还不能认定连接已建立?
为什么抓包可能看到多于三个握手报文?
“双方都能收发”能否完整解释三次握手?
8. 请简要介绍一下 WebSocket
面试题剖析
这道题考察实时通信协议的定位和建立方式。需要区分 WebSocket、HTTP 持久连接和 TCP,说明升级后的帧与消息机制,并注意握手版本和部署边界。
面试题解答
WebSocket 是应用层双向通信协议,连接建立后客户端和服务器都可以主动发送消息。典型 RFC 6455 场景运行于 TCP,ws:// 默认端口 80,wss:// 默认端口 443,后者使用 TLS 保护。
- 典型 HTTP/1.1 升级握手
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务器接受后返回 101 Switching Protocols 及正确的升级响应字段,其中 Sec-WebSocket-Accept 由请求中的 key 按协议规则计算。这个过程确认对端理解 WebSocket 升级,不能代替用户身份认证。具体字段和帧格式见 RFC 6455。
升级完成后,同一连接开始交换 WebSocket 帧,不再为每条消息发送一套普通 HTTP 请求响应。HTTP/2 等环境有扩展 CONNECT 的承载方式,因此不能说所有 WebSocket 都通过 101 建立。
- 消息与控制帧
WebSocket 支持文本和二进制消息,一条消息可以分成多个帧;协议层能够表达消息边界,这与 TCP 字节流不同。Ping/Pong 可用于探测连接,Close 用于关闭协商。经典协议中客户端发出的帧需要掩码处理,但掩码不是加密,机密性由 TLS 提供。
- 与轮询和长连接对比
普通 HTTP 长连接只是复用传输连接;轮询重复询问有没有新数据;长轮询让一次响应等待事件。WebSocket 则适合聊天、协作编辑等持续双向消息场景。仅服务端单向推送时,SSE 也可能适合。
- 实际部署
服务端仍需验证 Origin、认证用户并逐消息授权,代理需支持对应协议和空闲超时。连接中断后的重连、消息补发、业务去重和背压一般由应用负责。不能因为 TCP 可靠就承诺跨重连不丢业务消息。
面试时可以总结为:WebSocket 在建立连接后提供有消息边界的全双工通信,省去逐条 HTTP 请求的开销,但认证、重连与业务可靠性仍需另外设计。
可能追问的问题
WebSocket 和 HTTP 长连接有什么区别?
Sec-WebSocket-Key 是用户认证凭据吗?
WebSocket 掩码与 TLS 加密有什么区别?
断线重连后如何避免消息丢失或重复?
9. 为什么要四次挥手?
面试题剖析
这道题考察全双工连接的独立关闭、半关闭和关闭状态。需要说明典型四报文流程及 TIME_WAIT,同时明确挥手不必永远恰好四个报文,不能说三报文关闭必然丢数据。
面试题解答
TCP 两个方向可以独立结束发送。典型关闭过程包含双方各发送一个 FIN、各确认对方 FIN,因此常称为四次挥手。主动关闭方不一定是客户端,服务器也可以主动关闭。

图:典型主动/被动关闭场景;图中省略普通 ACK 的其他字段和中间业务数据。
- 主动方发送 FIN
主动方已经没有新数据要发送,发送 FIN, seq=u,进入 FIN-WAIT-1。FIN 排在此前发送数据之后,并占用一个序号;它只结束本端发送方向,主动方仍可接收对方数据。
- 被动方确认 FIN
被动方确认到 FIN 后回复 ACK, ack=u+1,进入 CLOSE-WAIT。主动方收到确认后进入 FIN-WAIT-2。被动方的应用此时可能还有待发送数据,所以可以继续发送。
- 被动方也发送 FIN
等应用完成本端发送并关闭发送方向,被动方发送 FIN, seq=v,进入 LAST-ACK。这里 v 取决于它实际已经发送的数据,不一定等于握手 ISN 加一。
- 主动方确认并等待
主动方回复 ACK, ack=v+1,进入 TIME-WAIT;被动方收到 ACK 后关闭。主动方等待 2MSL 后才结束该状态。MSL 是报文最大生存时间的协议概念,实际等待时长由系统实现决定,不是所有系统固定相同。
为什么有 TIME_WAIT
它允许最后 ACK 丢失时再次确认对方重传的 FIN,并使旧连接报文有机会在相同四元组被重新使用前消失。TIME_WAIT 不是等待对端应用“确认最后一个 ACK”,纯 ACK 不需要再被 ACK。
如果被动方收到 FIN 时已经能关闭发送方向,确认与 FIN 可以合并,线上可能只有三个报文;重传也会使数量增加。双方同时关闭时还会涉及 CLOSING 等其他路径。
面试时可以总结为:四次挥手来自两个方向独立关闭。收到对方 FIN 只表示对方不再发送,自己可能仍有数据,所以 ACK 和本端 FIN 常分开发送;最后主动方通过 TIME_WAIT 处理末次确认丢失和旧报文问题。
可能追问的问题
四次挥手可以变成三个报文吗?
TIME_WAIT 与 CLOSE_WAIT 分别说明什么?
最后一个 ACK 丢失后会发生什么?
调用 shutdown 关闭写方向后还可以读取吗?
为什么 TIME_WAIT 不一定在客户端?
10. 为什么建立连接是三次握手,关闭连接却是四次挥手呢?
面试题剖析
本题应围绕“哪些动作可以合并、哪些动作需要等待应用”进行对比。握手与挥手都是双向状态管理,但服务端能发送 SYN 和能发送 FIN 的时机不同。
面试题解答
建立连接时,服务端通常可以立即将自己的 SYN 与对客户端 SYN 的 ACK 合并;关闭时,对方 FIN 的确认与本端 FIN 往往不同时就绪,所以通常分开发送。
握手:两个动作通常同时就绪
服务端收到 SYN,既能确认客户端初始序列号,也能选择自己的初始序列号,发送 SYN+ACK。因此,双向 SYN 与各自确认的逻辑可以用三个报文完成。
挥手:确认和结束发送的时机不同
收到 FIN 后,TCP 可以确认它,但本端应用可能仍需返回数据。比如客户端上传请求体后关闭写方向,服务器仍要发送处理结果;此时不能仅因收到 FIN 就立即结束自己的发送方向。应用完成发送后才发本端 FIN。
因此,“三次握手、四次挥手”描述的是常见流程,不是固定报文计数。条件满足时 ACK 与 FIN 能合并成一次发送,也不会因此必然丢数据。
面试时可以总结为:区别在于动作是否同时就绪。建立连接时 ACK 与 SYN 通常可以合并;关闭时对方说完不代表自己说完,所以 ACK 与 FIN 常分开。
可能追问的问题
为什么收到 FIN 后服务器还可以继续发送数据?
关闭连接时 ACK 与 FIN 在什么条件下可以合并?
同时关闭时一定也是标准四报文流程吗?
11. 如果已经建立了连接,但是客户端突然出现故障了怎么办?
面试题剖析
这道题考察故障检测,而不是背一个保活计时器的默认值。需要区分应用退出、主机断电、网络中断,以及连接上有没有未确认数据;同时区分 TCP 可达性与业务可用性。
面试题解答
对端故障不一定立即被本端发现。能否及时检测,取决于是否收到 FIN/RST、是否有数据等待确认,以及是否启用了保活或应用心跳。

图:没有数据交互且没有探测机制的连接,可能长时间保持表面上的已建立状态。
- 进程退出但操作系统仍正常
操作系统通常会清理进程持有的 Socket 引用,触发正常关闭或因具体状态产生复位。对端可能看到 EOF 或连接错误。共享文件描述符、未读数据和关闭选项等会影响结果,不能一概断言进程退出必然发同一种报文。
- 断电、断网或静默丢包
这种情况可能没有 FIN 或 RST。如果本端正在发送数据,未确认数据会触发超时重传,超过重试或配置的用户超时后连接报错;不能看到一次超时就断定对端机器已死,也可能只是网络拥塞或路径故障。
- 空闲连接与 TCP Keepalive
可通过 SO_KEEPALIVE 等接口开启保活,在空闲一定时间后发送探测,多次无响应再判定不可达。保活不是所有 Socket 默认开启,时间和次数依系统配置。Linux 的 tcp_keepalive_time、tcp_keepalive_intvl、tcp_keepalive_probes 含义见 Linux 内核文档,不能把某组默认值当成 TCP 协议的统一规定。
- 业务更需要应用心跳与超时
TCP 仍能回应 ACK,不代表应用线程没有卡死。应用心跳检测服务是否能在规定时间内完成响应;请求超时、读写超时和重连策略则界定业务可接受的等待。断线后重试写操作必须配合幂等键或结果查询,因为对端可能已执行但响应未返回。
面试时可以总结为:有报文时通过关闭或错误信号发现,有未确认数据时通过重传超时发现,空闲连接依赖保活或心跳。TCP 只能提供传输层故障线索,业务健康和重复执行问题还需应用处理。
可能追问的问题
TCP Keepalive 和 HTTP keep-alive 有什么区别?
连接显示 ESTABLISHED 就说明应用一定健康吗?
关闭进程与拔掉网线的现象为什么可能不同?
请求超时后重试为什么可能重复执行?
12. TCP 的特点
面试题剖析
本题要求概括 TCP 的服务特性,并说明可靠性由哪些机制共同实现。注意按字节编号、端到端有序交付,以及 TCP 确认不等于业务处理完成;校验和也不是密码学防篡改机制。
面试题解答
TCP 是面向连接、全双工、可靠、有序的字节流传输协议。它通过端口支持应用通信,每条连接通常由本地 IP/端口与远端 IP/端口四元组识别。
- 面向连接和字节流
两端先建立连接状态,再传输连续字节。TCP 不保留每次写入的消息边界,不提供广播、多播服务。每个方向分别维护序号、确认和窗口状态。
- 序号、确认和重传
TCP 按字节编号,用累计 ACK 指出下一个期望字节。发送端保留尚未确认的数据,需要时重传;接收端可缓存乱序段,去除重复数据,再把连续字节按序交付。确认应答可以累计或延迟,不是每个报文都必须马上对应一个独立 ACK。
超时重传通过估计 RTT 及波动计算 RTO,并采用退避;快重传等机制可在超时前推断丢包。SACK 在协商后提供已接收非连续区间信息,有助于更准确地恢复丢失部分。
- 校验和
TCP 校验和覆盖伪首部、TCP 首部与数据,用于检测传输错误。它不是 MD5,也不能防止主动攻击者修改后重新计算校验和;传输安全应使用 TLS 等机制。
- 流量控制与拥塞控制
接收窗口 rwnd 表达接收端可接受的数据量,拥塞窗口 cwnd 约束网络中的在途数据。两者共同限制发送,分别防止接收端被压垮和网络过载。滑动窗口使多个字节段可以在等待确认时同时在途,提高利用率。
- 可靠性的边界
可靠并不意味着网络永久故障时数据仍必定到达,也不意味着对端已写入数据库。send 成功通常只说明数据被本地协议栈接受;TCP ACK 表示对端 TCP 已接收相应字节。应用事务完成需要应用层响应确认,跨重试去重需要业务幂等。
MSS 表示可接受的最大 TCP 数据段大小,两个方向分别通告;实际发送可小于 MSS,也会受路径 MTU、选项和窗口影响,不是固定按 MSS 块传输。
面试时可以总结为:TCP 用连接状态、字节序号、确认、重传、差错检测和窗口实现可靠有序字节流。它解决传输层交付问题,不替应用定义消息边界,也不保证业务恰好执行一次。
可能追问的问题
TCP ACK 与业务处理成功有什么区别?
TCP 校验和能防止恶意篡改吗?
接收窗口与拥塞窗口分别由谁维护?
MSS 是双方共同取一个最小值后永远不变吗?
13. 请详细说一下 TCP 的滑动窗口
面试题剖析
本题重点是窗口如何随确认推进,以及接收能力如何约束发送。建议用字节区间说明四类数据,明确窗口按字节计量,并把流量控制 rwnd 与拥塞控制 cwnd 分开。
面试题解答
滑动窗口允许发送方在尚未收到确认时继续发送一定数量的数据,避免退化为“发送一段、等一个 ACK”的停等方式。随着连续数据被确认,窗口左边界向前推进。

图:假设有效窗口保持 2000 字节;窗口滑动不等于必须等整个窗口全部确认。
- 发送缓冲区中的四类数据
已发送且已确认;已发送未确认;未发送但窗口允许;窗口外暂不能发送。真正的发送窗口包含中间两类,已确认数据在窗口左侧,暂不能发送的数据在右侧,不能把整个四类区域都称为当前窗口。
- 一个具体例子
假设下一个待确认字节为 1000,有效窗口 2000 字节,则当前允许区间为 [1000, 3000)。若 1000—1999 已发出,2000—2999 仍可发送。收到 ACK=2000 且窗口保持 2000 字节后,允许区间前移为 [2000, 4000),新释放出的 3000—3999 可以继续使用。
- 谁决定能发送多少
接收端通告 rwnd,发送端维护 cwnd。简化理解,允许的在途数据上限受 min(rwnd, cwnd) 约束;还能新发多少,还要扣除已在途部分并考虑实际缓冲区和协议状态。
接收程序读取缓冲区后,接收端可以通告更多空间;若应用处理慢,则窗口可能缩小。网络拥塞主要通过 cwnd 控制,不能把 rwnd 说成网络拥塞指示器。
- 零窗口与扩大选项
rwnd 为零时,发送方暂缓发送普通新数据,并使用持续计时器与零窗口探测避免因窗口更新丢失而永久等待。零窗口不等于对端连接已断开,也与 Keepalive 的故障探测目的不同。
TCP 首部原始窗口字段为 16 位,窗口扩大选项可以在握手阶段协商,使后续字段按比例解释,支持更大的接收窗口。扩大比例不是连接中随意重新协商的,SYN 中的窗口值本身不使用该缩放。
- 为什么能提高效率
窗口让多段数据持续在网络中传输。在高带宽、高 RTT 的路径上,带宽时延积较大,过小窗口会限制吞吐;不能把优势只归结为低时延网络。
面试时可以总结为:滑动窗口用字节区间限制未确认数据,ACK 到达后窗口前移。rwnd 保护接收端,cwnd 保护网络,零窗口探测和窗口扩大分别解决恢复通知与大窗口表达问题。
可能追问的问题
发送窗口包含哪两类数据,已确认部分在哪里?
rwnd 为零时为什么还需要探测?
窗口扩大选项什么时候协商?
高带宽、高 RTT 网络为什么需要更大窗口?
14. 请详细说一下 TCP 的拥塞控制
面试题剖析
本题考察发送端如何根据网络反馈控制在途数据。先区分流量控制和拥塞控制,再以经典 Reno 解释慢启动、拥塞避免、快重传和快恢复;必须说明算法版本和计量单位,避免把教学模型写成所有 TCP 实现。
面试题解答
拥塞控制通过限制进入网络的流量,避免瓶颈队列持续积压、丢包和拥塞崩溃。发送端维护拥塞窗口 cwnd,配合接收窗口 rwnd 约束在途数据。以下介绍经典 Reno 的简化行为,现代系统还可能使用 CUBIC 等算法。

图:示意从 1 MSS 起步;实际初始窗口取决于规范与实现。增长按 RTT 轮次理解。
- 慢启动
当 cwnd 小于慢启动阈值 ssthresh 时,对新确认数据的 ACK 增大 cwnd,经典规则每个确认通常最多增加一个 MSS。在理想 ACK 行为下,一个 RTT 内收到一轮确认,cwnd 近似翻倍,例如 1、2、4、8 MSS。不是每收到一个 ACK 就把整个窗口翻倍,延迟 ACK 和字节计数规则也会改变精确增长。
- 拥塞避免
达到阈值后采用较保守的增长,经典 Reno 约每 RTT 增加一个 MSS。若以字节计,单个 ACK 的典型近似增量为 MSS * MSS / cwnd;若窗口以 MSS 为单位,增量才可写成约 1 / cwnd。公式必须先明确单位。
- 超时重传
发生 RTO 超时时,通常说明反馈不足,需要保守恢复。经典规则把 ssthresh 设为max(FlightSize / 2, 2*MSS),并将 cwnd 降至一个 MSS 的丢失窗口,重新慢启动;FlightSize 是已发送但未累计确认的数据量,不应无条件拿配置窗口替代。
- 快重传
收到三个重复 ACK 等满足算法条件的信号后,发送端推断出现缺口,不必等待 RTO。例如第 2 段丢失,第 3、4、5 段分别到达,接收端可分别重复确认同一缺口,累积出三个重复 ACK。只收到第 3 段一次,不会凭空生成三份重复 ACK。乱序也可能导致误判,因此这些机制是基于反馈推断,而非证明物理链路一定丢包。

图:经典 Reno 单丢包示例;额外重复 ACK 与确认恢复数据的新 ACK 的处理不同。
- 快恢复
经典 Reno 快恢复将阈值按在途量约减半,并暂时设置 cwnd=ssthresh+3*MSS,反映重复 ACK 所指示的离网数据。每额外收到一个重复 ACK,可临时再增一个 MSS;确认恢复数据的新 ACK 到达后,将 cwnd 收回 ssthresh,进入拥塞避免。NewReno、SACK 恢复等对部分确认和多丢包的处理更完善,不能照搬单丢包过程解释所有实现。
丢包并非唯一反馈,ECN 也能表达拥塞。经典算法的具体规则见 RFC 5681。
面试时可以总结为:慢启动快速探测容量,拥塞避免谨慎增长,快重传缩短丢包恢复等待,快恢复在仍有 ACK 反馈时避免直接退回最小窗口;超时则采取更保守的恢复。所有结论要限定算法和单位。
可能追问的问题
慢启动是每个 ACK 翻倍,还是每轮 RTT 近似翻倍?
三个重复 ACK 是怎样产生的?
FlightSize、cwnd 和 rwnd 有什么区别?
超时恢复与快恢复为什么不同?
Reno、NewReno、CUBIC 和基于 SACK 的恢复属于完全相同的机制吗?
15. 说一下什么是半连接队列
面试题剖析
本题考察服务端握手状态与应用接收连接之间的关系。需要区分半连接队列和全连接队列,并说明这些名称和容量控制属于实现细节,而不是 TCP 报文中的两个协议字段。
面试题解答
在 Linux 常见实现中,半连接队列保存握手尚未完成、等待客户端最终确认的请求;全连接队列保存握手已完成、等待应用 accept 取出的连接。

图:队列描述的是监听端的连接处理过程;SYN Cookie 可以走不同的状态保存路径。
- 请求如何流转
服务端监听 → 收到 SYN → 记录请求并回复 SYN+ACK → 等待有效 ACK → 创建完成的连接并进入待 accept 队列 → 应用 accept 返回新连接的文件描述符。
等待握手的连接处于 SYN-RECEIVED,Linux 工具中通常显示为 SYN-RECV;完成握手后为 ESTABLISHED。监听 Socket 自身仍保持监听,不会因处理一个连接就整体变成已连接状态。
- 两个队列满的原因不同
半连接积压可能源于握手往返慢、第三次 ACK 丢失、客户端不完成握手或 SYN Flood。全连接积压通常意味着新建连接速度超过应用 accept 速度,例如事件循环被阻塞。仅看到连接超时,不能立即判断是哪一种。
- 参数与边界
Linux 的 tcp_max_syn_backlog 关联待确认请求容量;listen(backlog) 在现代 Linux 中主要限制等待 accept 的完整连接,并受到 somaxconn 等限制。开启 SYN Cookie 时,半连接保存方式不同。具体行为需结合内核版本与配置,参见 listen 手册。
增大队列可以缓冲突发,但不能解决应用长期处理不足,也不是抵御无限攻击的办法。排查时结合握手状态、队列溢出统计、accept 处理速度和 CPU/内存情况。
面试时可以总结为:半连接等待握手确认,全连接等待应用取走。握手由内核完成,accept 负责交付已建立连接,两个阶段应分别观察和调优。
可能追问的问题
半连接队列与全连接队列分别等待什么?
accept 调用慢会首先影响哪个队列?
listen(backlog) 与 tcp_max_syn_backlog 是同一个参数吗?
使用 SYN Cookie 时半连接状态如何变化?
16. 什么是 SYN 攻击?
面试题剖析
本题关注拒绝服务的资源消耗机制及防御边界。需要解释为何未完成握手也可能消耗状态,并区分 SYN Flood、分布式攻击和源地址伪造,避免把三者直接画等号。
面试题解答
SYN Flood 是通过大量 TCP 建连请求消耗目标握手处理能力或相关网络资源的拒绝服务攻击。来自多个攻击源时可构成 DDoS;它并不必然是分布式,也不要求所有请求都使用不存在的源 IP。

图:不同防御措施作用于不同位置,状态防护不能消除入口带宽压力。
- 为什么能造成压力
普通处理路径下,服务端收到 SYN 后保存待确认请求、回复 SYN+ACK,并等待 ACK。大量请求不完成握手时,状态、重传计时器、CPU 和网络带宽都会承压。每个待确认请求会超时清理,不是永久保留,但持续到来的请求仍可能不断挤占容量。
攻击者可以伪造源地址,也可以使用真实主机发起大量请求。服务器单靠观察一个地址并不能轻易证明其是否伪造,真实用户突发访问也可能造成类似表面现象。
- SYN Cookie
服务端把可验证信息编码进 SYN+ACK 的初始序列号,先不按常规方式保存每个待确认请求;客户端 ACK 返回后,利用确认号等信息校验并恢复必要状态。它是服务端技术,不是需要客户端理解的一个新 TCP 选项,也不是 HTTP Cookie。
- SYN Proxy 与上游防护
代理先与客户端完成握手验证,再为合法连接联系后端,隔离后端的半连接压力。代理本身仍需要资源。限速、容量调整、网络入口过滤和上游清洗各有作用,误限流也会影响共享出口下的正常用户。
防御思路与取舍可参考 RFC 4987。线上应结合异常 SYN 速率、握手完成率、队列溢出和带宽,而不是仅凭 SYN 数量判断攻击。
面试时可以总结为:SYN Flood 利用握手未完成阶段的处理开销制造压力。SYN Cookie 减少待确认状态,SYN Proxy 把握手验证前移,容量调整缓冲突发,大流量场景还需要网络侧能力。
可能追问的问题
SYN Flood 一定需要伪造源 IP 吗?
SYN Cookie 与 HTTP Cookie 有关系吗?
SYN Cookie 为什么不能解决入口带宽耗尽?
怎样区分访问突发、应用 accept 缓慢与握手攻击?
UDP
17. UDP 的特点
面试题剖析
本题考察 UDP 自身提供与不提供的能力。回答应从无连接、数据报边界和简单首部出发,同时指出可靠性、拥塞控制可以由上层补充,不能笼统认定所有 UDP 应用都不可靠。
面试题解答
UDP 是无连接、面向数据报的传输层协议。它使用端口进行复用与分用,首部固定 8 字节,由源端口、目标端口、长度、校验和四个字段组成。
- 无连接
发送前不需要 TCP 那样的协议握手,也不维护 TCP 式的确认、序号和关闭状态。UDP Socket 可以调用 connect 关联默认对端和影响收发行为,但这不是在网络上执行 TCP 三次握手。
- 保留数据报边界
每次数据报发送形成独立消息,接收 API 通常一次取一个数据报,不会像 TCP 一样把两个数据报的正文拼成无边界字节流。接收缓冲不足时,报文可能截断、报错或通过标志提示,余下部分通常不能像 TCP 一样留待下一次读取。
- 不自行保证可靠、有序交付
UDP 不内置确认重传和排序机制,数据报可能丢失、重复或乱序;校验和用于差错检测,不负责重传,也不能抵御主动篡改。上层可设计序号、确认、重试、去重或前向纠错,QUIC 就在 UDP 上实现了可靠流等能力。
- 轻量但并非无约束地快
UDP 无内置流量控制与拥塞控制,应用仍需对网络负责,避免持续超额发送。它支持单播,并可配合 IP 的广播或多播能力;广播只适用于 IPv4 等相应环境,IPv6 没有广播。大型数据报可能触发 IP 分片,通常应控制大小以适应路径 MTU。
面试时可以总结为:UDP 提供带端口的数据报交付和差错检测,保留消息边界、协议开销较小;可靠性、顺序、拥塞适应和业务确认则取决于上层设计。
可能追问的问题
UDP 调用 connect 会触发三次握手吗?
UDP 接收缓冲区小于数据报时会怎样?
QUIC 为什么能在 UDP 上实现可靠传输?
UDP 应用为什么也要考虑拥塞控制?
18. TCP 和 UDP 的区别
面试题剖析
本题不是简单的“TCP 慢、UDP 快”对照。应比较协议服务、消息边界、控制机制与应用需求,尤其区分 UDP 本身和基于 UDP 构建的可靠传输协议。
面试题解答
TCP 与 UDP 都是使用端口服务应用通信的传输协议,主要区别在于提供的服务模型与控制机制。

图:TCP 保留字节顺序,UDP 保留数据报边界;图中 UDP 示意成功收到的情况。
TCP 有序交付意味着前方字节缺失时后续字节需等待,可能影响实时业务。UDP 应用可以选择丢弃过期状态,但若自己增加重传、加密和拥塞控制,也会增加开销。实际性能取决于网络、实现、消息大小和应用需求,不能用“UDP 一定快”代替分析。
例如文件下载通常希望每个字节完整,而实时语音往往更关心按时播放。但音视频也可能因网络限制使用其他传输方式,文件也可以使用 QUIC 的可靠流,因此场景不是不可改变的协议绑定。
面试时可以总结为:TCP 提供可靠有序字节流,应用需自己定义消息边界;UDP 提供轻量数据报,应用需根据业务补充可靠性和拥塞适应。选择取决于完整性、时效性和实现成本。
可能追问的问题
UDP 一定比 TCP 快吗?
TCP 为什么会产生队头阻塞?
为什么 HTTP/3 使用 UDP 却仍然可靠?
实时业务是否完全不需要可靠消息?
19. TCP 的应用场景
面试题剖析
本题考察根据业务要求选择协议的能力。应解释为什么完整、有序、低实现成本使 TCP 合适,而不是只重复 TCP 特点;也应说明长连接和业务确认仍需应用处理。
面试题解答
TCP 适合需要可靠、有序字节流,并希望由系统协议栈完成重传、流量控制和拥塞控制的点对点通信。
典型场景
文件传输、软件包下载:少一个字节都可能造成内容损坏,需要完整交付并配合应用层校验。
SSH 远程登录:命令与输出需要有序传递,安全保护由 SSH 协议承担。
邮件协议:SMTP、IMAP、POP3 的常见传输依赖 TCP,避免应用自行实现基本可靠性。
数据库连接、许多 RPC:需要连续有序传输请求与结果,同时可通过连接池复用连接。
HTTP/1.1、HTTP/2:通常利用 TCP 传输;HTTP/3 使用 QUIC,因此不能说所有 HTTP 都基于 TCP。
选择时还要考虑什么
长连接能减少建连成本,但要维护空闲超时、连接池容量和异常恢复。单条字节流在丢包时会产生有序交付等待,多个业务请求共享它时也可能相互影响。对时效敏感的业务,应评估等待重传是否比丢弃旧数据更合适。
TCP ACK 不等于事务提交。数据库写入或支付接口仍需应用层响应、唯一业务标识与幂等处理;连接断开后也不能直接判断最后一笔请求是否执行成功。
面试时可以总结为:TCP 适合完整性和顺序性优先、希望复用成熟协议栈的场景,典型如文件、邮件、SSH 和数据库。连接复用与业务幂等则是选择 TCP 之后仍要完成的工程工作。
可能追问的问题
使用 TCP 传文件还需要应用层校验吗?
数据库使用 TCP 后为什么仍需事务和幂等设计?
长连接池如何处理已被对端关闭的空闲连接?
20. UDP 的应用场景
面试题剖析
本题应围绕时效性、数据报边界、广播多播和可定制传输来解释场景。避免把“能容忍部分丢包”理解成不需要任何可靠性,也不要忽略基于 UDP 的可靠协议。
面试题解答
UDP 适用于希望直接控制数据报发送、延迟与丢包处理策略,或需要 IP 广播、多播能力的场景。
- 实时音视频
过期语音帧即使补回也可能错过播放时间。应用可以根据时延预算选择重传、前向纠错、抖动缓冲和丢包隐藏,并动态调整码率。实时性不是任意发包的理由,仍要做拥塞适应。
- 在线游戏状态同步
位置、方向等更新可用新状态覆盖过期状态;登录、交易、道具变更等关键事件则可能需要可靠通道或应用层确认。一个应用可以根据不同消息需求组合多种机制。
- 小型查询与局域网发现
DNS 常见查询可使用 UDP,超时由解析器重试,必要时用 TCP。DHCP、部分服务发现协议使用广播或多播,适合发现尚不知道地址的服务;它们的具体范围受 IP 版本和网络配置限制。
- 承载可演进的传输协议
QUIC 在 UDP 上实现加密、可靠流、流量控制、拥塞控制与连接迁移,供 HTTP/3 等使用。这里提供可靠性的主体是 QUIC,不是 UDP 自动获得了 TCP 的能力。
UDP 数据报应避免不必要的 IP 分片,同时处理 NAT 映射过期、网络封禁与丢包。选择 UDP 意味着上层对消息时效和可靠性承担更多设计责任。
面试时可以总结为:实时媒体与游戏用 UDP 定制时效策略,发现协议利用数据报与广播多播,QUIC 则借 UDP 部署完整的现代传输机制。具体场景取决于上层设计,不能简单等同于“允许丢数据”。
可能追问的问题
游戏中的所有消息都适合直接丢弃吗?
DNS 什么情况下会改用 TCP?
UDP 音视频为什么也需要拥塞控制?
QUIC 使用 UDP 的优势是什么?
继续阅读
阅读导航




