本文面向 C++ 游戏客户端 / 工具开发方向的校招面试,按「面试问法 → 一句话回答 → 机制与过程 → 抓包或客户端场景 → 易错点 → 适用边界 → 通用原则」整理 20 个计算机网络高频基础点,分四章:分层与寻址;传输层——连接、可靠性与性能;应用协议与安全;游戏客户端实时网络。目标是每一题都能先口述一句话结论,再讲清背后的机制,最后说清哪些是协议标准规定、哪些是实现与版本边界。

与已有资料的分工:C++八股.md的第五章与网易互娱面经 1中已有三次握手、TCP/UDP 对比、粘包、HTTP1.1/2/3 等结论式答案,本文不整题复制,而是统一术语、展开机制与边界;socket 的事件通知模型(epoll/IOCP、阻塞/非阻塞)属于操作系统 I/O 范畴,在《操作系统基础面试精讲》第 19–20 题展开,本文只在第 18 题引用,不重复讨论 I/O API;《秋招学习笔记 2》的四次挥手示意图可与本文第 8 题对照,《秋招学习笔记 1》里零散的 socket listen/accept 条目属于系统调用层,可与本文第 2、7 题的端口和建连过程对照阅读;语言层题目留给《C++ 基础面试精讲》,本文不涉及。

阅读约定:术语在第一次出现处解释;凡是「协议标准规定」与「实现或版本相关」分开表述,标准依据 RFC,文末列出了来源;涉及游戏网络的结论都是工程权衡而非协议规定。本文不含需要编译运行的代码,用步骤列表、时序说明和对照表代替伪代码,确保讲清过程而不依赖未经验证的代码。

分层与寻址

TCP/IP 分层与数据封装:一条消息从应用到网卡经历了什么?

一句话回答:TCP/IP 按层协作:应用数据交给传输层加上 TCP(传输控制协议)或 UDP(用户数据报协议)头,网络层加上 IP(网际协议)头,链路层再加帧头和帧尾,每层只处理本层关心的信息并向下传递;接收端按相反顺序拆除(解封装),逐层交给对应的处理程序。

分层的动机是隔离变化、复用能力:应用层(HTTP、DNS、游戏自研协议)不关心路由和链路类型;传输层(TCP/UDP)提供进程到进程的通信与各自可选的保证;网络层(IP)负责跨网络的寻址与转发;链路层(以太网、Wi-Fi)负责一段链路上的实际传输。OSI(开放系统互连)七层是教学参考模型,TCP/IP 体系可按四层或五层讲述;真实协议栈并不严格贴合模型——例如 QUIC 把可靠传输搬到了用户态、TLS 夹在传输层与应用之间。

封装不是物理包装,而是数据被依次加上各层的头部:TCP/UDP 写入源/目的端口与校验和,序号是 TCP 才有的字段;IP 写入源/目的地址,IPv4 用 TTL(Time To Live,生存时间)、IPv6 用 Hop Limit(跳数限制);以太网等链路层帧还包含本段链路的源/目的 MAC 地址与帧校验序列(FCS)。每个头只被对应层解释,对其它层都是载荷。

端到端与逐跳的分工:IP 目的地址通常保持不变,TTL/Hop Limit 随转发递减,NAT 等中间设备还可能改写 IP/端口;而每经过一段链路,数据会重新封装为该链路的帧,链路格式可能不同。普通路由转发主要依据 IP 头,中间防火墙、NAT 或代理也可能检查甚至改写传输层信息。

一次请求从应用到网线的下行过程

  1. 应用构造应用层消息,例如一个自研游戏协议的登录包或一段 HTTP 请求;
  2. 传输层加上 TCP/UDP 头:写入源/目的端口,TCP 另有序号等,形成「段」(TCP)或「数据报」(UDP);
  3. 网络层加上 IP 头:写入源/目的 IP、IPv4 TTL 或 IPv6 Hop Limit,形成 IP 包,并查路由表决定下一跳;
  4. 链路层封装成帧:填上下一跳设备的 MAC 地址与帧校验序列(FCS);
  5. 网卡把帧转成电信号/光信号发出。路径上每台设备解封装到网络层再重新封装下一段链路帧;主机接收的过程与之相反。
层次典型协议本层标识数据单位
应用层HTTP、DNS、游戏自研协议应用自己定义的请求/消息消息
传输层TCP、UDP端口号(定位进程)段 / 数据报
网络层IP、ICMPIP 地址(定位主机与网络)包
链路层Ethernet、Wi-Fi、ARPMAC 地址(定位网卡)帧

抓包与排障视角:抓包工具里遇到的每个包都可以按层展开:帧头只在本地网段有效,公网传输中会被运营商链路重新封装;IP 与 TCP 头通常可在端点及中间设备看到(NAT/代理会改写或终止连接,TLS 另行保护应用数据)。因此排障可按「地址/路由是否可达(ping / traceroute 仅作线索)→ 传输端口是否可用 → 应用数据是否正确」逐层缩小范围。分层模型最大的实用价值就是提供这个排查顺序。

易错点

  • 分层是概念模型,实现并不严格:QUIC 在用户态重建了可靠传输,TLS 位于传输层与应用之间;不要把现实协议硬塞进 OSI 七层的每一格;
  • 「某一层加了头,其他层就看不到内容」是错觉:防火墙与中间设备可以检查甚至修改到应用层(深度包检测、代理);封装本身不提供保密(那是 TLS 的职责);
  • 五层/四层模型的层数之争不重要,面试真正要考的是「这个信息由哪一层负责」的推理方式;
  • 链路层之下还有物理层细节(编码、介质),说清各层职责即可,不必背 OSI 的全部名目。

适用边界:分层与封装是 TCP/IP 体系的通用结构(RFC 1122 描述主机要满足的要求);每一层的具体封装格式随链路技术和服务实现而变。本文后续各题的定位方法相同:先确认问题发生在哪一层,再谈该层的机制。

与已有资料的关系:C++八股.md 第五章按题给的是结论式答案;本题把「封装 / 解封装」展开成分层地图,供后文所有题目引用。复盘时若被问到网络问题,先用这层的排查顺序定位,再回填具体机制。

通用原则:遇到网络问题先做「层归属」:地址与路由、传输语义、应用协议各有独立的失败模式。分层思维的价值是让排查有顺序、职责有边界;任何跨层优化(如应用层自研可靠传输)都要先理解它打破了哪一层的假设。

MAC 地址、IP 地址和端口分别定位什么?

一句话回答:MAC 地址定位同一链路内交付给哪块网卡(下一跳交给谁),IP 地址定位联网主机(跨网络的端到端地址),端口定位主机内的进程或套接字;出网时逐跳改写的是 MAC,IP 通常作为端点保持(除 NAT 改写),端口由传输层用来分发到达的报文。

MAC(媒体访问控制)地址(以太网常见 48 位,链路层)只在同一链路内有效:以太网帧的目的地址决定这一跳把帧交给哪块网卡。同一局域网内 IP 到 MAC 的解析由 ARP(地址解析协议)负责(IPv4),结果缓存在 ARP 表里。

IP 地址(网络层)带网络前缀:主机用子网掩码判断目的 IP 是否与本机同网段——同网段直接交给目的主机,不同网段则交给默认网关,由网关逐跳转发。注意帧的目的 MAC 填的是「下一跳设备」的 MAC,而不是最终服务器的 MAC。

端口(传输层,16 位)用于区分同一主机上的不同进程或连接:一条 TCP 连接由四元组「源 IP、源端口、目的 IP、目的端口」唯一标识,一个监听端口可以同时服务大量连接(例如 443 上的成千上万条 HTTPS 连接),连接之间靠四元组区分。

客户端 192.168.1.10 访问 203.0.113.5:443 的地址解析过程

  1. 用子网掩码判断:203.0.113.5 不在 192.168.1.0/24 内 → 下一跳是默认网关 192.168.1.1;
  2. 用 ARP 查到网关的 MAC,把它填为以太网帧的目的地址;
  3. 网关解帧、查路由表,逐跳转发;每一跳重新封装新的链路帧;
  4. 进入服务器所在网段后,最后一段链路由路由器用 ARP(IPv6 为邻居发现)找到服务器的 MAC 并交付;
  5. 服务器内核按目的端口 443 找到监听进程,把报文交给它。

客户端连不上服务器的三个层次:「IP 写对了但连不上」常见原因是端口被防火墙/安全组拦截,或 NAT 下服务器无法主动连入(需要已建立的映射);在不同链路位置抓包,帧的目的 MAC 可能不同;TCP 四元组在 NAT 两侧也可能不同。排查「连不上」时按「IP 可达性 → 端口 → 应用协议」依次缩小范围,正是三种地址各自的作用域决定的。

易错点

  • 「MAC 永久不变」不准确:可以软件修改,虚拟机/虚拟网卡每次启动也可能更换;MAC 的定位价值只在链路内;
  • 端口不是「机器地址」:多进程可用不同端口,同一端口也可被大量连接复用;服务器端口的角色是「监听点」;
  • IPv6 没有广播 ARP:地址解析使用多播与邻居发现(Neighbor Discovery)机制,不能把 IPv4 的细节当成通用真理;
  • NAT 之下的 IP/端口可能被网关改写,「端到端地址不变」需要加条件(见第 5 题)。

适用边界:地址分工是通用模型;ARP 属于 IPv4(RFC 826),IPv6 对等机制是邻居发现(RFC 4861);公网上 NAT 会改写地址与端口(RFC 3022)。具体设备的 ARP 缓存时长、NAT 映射行为因实现而异。

与已有资料的关系:本题与第 4 题(DHCP/ARP)、第 5 题(网关与 NAT)互相衔接;C++八股.md 第五章讲 TCP 时默认读者已理解端口,本题把那部分先修补全。

通用原则:记住三层地址的「作用域」:MAC 只在链路内,IP 端到端(NAT 前),端口是进程级复用键。出错时先问「哪一层的地址在这个范围内不对」,而不是笼统地说「网络不通」。

输入域名后,DNS 是怎么把它解析成 IP 的?

一句话回答:客户端把域名交给系统的存根解析器(stub resolver),由它询问递归解析器;递归解析器先查自己的缓存,未命中才按「根 → 顶级域 → 权威服务器」逐级查询,把结果按 TTL 缓存后返回 IP。递归解析器缓存命中时通常只需客户端与解析器之间的一次查询往返;本机缓存命中甚至无需网络查询。

DNS(Domain Name System,域名系统)是一个分布式数据库(RFC 1034/1035):根服务器知道顶级域(.com、.cn)的位置,顶级域知道各域名的权威服务器,权威服务器保存最终记录(A 记录是 IPv4、AAAA 是 IPv6、CNAME 是别名)。记录带 TTL(Time To Live),解析器在 TTL 内可直接用缓存回答。

两种查询方式要分清:客户端到递归解析器是「递归查询」(你帮我查完);递归解析器到各级服务器是「迭代查询」(给我下一步去哪问)。把「每查一次都要从根服务器开始」当事实是典型误区——缓存与递归层承担了绝大部分流量。

连接与传输:DNS 查询通常走 UDP 53 端口(短小、快),响应被截断后可能转用 TCP 53,区域传送或客户端配置也可直接使用 TCP;明文 DNS 可被中间人观察和篡改,因此出现了加密方案(DoT,即基于 TLS 的 DNS,常走 TCP 853;DoH,即基于 HTTPS 的 DNS,走 HTTPS)。

api.game.example.com 的解析过程(缓存未命中)

  1. 应用调用系统解析接口;按系统配置可能先检查 hosts 文件或本地缓存;
  2. 未命中则存根解析器向配置的递归解析器发送查询(通常 UDP 53);
  3. 递归解析器没有缓存记录,先向根服务器查询,得到 .com 顶级域服务器的地址;
  4. 向 .com 服务器查询,得到 example.com 的权威服务器地址;
  5. 向权威服务器查询,拿到 A/AAAA 记录;
  6. 递归解析器按 TTL 缓存结果并返回给客户端;之后的同域请求直接命中缓存返回。

游戏客户端的域名与排障:更新/登录域名常通过 CDN(内容分发网络)或调度系统解析到就近节点,TTL 内换 IP 会「生效慢」;排查「连不上服务器」时先用 nslookup/dig 看解析结果,再用系统解析接口(如 getent hosts)确认应用拿到的地址,以区分 DNS 污染、缓存与「服务器本身挂了」。

易错点

  • 「每次都从根服务器查一遍」错误:递归解析器与各级缓存决定了常见路径只有一次询问;
  • TTL 允许缓存保留记录至到期;缓存也可能提前清除或重新查询,配置变更的生效速度受各层缓存策略影响,TTL=0 也不保证所有客户端立即切换;
  • 传统 DNS 查询默认明文,可能被观察/篡改;DoT/DoH 可保护客户端到可信解析器的链路,结果真实性还取决于解析器及 DNSSEC 等校验,单纯查看 IP 不等于认证;域名解析出多个 IP 时选中哪个由客户端实现决定(双栈机器还有 IPv6/IPv4 竞速机制,RFC 8305);换 IP 后旧 TCP 连接仍绑定旧地址,需要重连。

适用边界:DNS 协议本身(RFC 1034/1035)与部署实践(CDN 调度、EDNS0、DoT/DoH)要分开理解:缓存放哪一层(操作系统、浏览器、路由器、运营商)依实现而定,不要在面试中把某一层的行为说成协议规定。

与已有资料的关系:本文新增覆盖,现有资料未展开 DNS;复盘时若遇到「解析慢 / 换服务器不生效」类追问,从本题回填机制与排障命令。

通用原则:域名是一层间接:换服务器不必改客户端,代价是解析、缓存与传播延迟。理解间接层的代价,才能解释「配置改了为什么还没生效」。

DHCP 和 ARP 分别解决什么问题?

一句话回答:DHCP(动态主机配置协议)负责「我该用什么地址」——自动分配 IP、掩码、网关、DNS 等配置,使用租约;ARP(地址解析协议)负责「这个 IP 对应哪块网卡」——在同一链路内把已知 IP 解析成 MAC。两者一个管配置,一个管解析。

DHCP(RFC 2131,应用层协议,UDP 67/68 端口)解决主机不知道自己该用什么地址的问题:IPv4 主机首次申请地址时可从 0.0.0.0 广播完成典型四步交互,拿到地址、掩码、网关、DNS 与租期;若服务器未另行指定,通常在租期过半(T1)尝试续租。IPv6 可通过 SLAAC(无状态地址自动配置)自动配置,也可使用 DHCPv6(RFC 8415);DHCPv6 的消息与地址机制不同,不能把 IPv4 的广播流程照搬过去。

ARP(RFC 826)工作在链路层与网络层之间:发送方广播「谁有 192.168.1.1,请告诉我你的 MAC」,拥有该 IP 的主机单播回复自己的 MAC;结果放入 ARP 缓存(有条目老化时间)。只有同一链路内的通信才需要 ARP——跨网段时解析的是网关的 MAC(见第 2 题)。

两种协议的触发时机很典型:DHCP 是「开机/换网络」时一次性获取配置;ARP 是每次要发帧时可能发生的即时解析。这就是为什么「IP 配对了也可能同网段不通」(ARP 没通);在启用 IPv4 自动链路本地地址的系统上,DHCP 失败也可能看到 169.254.x.x。

新设备插入网络到能上网的完整过程

  1. 网卡发出 DHCP DISCOVER 广播(DISCOVER → OFFER → REQUEST → ACK 合称 DORA);
  2. 服务器 OFFER 一个空闲地址与配置;客户端 REQUEST 正式请求(广播告知其他服务器);
  3. 服务器 ACK 并登记租约,客户端配置地址、掩码、网关、DNS;
  4. 要发往外网时,先查路由表确定下一跳是网关,再用 ARP 解析网关 MAC;
  5. 封装帧发出;DHCP 租约到期前(通常过半时)单播续租,无需重新广播。

虚拟机 / 测试机连不上:Windows 上自动分配的 169.254.x.x(APIPA)或 Linux 的链路本地地址,可能提示 DHCP 未成功,但不能仅凭地址推断故障根因;同一网段 ping 不通但地址配置正常,常见原因是 VLAN(虚拟局域网)隔离、过滤规则或 ARP 层不通。排障时先分清是「配置层」还是「解析层」的问题。

易错点

  • 169.254/16 是 IPv4 链路本地地址:自动分配时常与 DHCP 获取失败有关,也可能由其他配置途径产生;别单凭它判断为 IP 冲突;
  • ARP 缓存有老化时间:IP 被换绑给另一台设备后,旧缓存会短暂造成错投;
  • IPv6 不使用 ARP,用邻居发现(多播 + 请求/通告消息);
  • 代理 ARP、静态 ARP 是部署手段而非协议默认行为,面试中不要把它们当作标准机制。

适用边界:DHCP 的报文格式、选项随版本演进,v4(RFC 2131)与 v6(RFC 8415)是两套协议;ARP 仅适用于 IPv4,IPv6 的邻居发现(RFC 4861)功能对等但机制不同。

与已有资料的关系:本题与第 2、5 题构成「配置 → 解析 → 网关」链条;C++八股.md 未展开这两个协议,属增量内容。

通用原则:配置问题先分清两层:我有没有地址(DHCP/静态配置),地址怎么到达对端(ARP/邻居发现、路由)。分层的排查习惯在这里体现得最直接。

子网掩码、默认网关和 NAT 是怎么配合的?

一句话回答:子网掩码划分网络前缀,主机据此判断目的地址是否同网段:同网段直接解析 MAC 直发,不同网段交给默认网关;家用 NAT(网络地址转换)常用 NAPT(网络地址端口转换)使多个私网地址通过公网地址及不同端口访问外网,代价是外部无法直接发起连接,需要端口映射或打洞。

私有地址段(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16,RFC 1918)不会在公网被路由,只能用于内部网络。子网掩码把 IP 分成网络部分与主机部分,用于「直连还是交给网关」的决策;默认网关是路由表中一条兜底路由指向的下一跳。

NAT 网关维护转换表:出方向把「内网源地址:端口」映射成「公网地址:端口」(端口级复用叫 NAPT/PAT),回包按表反向改写后送内网主机。它让多台内网设备共用一个公网地址,同时让「没有映射的外部入站包」无法找到内网主机。

NAT 不是防火墙;没有映射时通常无法转发外来入站包,但过滤规则仍由设备策略决定。需要外部主动连接的应用(P2P、自建房间、部分联机游戏)要依赖端口映射(UPnP/手动配置)或打洞(双方先向外建立映射)。NAT 会破坏依赖 IP 地址作为标识的协议(如 FTP 主动模式、IPsec),需要应用层网关(ALG)或 UDP 封装来兼容。

家庭网络访问游戏服务器的路径

  1. PC 判断目的 IP 不在 192.168.1.0/24 内 → 交给默认网关(家用路由器);
  2. 路由器做源地址转换(SNAT):改写源为公网地址 + 新端口,并记录映射;
  3. 数据经运营商网络到达游戏服务器,服务器只看到路由器的公网地址;
  4. 回包到达路由器,查表还原为 PC 的内网地址与端口再转发;
  5. 若服务器或 P2P 想主动连 PC,路由器上没有适用的映射时通常无法转发;可配置静态端口映射、打洞或经中继,仍受过滤策略限制。
场景谁做改写结果
同网段通信无解析 MAC 后直接交付
跨网段到公网NAT 路由器源地址被改写,外部看不到内网 IP
外部主动入站(经 NAT)需要适用的映射与过滤规则无映射时通常无法转发
IPv6 网络通常不需 NAT能否入站还取决于地址作用域、路由和防火墙

P2P 联机与 NAT 类型检测:两边都在 NAT 后的玩家要直连:先用 STUN(会话穿透实用工具)发现各自公网映射,再同时向对方映射发 UDP 包完成打洞;若 NAT 对不同外部目标分别分配映射,向 STUN 服务器测出的端口未必可供另一玩家访问;过滤策略也可能阻断打洞,需要 TURN(基于中继的 NAT 穿透)等中继服务。NAT 类型检测可提供直连可行性的线索,但不能保证直连成功。

易错点

  • 「NAT 是安全措施」是过度简化:它只是地址转换,过滤入站只是副作用;真正的安全策略应由防火墙承担;
  • 「不同内网用了相同地址会冲突」不对:私网地址可以重复,只要不跨 NAT 泄漏;两台设备都叫 192.168.1.10 很正常;
  • 打洞不是总能成功,对称 NAT、企业网络策略都会阻止;设计功能时要有中继兜底;
  • 家用路由器、运营商级 NAT(CGNAT)可能叠加多层转换,映射行为(超时、是否对称)因设备而异,不要假设某一种。

适用边界:NAT 是部署实践(RFC 3022 描述传统 NAT/NAPT),不是 IP 协议本身的组成部分;UDP 的 NAT 行为要求可参考 RFC 4787。打洞与中继是工程手段,具体可用性取决于设备与网络策略。

与已有资料的关系:与第 4 题(地址获取)、第 18 题(实时联机的 UDP/NAT 可行性)呼应;打洞的具体做法放在第四章。

通用原则:IPv4 私网地址不能直接在公网路由;经 NAT 接收入站流量需要适用的映射(可由出站流量或静态配置建立)与允许的过滤策略。设计需要外部主动连接的功能时,先假设最坏 NAT 行为,准备中继兜底,而不是指望打洞一定成功。

传输层:连接、可靠性与性能

TCP 和 UDP 各自提供什么保证?怎么选?

一句话回答:TCP 是面向连接的可靠有序字节流,内建重传、排序、流量与拥塞控制;UDP 是无连接的数据报,保留消息边界但不保证送达与顺序,头部较小;可靠性与发送节奏需由应用自行设计。选择依据是业务对「延迟、可靠性、顺序、消息边界」四个维度的需求,而不是「哪个更快」的笼统印象。

连接与状态:TCP 用三次握手建立双方状态(序列号、窗口、选项),传输期间维护定时器与发送/接收缓冲,关闭按状态机退出;UDP 无连接——每个数据报独立寻址,协议栈不保存对端状态,应用如果需要「连接」语义要自己维护。

可靠性语义:TCP 用序号 + 确认 + 重传 + 去重保证「连接存续期间字节流完整、有序、不重复」;UDP 只有校验和检查(IPv4 可置零,IPv6 普通 UDP 报文必须校验,特定隧道有例外),丢包、乱序、重复都可能发生,协议不负责通知应用。

边界与开销:TCP 是字节流,应用消息边界要自己做分帧(第 10 题);UDP 成功送达时保留单个数据报边界(接收缓冲不足可能截断),但单个数据报大小受 MTU 约束(第 12 题)。TCP 头部最小 20 字节(带选项更大),UDP 固定 8 字节;TCP 内建流控与拥塞控制,UDP 没有——应用不限速就可能在拥塞时持续丢包。

维度TCPUDP
连接与状态面向连接,维护序号/窗口/定时器无连接,无对端状态
交付保证连接内可靠、有序、不重复尽力而为,可能丢/乱序/重复
消息边界无(字节流)有(数据报)
头部开销最小 20 字节固定 8 字节
流控/拥塞控制内建(rwnd + cwnd)无,由应用决定
延迟特性丢包后需等待重传,抖动较大每包独立,可丢旧用新
典型用途登录、聊天、文件、支付DNS、音视频、实时状态同步

按四个维度选传输

  1. 延迟:交互实时(毫秒级手感)还是后台任务(秒级可接受);
  2. 可靠性:每条消息是否必须送达(登录结果、扣费、结算必须;位置更新可以丢);
  3. 顺序:迟到但旧的更新还有没有意义(状态快照无意义、聊天记录有意义);
  4. 边界与大小:是否需要保留消息边界、单条消息是否可能超过 MTU;
  5. 映射到通道:必须可靠有序走 TCP 或 QUIC 的可靠流;时效优先、可丢弃走 UDP 通道;两类都需要就分成两条通道,而不是二选一。

同一个客户端的两条通道:游戏客户端里登录、充值、聊天用 TCP/WebSocket(简单可靠);战斗状态以 20–60Hz 用 UDP 更新,丢一帧无所谓、下一帧覆盖即可。选传输是按消息类别做的,不是「整个游戏选一次」;面试里把这层分工讲清楚,比背协议优缺点更有说服力。

易错点

  • 「UDP 比 TCP 快」是过度简化:UDP 省掉的只是协议内建机制的成本;网络好时 TCP 延迟同样低,UDP 的优势体现在丢包/抖动场景下的可控性;
  • 「TCP 保证消息必达」不成立:保证的是连接存续期间的字节流完整有序;连接断开后要靠应用重试,而重试的语义(至少一次、幂等)由应用自己保证;
  • 「UDP 不可靠所以没人用」错误:DNS 查询、音视频、实时游戏大量跑在 UDP 上,可靠性按需在应用层补(第 19 题);
  • 「TCP 可靠、UDP 实时」不是非黑即白:QUIC 在 UDP 上重建了带流控/拥塞控制的可靠传输(第 16 题),分类维度是能力组合,不是协议名字;
  • 别把实现细节当协议保证:初始重传超时、拥塞算法、默认缓冲区大小都随内核与平台变化。

适用边界:规范层面:TCP 是 RFC 9293,UDP 是 RFC 768;具体吞吐、延迟取决于实现(内核参数、拥塞算法)与网络路径。游戏场景下的选择见第 18 题——那是工程权衡,不是协议规定。

与已有资料的关系:C++八股.md 第五章与网易互娱面经 1 都有结论式对比;本文第 6–12 题把机制逐个展开,本题只搭骨架,复盘时按机制回填追问。

通用原则:选协议先列需求四维(延迟、可靠性、顺序、边界),再映射到能力组合;面试中把「UDP 更快」换成「这份消息可以丢还是必须到」,答案立刻就有了工程依据。

TCP 三次握手为什么是三次?序列号协商了什么?

一句话回答:三次握手交换并确认双方的初始序列号:客户端 SYN(同步,seq=x)→ 服务器 SYN+ACK(确认,seq=y, ack=x+1)→ 客户端 ACK(ack=y+1);客户端发出第三个 ACK 后进入 ESTABLISHED,服务器收到它后也进入 ESTABLISHED;该 ACK 可以捎带数据。第三次确认让服务器知道客户端收到自己的 SYN 和初始序列号,完成双向序号同步,也有助于识别迟到的旧 SYN。

序列号:TCP 把字节流里的每个字节编号,连接建立时双方各自选择一个初始序列号(ISS)。SYN 标志本身消耗一个序列号,所以确认号要写成 x+1、y+1。序列号让双方能检测丢失、重复与迟到的旧包。

三次的语义:第一次 SYN 让服务器确认「客户端能发、自己该准备接收」;第二次 SYN+ACK 让客户端确认「服务器能收发」;第三次 ACK 让服务器确认「客户端确实收到了我的 SYN」。对迟到的旧 SYN,第三次确认不会凭空出现;序列号检查与状态机一起降低旧报文干扰,不是单靠第三个包防所有攻击。

选项协商:MSS(最大报文段长度)、窗口扩大因子、SACK(选择性确认)等可选项在 SYN 中交换(RFC 9293,扩展见 RFC 7323);这些选项影响之后的数据传输参数。若 SYN 或 SYN+ACK 丢失,尚未得到确认的发送方按超时重传并退避。

一次建连的抓包顺序

  1. 客户端选择初始序列号 x,发送 SYN,进入 SYN-SENT 状态;
  2. 服务器收到后回 SYN+ACK:自己的初始序列号 y,确认号 x+1;
  3. 客户端缓存 y,回复 ACK(确认号 y+1),进入 ESTABLISHED;服务器收到该 ACK 后也进入 ESTABLISHED;
  4. 连接建立完成,客户端此刻就可以发送数据,第三个 ACK 也可以捎带首段数据(例如 TLS 的 ClientHello);
  5. 若 SYN 或 SYN+ACK 丢失,由发送方按重传超时重发,超时时间指数退避(RFC 6298)。

客户端连接服务器超时的现场:抓包只看到 SYN 在重复、没有 SYN+ACK:说明被防火墙丢弃或服务器没有监听;有 SYN+ACK 但服务器未见最后的 ACK:还要排查回程丢包、防火墙或客户端异常。长连接比每次新建省一次握手的往返,这也是游戏登录后保持连接的原因之一。

易错点

  • 「三次握手的目的是防 SYN Flood」不对:SYN Flood 是利用半连接状态的攻击,三次握手是正常建连机制;SYN cookies 才是防护手段;
  • 「前两个包永远不带数据」不严格:第三个 ACK 可以捎带数据;开启 TCP Fast Open(RFC 7413)时 SYN 也可能携带数据;
  • 「握手一定是 1.5 个 RTT 的固定成本」随实现与选项变化;TFO、0-RTT 类机制会减少往返;
  • 「初始序列号必须随机」是安全建议(RFC 6528):随机的 ISN 增加序列号猜测难度,但这不是三次握手的目的表述。

适用边界:三次握手与状态机在 RFC 9293 中定义;TFO、多路径(MPTCP)、选项的部署支持度属于实现/版本相关,面试中先说清标准流程,再补充扩展的存在。

与已有资料的关系:秋招学习笔记 2 有挥手示意图,本题与第 8 题配套;C++八股.md 的三次握手答案是结论式的,本文补「为什么是三次」与选项协商。

通用原则:握手解决的是「双方状态同步」问题:任何两边各自初始化状态、又需要对方确认的协议,基本都要一次异步往返确认。把这条抽象出来,就能理解 TLS、QUIC 握手为什么各有版本差异。

TCP 关闭连接为什么是四次挥手?TIME_WAIT 属于谁?

一句话回答:断开是全双工的:主动方 FIN → 被动方 ACK → 被动方数据发完后 FIN → 主动方 ACK;在正常的顺序关闭中,先发 FIN 的一方进入 TIME_WAIT,等待 2MSL(同时关闭时双方都可能进入),让本连接四元组上的迟到报文段过期,同时保留重发最后 ACK 的机会。

连接的两个方向独立关闭:一方发送 FIN 只表示「我没有数据要发了」,对方 ACK 之后仍可以继续发送数据(半关闭状态),所以通常需要四个报文段。FIN 与 SYN 一样消耗一个序列号。

TIME_WAIT 的归属:通常是先发 FIN 的一侧;同时关闭是例外。等待 2MSL(最大报文段生命周期的两倍)有两个作用:一是让本连接上迟到的报文段在网络中过期,避免被相同四元组的新连接误收;二是如果最后的 ACK 丢失,被动方会重传 FIN,主动方此时还能重发 ACK,避免对方卡死。

异常关闭用 RST(重置):端口无监听、协议错误或应用强制关闭时会收到 RST,不走四次挥手。同时关闭(双方几乎同时发 FIN)时双方都会经历 TIME_WAIT。

主动关闭一侧的挥手时序

  1. 客户端发送 FIN(我发送完了),进入 FIN_WAIT_1;
  2. 服务器收到后回 ACK(知道你要关了),自己仍可继续发送剩余数据;
  3. 服务器数据发完,发送自己的 FIN,进入 LAST_ACK;
  4. 客户端回 ACK 并进入 TIME_WAIT,等待 2MSL;
  5. 2MSL 超时后连接状态彻底释放。若最后这个 ACK 丢失,服务器重传 FIN,客户端在 TIME_WAIT 内重发 ACK。

连接大量堆积时的排查:高并发服务端若经常主动关闭连接,会积累大量 TIME_WAIT 连接状态(资源压力取决于四元组复用与系统实现),应检查连接复用、关闭策略与端口/内存消耗,而非强制让客户端先关:HTTP 并无固定由客户端关闭的规定;CLOSE_WAIT 堆积常表示本端已收到 FIN,但应用迟迟没有结束本方向并关闭套接字。两种状态指向的问题完全不同,排查时先分清。

易错点

  • 「TIME_WAIT 是服务器才有的状态」错误:它属于主动关闭方,客户端主动关时就是客户端 TIME_WAIT;
  • 「TIME_WAIT 是在等对方关闭」错误:等的是迟到报文过期与 ACK 重发机会,对方在收到 ACK 后已经释放;
  • 「CLOSE_WAIT 是网络问题」错:通常是应用层忘了关闭套接字或读取循环没退出;
  • 「每个连接关闭必须恰好四个包」:ACK 可以捎带数据、FIN 也可能与数据合并,抓到的包数受具体时序影响;
  • 「2MSL 就是固定两分钟」:MSL 的取值与内核实现相关,内核参数(如 tcp_tw_reuse 类开关)是平台细节,不要背成协议规定。

适用边界:状态机与 TIME_WAIT 的语义在 RFC 9293 定义;MSL 具体数值、端口复用开关、连接池与 keep-alive 实践属于实现与部署层面。

与已有资料的关系:秋招学习笔记 2 已有挥手图;本题补「TIME_WAIT 归属、RST、同时关闭」这些边界,CLOSE_WAIT 与文件描述符清理可以结合《操作系统基础面试精讲》的文件与 I/O 部分理解。

通用原则:关闭是双向且不对称的:只要两方都可能有在途数据,退出就需要分别确认;收发方向与套接字资源要分别管理,收到对端 FIN 后本端应用仍须及时完成关闭。排查连接堆积先问「谁先发 FIN、应用是否完成关闭」,再谈内核参数。

TCP 怎么保证可靠传输?序列号、ACK 和重传是什么关系?

一句话回答:TCP 给每个字节编号,接收方用累积确认告诉发送方「期望的下一个字节」;发送方按 RTT(往返时延)测量动态估算重传超时(RTO)等待确认,超时或收到足够重复 ACK 时重传缺失数据;重传保证的是字节流的完整,代价是延迟毛刺。

累积确认:ack=N 表示 N 之前的所有字节都已收到。接收端可缓存窗口内的乱序段,缺失字节补齐后才能有序交付应用(是否缓存乱序数据依实现);接收窗口用于限制可接收的序号范围及缓冲占用,不能把它当成区分新旧连接或防攻击的唯一机制。

超时重传:RTO 由平滑的往返时间估计(SRTT)与往返时延变化估计(RTTVAR)动态计算(RFC 6298),该规范建议初始 RTO 为 1 秒(具体实现可更保守);连续超时会指数退避。超时太短会误重传、太长则恢复慢,所以测量比固定值重要。

快速重传与选择性确认:经典快速重传规则在收到第 3 个重复 ACK 时重传缺失段(RFC 5681);实现还可能采用其他丢包检测策略。协商 SACK(RFC 2018)后,接收端可报告已收到的非连续数据块,发送端据此更精准地补洞;校验和检出的损坏段会被丢弃,后续靠确认与重传恢复。

一个段丢失后的恢复过程

  1. 为便于示意,把等长数据段编号为 1~5(下述 ack=N 表示期望第 N 段;实际 TCP ACK 按字节序号计);发送方连续发出这 5 段;
  2. 第 2 段丢失,接收方依次收到 3、4、5 号段后各回一条重复 ACK(ack=2),共 3 条;
  3. 发送方收到第 3 个重复 ACK,触发快速重传第 2 段;
  4. 接收方收到后回 ACK(ack=6),累积确认一次覆盖第 2–5 段;
  5. 若条件不满足(重排干扰、重复 ACK 不足),则等 RTO 超时后重传;
  6. 应用看到的现象是「第 2 段数据晚到」——业务若不能接受这种等待,就不该把实时消息压在单一 TCP 流上。

丢包对手感的影响:一局对战中,交互命令走 TCP 时一次丢包会让后续命令全部等待重传(队头阻塞),操作出现卡顿尖峰;这也是实时状态同步另走 UDP 通道的原因:重传还是丢弃,由应用按消息语义决定(第 19 题)。

易错点

  • 「ACK 是逐段确认」错误:是累积确认,一个 ACK 可以确认多个段;
  • 「重传由应用负责」错误:TCP 内建重传,应用只面对连接存活与否;
  • 「用 ping 的 RTT 当 RTO」不准确:ping 测的是 ICMP 往返且可能被设备区别对待,TCP 有自己的 RTT 估计;
  • 「重传一定能送达」错误:重传多次失败后连接会复位/超时,应用必须处理连接级失败并重连;
  • 「SACK 总是可用」:需要双方在握手中协商,不支持时退化为累积确认。

适用边界:重传与拥塞细节属于实现层面:RFC 5681 给出推荐算法,RFC 6298 规定 RTO 计算;不同内核/版本的实际算法不同(Reno、CUBIC、BBR 等),不要把某一种当成 TCP 的定义。

与已有资料的关系:C++八股.md 的「可靠性」结论在本题展开为过程;自研可靠层(第 19 题)就是把这套机制按业务裁剪后的版本。

通用原则:可靠传输 = 编号 + 确认 + 超时/快速重传 + 去重与窗口约束的组合。任何自研可靠层都绕不开这四件套,区别只在于策略(等待多久、什么可以放弃)。

TCP 是字节流:粘包和拆包的本质是什么?怎么分帧?

一句话回答:TCP 只保证字节的顺序,不保留应用消息边界:一次发送可能被拆成多段到达,多次发送也可能被合并收到,这就是「粘包/拆包」现象;解决方法是应用层自己定义分帧规则——长度前缀、分隔符或固定长度,并按「攒够一条再交付」的状态机读写。

根本原因:TCP 段的大小由 MSS、拥塞窗口、发送缓冲与调度决定,与应用的发送调用次数没有对应关系;接收端一次读取能拿到多少字节也取决于接收缓冲与实现。所以「一次 write 对应一次 read」在 TCP 上从来不成立。

三种惯用分帧:① 长度前缀——头部固定字节数写消息长度,最通用(自研游戏协议常用);② 特殊分隔符——如换行、0 字节,需要处理内容中出现分隔符的转义或限制;③ 固定长度 / 固定头 + 类型字段——协议头自带长度或类型,适合定长小消息。

接收端要做半包处理:读缓冲区可能含「半个头」「一条半消息」等状态,必须循环攒够再解析;同时要校验长度上限,防止对端伪造超大长度造成内存耗尽(DoS)。UDP 数据报本身有边界,但同样可能丢/乱序/重复。

长度前缀协议的接收循环

  1. 把本次读到的字节追加到接收缓冲区;
  2. 缓冲不足 4 字节(头部长度)就继续读;
  3. 解析长度字段 L,并校验不超过协议上限(防止恶意大包);
  4. 缓冲不足 4+L 字节就继续读;
  5. 取出一条完整消息交给业务层,并从缓冲区中删除;
  6. 回到第 2 步继续处理剩余字节——一次读取可能包含多条消息或半条消息。

自研游戏协议的常见形态:protobuf 等消息体外面通常就是「长度前缀」;调试「消息断裂/丢消息」时先看接收代码是否把一次 recv 当成一条消息;编辑器与插件进程之间用本地套接字通信时同样要分帧——分帧是协议设计问题,不是某条网络路径的问题。

易错点

  • 「write 一次对应 read 一次」错,这是分帧问题的根源;
  • 「TCP 会按 send 的边界送达」错——那是 UDP 数据报的语义;
  • 「加了分隔符就万无一失」不成立:消息内容若可能包含分隔符,必须转义或改用长度前缀;
  • 分帧必须有长度上限与状态检查,否则恶意/损坏长度会导致内存问题;
  • 「UDP 不需要分帧」——传输层边界保留,但应用最好按 MTU 约束控制单包大小(第 12 题),并且仍要处理乱序与重复。

适用边界:分帧属于应用协议设计;TCP 不提供消息边界(RFC 9293)。高层协议自带帧格式:WebSocket(RFC 6455)、HTTP/2(RFC 9113)、QUIC(RFC 9000)都是在自己的层次里定义了帧,思想与本题一致。

与已有资料的关系:C++八股.md 有「粘包」结论题,本文补接收循环与长度校验这些落地细节;第 19 题的自研协议会直接复用本题的分帧方式。

通用原则:流式接口必须由协议设计者定义「边界」与「接收状态机」:只有收到完整一条才交付,剩余字节留待下次。凡是 read/recv,都要假设出现短读、多读、半包。

流量控制和拥塞控制分别在保护谁?

一句话回答:流量控制保护接收方缓冲:接收方通过 rwnd(接收窗口)字段通告「我还能收多少字节」;拥塞控制保护网络:发送方用 cwnd(拥塞窗口)估计路径容量,丢包/拥塞信号出现时收缩;允许在途数据量通常受 min(rwnd, cwnd) 限制;已发未确认字节也占用窗口。

接收窗口(rwnd):TCP 头中的窗口字段(配合窗口扩大选项突破 64KB 限制,RFC 7323)由接收方随 ACK 通告,表示经过自己缓冲后还能接收多少数据。窗口为 0 时发送方暂停正常数据发送,并周期发送零窗口探测避免双方死等;接收方消费数据后会通告新的窗口。

拥塞窗口(cwnd):发送方维持的估计值,不随报文传输。以经典 Reno 为例,慢启动阶段随新数据被确认而增大 cwnd(近似每 RTT 翻倍),进入拥塞避免后增长放缓;检测到丢包时调整门限/窗口并配合重传。CUBIC、BBR 等算法的增长与丢包反应并不相同。发送速率由 ACK 的到达节奏「自计时」,这就是为什么 TCP 在高 RTT 链路上吞吐受限。

两者是不同机制、不同保护对象:rwnd 由接收端声明、针对接收缓冲;cwnd 由发送端维护、针对网络路径。常见的说法「滑动窗口」只是发送/接收缓冲用到的数据结构,rwnd 与 cwnd 两个限制都叠加在它上面。

维度流量控制(rwnd)拥塞控制(cwnd)
保护对象接收方缓冲区网络路径(队列/链路)
信息来源接收方在 TCP 头中通告发送方自己测量(ACK 节奏、丢包信号)
失效后果接收方丢数据、缓冲溢出网络过载、路由器排队丢包
调整方式按接收缓冲余量增减慢启动快速增长、遇拥塞收缩
实现位置协议字段 + 双方配合发送端算法(实现相关)

一个发送窗口的完整时序

  1. 连接建立后,发送方按所用拥塞算法与实现配置的初始 cwnd 开始发送(如部分实现采用 10 个 MSS);
  2. 以 Reno 为例,慢启动时随新数据被确认而增大 cwnd,约每 RTT 翻倍;
  3. 按经典快速恢复规则,第 3 个重复 ACK → 重传缺失段,调整慢启动门限(约减半);
  4. Reno 的拥塞避免:每个 RTT 约增加 1 个 MSS;其他算法增长规则不同;
  5. 接收方缓冲变满 → 通告的 rwnd 变小甚至为 0,发送方停发并探测;
  6. 接收方消费数据后通告更大窗口,发送恢复——瓶颈可能是两条限制中的任意一条。

「大丢包之后变慢」是正常行为:下发大体积对局数据时一次丢包会让 cwnd 收缩,之后吞吐缓慢恢复;服务器发送速率上不去也可能是客户端处理不过来导致 rwnd 变小。排查时先分清瓶颈在接收端消费速度(rwnd)还是路径容量(cwnd、RTT、丢包率),再决定优化方向。

易错点

  • 把流量控制与拥塞控制混为一谈是本题最典型的错误:一个保护接收方、一个保护网络;
  • 「窗口由发送方单方面决定」错误:rwnd 是接收方声明的,发送方用 min(rwnd, cwnd) 约束在途数据;
  • 「丢包一定是网络故障」:拥塞控制把丢包当作拥塞信号本身就是设计行为;
  • 「TCP 会自己占满带宽」:受 RTT、丢包率与窗口恢复速度限制;长肥管道(高延迟高带宽)需要窗口扩大与适配算法;
  • 「收到零窗口就断开」错误:用零窗口探测维持连接。

适用边界:流控语义在 RFC 9293,窗口扩大在 RFC 7323;拥塞控制有规范约束及示例算法(RFC 5681),具体发送端算法可变;实际内核可能使用 CUBIC(RFC 9438)或 BBR 等,算法会随版本演进。

与已有资料的关系:与 C++八股.md 的「流控/拥塞」结论相比,本文补上窗口字段与自计时机制;第 17 题的延迟抖动来源可以从本题的排队因果回溯。

通用原则:在途数据受较小窗口约束,吞吐还取决于 RTT、链路带宽与丢包。优化网络性能先找瓶颈:接收端消费速度、路径容量、还是往返时延,分别对应 rwnd、cwnd、RTT 三个旋钮,而不是笼统地说「网络慢」。

MTU、MSS 和 IP 分片是什么关系?

一句话回答:MTU(最大传输单元)是链路可承载的最大网络层包大小(以太网常见 1500 字节);IPv4 在未设置 DF 时可由路由器分片,IPv6 仅源端可分片;TCP 两端用 MSS(最大报文段长度)选项各自通告愿意接收的 TCP 载荷上限,发送端还须按路径 MTU(PMTU)调整封包,尽量避免 IP 分片。

MTU 属于链路层:以太网链路常见的 1500 字节指的是它可以承载的 IP 包(含 IP 头)上限;Wi-Fi、VPN、PPPoE 拨号、部分移动网络的 MTU 可能不同,隧道等封装会压缩可用包大小。IP 包超过某条链路的 MTU 时,IPv4 路由器在允许时可分片,否则丢包并通告;IPv6 路由器不分片,会丢弃超大包并发送 ICMPv6「包太大」消息。

MSS 是 TCP 载荷上限,由双方通过 SYN 选项分别通告各自可接收的值:典型以太网 + IPv4 场景下为 1460 = 1500 − 20(IP 头) − 20(TCP 头),IPv6 基本头 40 字节;TCP 选项与隧道封装还可能占用包预算,实际单段可用载荷要考虑路径 MTU。MSS 是接收端在 SYN 中通告的上限,发送端另受 PMTU 约束,不能写成固定的 MTU−40。

IPv4 允许路由器分片,分片用标识与片偏移表达,目的端负责重组;DF(不分片)位用于主动发现路径 MTU(PMTUD):路由器遇到放不下又不能分片的包时回 ICMP「需要分片」消息告知可用 MTU。ICMP 常被防火墙屏蔽,于是就出现「小包通、大包不通」的 PMTUD 黑洞。IPv6 只允许源端分片,路由器对超大包丢弃并回 ICMPv6「包太大」。

一次路径 MTU 发现(PMTUD)的过程

  1. IPv4 发送方按已知 PMTU 组包并设 DF=1;IPv6 没有 DF 位,由源端控制分片;
  2. 中间链路 MTU 更小,当前包转发不出去;
  3. 路由器回 ICMP 错误(IPv4「需要分片」/ IPv6「包太大」),携带可用 MTU 值;
  4. 发送方降低封包大小重发,之后按新值发送;
  5. 路径可能变化,需要定期重新探测;
  6. 若 ICMP 被屏蔽:表现为大包丢失、小包正常,需要 MSS 钳制或应用层保守包大小兜底。
名称所属层含义与行为
MTU链路层单次能承载的最大 IP 包;超出则分片或丢弃
MSSTCP 层接收方在 SYN 中通告的 TCP 载荷上限;发送方还受 PMTU 约束
IP 分片网络层IPv4 允许路由器分片(DF 可禁),IPv6 仅源端可分片
UDP 数据报大小传输层超 MTU 时靠 IP 分片或应用自行切分

「小包都通、大包必丢」:游戏客户端可尝试把 UDP 载荷控制在约 1200 字节并留出封装余量,以降低分片风险;但这不是所有路径的安全上限,仍需按实际路径探测与回退;大量技能特效或大消息导致「平时正常、一放大招就断线」,可能是 PMTUD 黑洞、大包分片丢失,也可能是带宽拥塞或业务处理问题——此时应该由应用控制包大小,而不是指望网络替应用分片。

易错点

  • 把「MSS = MTU − 40」当常数错误:要按实际头部计算(IPv6、选项、隧道封装不同);
  • 「IPv4 和 IPv6 分片行为一样」错误:IPv6 路由器会丢弃超大包并回 ICMPv6,源端负责调整封包或按需分片;
  • 「分片是 TCP 的事」错误:分片发生在 IP 层,TCP 只是用 MSS 尽量避免它;
  • 「PMTUD 总是可靠」:ICMP 常被策略屏蔽,需要兜底(MSS 钳制、保守包大小);
  • 「MTU 1500 无处不在」:VPN、PPPoE、蜂窝网络可能更小,跨网络功能要按最小值设计。

适用边界:具体 MTU 取决于链路技术与部署(以太网、Wi-Fi、隧道、载波网络);RFC 8200 规定 IPv6 路由器不得分片;MSS 钳制是中间设备的部署手段,不属于协议语义。UDP 应用可参考 QUIC 为初始报文选用约 1200 字节的保守载荷,但实际大小仍需结合路径 MTU、封装开销与探测结果,不能保证所有路径都可达。

与已有资料的关系:与第 10 题(分帧与长度上限)、第四章的包大小设计衔接;C++八股.md 未单独展开 MTU/MSS,属增量。

通用原则:每层都有自己的「最大尺寸」约束,协议设计者必须主动选择在哪一层打包、留多少余量。实时业务宜主动控制单包大小,并按实际路径探测与分片风险设计兜底,而不是假设 IP 分片总能成功。

应用协议与安全

HTTP 请求响应长什么样?1.1 的连接复用解决什么问题?

一句话回答:HTTP 是应用层的请求-响应协议,语义(方法、状态码、头字段、缓存规则)由 RFC 9110 定义;HTTP/1.1 默认使用持久连接,可以在同一条 TCP 连接上依次发送多个请求响应,省去反复握手;在同一连接上使用流水线发送多个请求时,响应仍须按序返回,慢响应会挡住后续响应;不使用流水线时通常先完成一个请求再发下一个。

报文结构:请求行(方法 + 资源路径 + 版本)或状态行(版本 + 状态码)、若干头字段、可选的体。方法语义中重要的是「安全」(不改变服务器状态)与「幂等」(重复执行结果一致):GET/PUT/DELETE 按规范语义是幂等的,POST 不保证幂等;具体接口若违背这些语义仍须另行核实。失败后能否重试还要考虑请求是否已被执行、响应是否丢失及业务副作用(RFC 9110)。状态码按 1xx/2xx/3xx/4xx/5xx 分类。

HTTP/1.1 的持久连接默认开启(RFC 9112):连接建立后不随一次请求结束而关闭,可以复用给后续请求;相比每次新建,省掉 TCP 三次握手(以及 HTTPS 的 TLS 握手)往返。浏览器还会对同一站点开多条并行连接来绕开串行等待。

应用层队头阻塞:使用 HTTP/1.1 流水线时,一个连接上的响应必须按请求顺序返回,前面处理慢会挡住后续响应;不使用流水线时该连接通常串行等待。流水线(pipelining)在规范里有定义,但因兼容性与按序响应带来的队头阻塞等问题在浏览器中很少使用——真正的解法是 HTTP/2 的多路复用(第 14 题)。

客户端一次 API 调用的往返成本

  1. 域名解析(可能命中缓存);
  2. TCP 三次握手(复用已有连接则跳过);
  3. TLS 握手(HTTPS:首次连接 1 个 RTT,会话恢复可减少;复用则跳过);
  4. 发送请求行、头字段与消息体;
  5. 服务器处理并返回响应;
  6. 连接保持打开(Keep-Alive),供后续请求复用;
  7. 空闲超时或达到最大请求数后,由一方发起关闭(挥手可能发生在任一侧)。

游戏客户端启动阶段:启动时要拉配置、公告、排行榜等多个接口:复用同一条 HTTPS 连接比每次新建省下多组 TCP+TLS 往返;但若将慢接口与大文件串行安排在同一条 1.1 连接上,后面的请求会被堵住——合理使用多连接并发,就是在规避应用层队头阻塞。

易错点

  • 「HTTP 无状态」说的是协议不保存会话,Cookie/Token 是应用机制,不代表服务器实现无状态;
  • 「GET 不能带请求体」:规范未禁止但语义与实现有歧义,不要依赖;
  • 「幂等 = 安全」混淆:安全方法一定幂等,幂等方法不一定安全(DELETE 幂等但会改状态);
  • 「HTTP 库会自动重试失败请求」是实现行为:POST 重试可能造成重复提交,需要服务端幂等键兜底;
  • 连接复用不等于无限:连接有超时、服务器有最大并发与请求数限制,长时间空闲会被任一侧关闭。

适用边界:语义与消息语法见 RFC 9110 / RFC 9112,缓存行为见 RFC 9111;HTTP/2 与 HTTP/3 的连接复用模型完全不同(第 14、16 题)。服务器的超时、连接数等配置属于实现细节。

与已有资料的关系:C++八股.md 有 HTTP1.1/2/3 对比的结论;本文按版本拆成三题(13/14/16)分别展开。客户端 HTTP 库的使用经验可在复盘时回填到本题的步骤说明。

通用原则:应用的每一次「请求」背后都可能有 DNS、TCP、TLS、业务处理多段成本。优化网络性能的第一步是数清楚现在花了几次往返,再决定是复用、预连接还是升级协议版本。

HTTP/2 多路复用解决了什么,又没解决什么?

一句话回答:HTTP/2 在一条 TCP 连接上把消息拆成二进制帧、用流(stream)承载多个并发的请求响应,解除了 HTTP/1.1 的应用层队头阻塞,并用 HPACK 压缩头部;但它仍跑在 TCP 上——底层丢包时整条连接的所有流一起等待重传,TCP 本就有的传输层队头阻塞仍然存在。

帧与流:HTTP/2 把请求、响应、头部、控制信息拆成带流 ID 的二进制帧,不同的流可以在同一连接上交错传输、乱序完成(RFC 9113);协议自带流量控制与优先级机制(优先级语义后来由 RFC 9218 重新定义)。HPACK(HTTP/2 头字段压缩)可减少重复头字段的传输开销。

相比 HTTP/1.1 的收益:同一服务通常可复用较少的连接(减少握手、避免多连接竞争),并发响应不再被请求顺序强制阻塞,头部开销降低,移动网络下更省流量。

未解决的部分:TCP 是有序的字节流,一旦一个段丢失,后续所有字节都要等重传——HTTP/2 的所有流都共享这条 TCP 连接,因此丢包会把全部并发请求一起拖慢。这就是传输层队头阻塞,也是 HTTP/3(第 16 题)要解决的核心问题。

两个请求在 HTTP/2 上并发的时序

  1. 连接建立后,客户端发送流 1(HEADERS)与流 3(HEADERS);
  2. 服务器并发处理:流 3 先完成,直接发流 3 的 DATA 帧(END_STREAM);
  3. 流 1 的响应迟到,但不会被流 3 挡住——帧在同一连接上交错传输;
  4. 若底层发生丢包,TCP 必须先重传缺失字节,流 1 与流 3 的后续帧一起被延后;
  5. 丢包恢复后两流继续——应用层的顺序阻塞消失了,传输层的耦合仍在。
版本连接复用方式消息边界队头阻塞
HTTP/1.1同连接串行,靠多连接并行应用自行解析(无帧)应用层按序阻塞
HTTP/2单连接多路复用(帧/流)协议帧层定义应用层解除,TCP 层仍在
HTTP/3QUIC 连接上多路复用QUIC 流承载 HTTP 帧丢包不会强制阻塞其他流的有序交付,仍共享拥塞预算

弱网下拉取多个资源:客户端同时请求头像、榜单、配置等多个小资源时,HTTP/2 把连接数收敛到一两条、头部压缩省流量;但移动弱网丢包时,如果把「实时小请求」和「大文件下载」放在同一条 H2 连接上,会互相干扰——按业务拆分连接,或对弱网场景下沉到 HTTP/3。

易错点

  • 「HTTP/2 一条连接一定更快」不成立:丢包时全连接的流共命运,慢速大流可能拖累小请求;
  • 「多路复用等于不排队」错误:服务器处理、内核缓冲仍可能排队,只是不再按顺序完成;
  • 「明文 h2c 随处可用」:HTTP/2 明文的协商与部署支持不如 TLS 之上的 ALPN 普遍;
  • 「服务器推送(Push)是标配」:实际很少使用,现代替代是预加载提示(103 Early Hints / Link);
  • 「HTTP/2 必须使用 TLS」也不对:规范允许明文 h2c,但浏览器场景通常通过 TCP 上的 TLS 协商 h2;QUIC 承载的是 HTTP/3。

适用边界:HTTP/2 规范是 RFC 9113(HPACK 见 RFC 7541);多路复用是协议层事实,性能表现依赖实现与网络条件;协商方式(ALPN)与浏览器/服务器行为属于部署层面。

与已有资料的关系:与第 13 题(1.1 复用)和第 16 题(HTTP/3)构成版本演进链;C++八股.md 的三版本对比在本文被拆细成机制。

通用原则:多路复用是把多个逻辑通道叠到一个物理通道上:收益是复用与调度,代价是共享层的故障会耦合所有通道。评估任何复用方案,先问「底层一旦阻塞,损失的粒度是什么」。

HTTPS 和 TLS 各做什么?握手时到底发生了什么?

一句话回答:TLS 在传输层之上提供机密性、完整性及对证书身份的验证机制;HTTPS 通常是 HTTP over TLS(HTTP/3 则把 TLS 1.3 集成于 QUIC)。TLS 1.3 中基于证书的常规 (EC)DHE 握手通常用一次往返完成(具备条件的会话恢复可发送 0-RTT 早期数据):客户端发 ClientHello,服务器返回密钥共享信息及证书,客户端验证证书链与主机名后完成握手。若跳过校验,加密通道也可能通向冒名服务器。

TLS 的三项保证:机密性(协商会话密钥后用对称加密保护应用数据)、完整性(认证加密防篡改)、身份认证(典型 HTTPS 客户端核对服务器 X.509 证书链、信任库、有效期与访问主机名;SNI 告知服务器所访问域名,但不是身份校验)。

以证书认证的 TLS 1.3 握手为例(RFC 8446):客户端 ClientHello 携带支持的版本、密码套件与密钥共享(key_share);服务器回 ServerHello 与密钥材料,随后把证书、CertificateVerify(证明持有私钥)、Finished 加密发送;客户端验证证书与 Finished 后发送自己的 Finished,之后才是应用数据。常规 (EC)DHE 握手的密钥由共享秘密经 HKDF 派生,提供前向保密;TLS 1.3 也允许纯 PSK 恢复,该模式不具备同样的前向保密;RSA 密钥传输等旧方式已被移除。

会话恢复与 0-RTT:用会话票据恢复时,在服务器支持且接受 0-RTT 时,客户端可在第一个飞行中携带早期数据,但它可被重放,只适合能容忍重放的操作,不能仅凭 HTTP 方法幂等就断定安全。ALPN(应用层协议协商)可在 TCP+TLS 中协商 http/1.1、h2,在 QUIC 握手中协商 h3。DTLS(数据报传输层安全协议,1.3 见 RFC 9147)提供数据报上的 TLS 风格安全;WebRTC 媒体通常由 DTLS 协商密钥,再用 SRTP 保护,实际 DTLS 版本依部署而定。

首次 HTTPS 请求的典型证书握手时序(TLS 1.3)

  1. TCP 三次握手完成(HTTP/3 场景则内置在 QUIC 握手中);
  2. 客户端发送 ClientHello:版本、密码套件列表、key_share、SNI;
  3. 服务器回 ServerHello + key_share,随后加密发送证书链、CertificateVerify 与 Finished;
  4. 客户端验证证书(信任链、主机名、有效期),计算共享密钥,发送 Finished;
  5. 双方用会话密钥加密应用数据——HTTP 请求此刻才真正发出;
  6. 复用场景下(会话票据恢复)握手可缩短,0-RTT 时请求提前随早期数据发出,需要防重放。

客户端下载器的证书校验:游戏客户端的资源下载走 HTTPS 时,若为了省事跳过证书校验(接受任意证书),上线后可能被中间人劫持下发恶意资源;正确做法是使用严格校验并对自签/内网证书做显式配置,开发调试用开关与生产模式分离——遇到证书校验失败应拒绝连接并排查证书、主机名及系统时间,不能以「环境问题」为由忽略。

易错点

  • 「HTTPS 加密了,数据一定安全」错误:它保护的是传输到服务器的通道;服务器被攻破、业务日志泄漏、代理处解密都不在其职责内;
  • 「TLS 只负责加密不负责身份」错误:HTTPS 客户端须校验证书;跳过(或信任任意证书)等于放弃服务器身份认证;
  • 「HTTPS 一定很慢」是过度简化:TLS 1.3 常规握手通常需 1 个 RTT,加密成本也依设备、负载与配置而变;
  • 「还能继续用 TLS 1.0/1.1」:已被主流弃用,应至少允许 1.2、优先 1.3;
  • 「抓包抓不到就安全」:调试/逆向场景可能安装根证书实施中间人,客户端的校验实现要能识别异常。

适用边界:TLS 1.3 规范是 RFC 8446;证书信任模型与根证书列表由操作系统/运行时与组织策略决定;HTTPS 的 URL 语义与默认端口属 HTTP over TLS 的约定(RFC 9110);QUIC 场景下 TLS 内嵌于 QUIC(RFC 9001),不是「先 TLS 再 QUIC」两段式。

与已有资料的关系:秋招实习项目.md 中有 TLS 快照相关条目,与本题互补;C++八股.md 的 HTTPS 结论在本题展开为握手时序与校验条件。

通用原则:安全设计的默认假设是「通道会被人看着」:加密解决传输机密性,认证解决对象真实性,两者缺一不可;任何「先跳过校验、以后再补」的做法都是把风险留给线上。

HTTP/3 为什么跑在 QUIC/UDP 上?

一句话回答:HTTP/3 把 HTTP 的帧映射到 QUIC(基于 UDP 的传输协议)之上:QUIC 流分别可靠、有序交付,一条流缺字节不会强制阻塞其他流的有序交付(拥塞预算仍共享),避免 TCP 字节流跨流的队头阻塞;QUIC 内建 TLS 1.3 与连接迁移,通过连接 ID 支持网络路径变化时识别连接(能否迁移仍受连接参数与路径校验限制)。它不是「HTTP/2 的加速版」,而是把传输能力重做了一遍。

QUIC(RFC 9000)常在用户态实现传输:每条流有自己的字节偏移与有序交付,丢失的流数据可在新包中重传;包号按连接的包号空间管理,并非每条流各有一套包号。其他流不因该流缺字节而被强制阻塞,但仍共享拥塞窗口、连接级流控等资源。QUIC 使用 UDP 便于部署在现有网络中,UDP 也可能被企业网络屏蔽。

与 TLS 的集成:QUIC 握手内嵌 TLS 1.3(RFC 9001),初始包就携带客户端密钥材料,通常一次往返完成握手与参数协商;0-RTT 早期数据同样支持(有重放风险)。连接迁移:在使用连接 ID、双方允许迁移并完成路径验证的条件下,客户端从 Wi-Fi 切换到蜂窝网时即使 IP/端口变化也可能延续连接,而不是无条件保证不断线。

部署现实:部分企业网络与代理屏蔽 UDP,需要回退到 HTTP/2;HTTP/3 对客户端实现、内核旁路与拥塞算法有额外要求。HTTP 语义本身不变(RFC 9110),变化的是承载方式(映射见 RFC 9114)。

QUIC 连接建立与两条流并发

  1. 客户端发送初始包,其中携带 TLS ClientHello 与 QUIC 传输参数(握手与加密并行进行);
  2. 服务器回应初始包与握手包(含证书等),通常 1 个 RTT 内双方密钥就绪;
  3. 客户端在流 A、B 上发送请求数据帧;
  4. 流 A 的数据丢失时重传其缺失字节,流 B 的有序交付不必等待流 A,但共享拥塞控制可能延缓发送;
  5. 网络切换(Wi-Fi → 蜂窝)且双方支持迁移时,经路径验证后可使用连接 ID 延续会话;
  6. 若 UDP 被封禁,客户端按能力协商回退到 HTTP/2。

移动端的资源热更新:移动游戏下载热更资源、公告时,HTTP/3 在某些丢包或网络切换条件下可能减少跨请求阻塞;是否降低抖动需要实际测量;某些实时对局数据不走 HTTP,而采用专门的 UDP 协议(第 18、19 题)——H3 主要服务于资源分发等 HTTP 场景,具体选型依业务。

易错点

  • 「HTTP/3 = HTTP/2 + UDP 加速」错误:它把可靠性、流控、加密重新设计,流级独立重传是本质区别;
  • 「QUIC 不受丢包影响」:流内仍会等待重传,只是不再拖累其他流;
  • 「用了 HTTP/3 就一定更快」:高带宽低丢包的网络差距很小,优势在弱网、丢包与网络切换场景;
  • 「HTTP/3 不需要 TLS」错误:QUIC 强制使用 TLS 1.3,且集成在握手内;
  • 「QUIC 就是游戏 UDP 协议」:思想相近(用户态传输、按需可靠性),但 QUIC 是标准化通用传输,游戏协议按其业务定制。

适用边界:HTTP/3 语义复用 RFC 9110、映射定义在 RFC 9114;QUIC 传输细节在 RFC 9000/9001/9002。可用性取决于客户端库与服务端支持以及网络对 UDP 的放行策略。

与已有资料的关系:与第 14 题(H2 的 TCP 层队头阻塞)、第 18–19 题(实时 UDP 与自研可靠性)形成「在 UDP 上重建传输」的三种例子:QUIC、自研游戏协议、WebRTC 数据通道。

通用原则:当现有传输层语义不符合需求时,业界的选择是在 UDP 上重建一层可裁剪的传输:QUIC 把 TCP 的核心能力搬到用户态并按流拆分。衡量这类方案先看它把故障粒度缩小到了什么级别。

游戏客户端实时网络

延迟、抖动、丢包分别影响什么?怎么度量?

一句话回答:单向延迟是数据从发出到收到的时延;RTT 是往返时间、易测但不能直接等同单向延迟。抖动是时延变化,丢包是数据未能到达;延迟让操作「慢半拍」,抖动与丢包造成卡顿和瞬移。处理手段是预测、插值与平滑,设计时要按最差分位数(P95/P99)而不是平均值来考虑体验。

RTT 的构成:传播时延(物理距离与介质决定,不可压缩)、排队时延(路由/交换设备的缓冲,拥塞时增大)、处理时延与序列化时延(包大小除以链路速率)。抖动就是这些分量随时间波动,常用均值加方差或分位数衡量。

丢包的来源:拥塞导致的队列溢出、无线链路误码、中间设备策略。丢包对不同协议的影响不同:对 TCP 命令流引发重传等待(第 9 题),对独立完整的 UDP 状态快照可等下一帧覆盖;若后续快照依赖丢失的基线,仍须等待或请求补全。

从输入到显示的完整链路:客户端采样输入 → 上行 → 服务器按 tick 模拟 → 下行快照 → 客户端插值/渲染,每一环都叠加延迟。缓解手段:客户端预测(本地先行呈现、服务器结果到达后校正)、快照插值(用额外缓冲换平滑)、抖动缓冲、丢弃过期状态。这些都是「用可接受的误差换流畅」,不是消除延迟。

一次操作从按键到显示的时序

  1. 客户端采样输入并附上序号,发往服务器;
  2. 上行时延 d1 后到达服务器;
  3. 服务器按 tick 模拟,产出快照(含该输入的结果);
  4. 下行时延 d2 后客户端收到快照;
  5. 若采用插值渲染,还要再等抖动缓冲时长才显示;
  6. 未预测的可见响应时间还包括输入采样、服务器等下一个 tick/处理、客户端渲染等成本:约为 d1 + d2 + 这些处理时间 + 插值缓冲。启用本地预测时可先显示预测结果,再按服务器快照做回滚/平滑校正。

手感问题的定位顺序:例如某些射击/动作游戏即使 RTT 在 60–100ms 仍可借助预测与插值维持体验,但并无普适可玩阈值;几十毫秒的时延波动是否明显还取决于缓冲、帧率和玩法;MOBA 的技能判定在服务器侧,客户端预测只影响表现——理解「判定权威在服务器」就能回答「为什么不能只做本地判定」。排查手感问题时先分清是网络延迟、抖动,还是本地帧率/主线程卡顿伪装成的延迟。

易错点

  • 「平均延迟低就流畅」:抖动和丢包同样致命,平均分掩盖尖峰;
  • 「ping 值就是游戏延迟」:ICMP 与业务包可能走不同路径/优先级,且不含服务器处理与插值缓冲;
  • 「丢包率高就一定卡」:少量均匀丢包对 UDP 状态流影响小;对 TCP 命令流则会被重传放大;
  • 「网络问题都是运营商」:客户端主线程卡顿、渲染掉帧、GC 停顿都会表现为操作迟滞;
  • 「延迟可以完全消除」:物理距离决定的下限不可突破,预测/插值只是掩盖感知。

适用边界:具体数值目标与玩法、区域部署、平台有关,不能套用固定阈值;ICMP 行为与设备策略因网络而异。本文只给机制与度量方法,实际指标以业务埋点测量为准。

与已有资料的关系:与第 9、11 题的排队/重传机制互为因果;复盘时遇到手感类追问,从本题的「输入到显示」链路拆解回答。

通用原则:网络质量不是一个数,而是分布:按最差分位数设计交互(超时、补偿、提示),按平均值优化带宽。设计实时功能时先明确「误差可以出现在哪里、由谁负责收敛」。

实时游戏为什么常用 UDP?什么时候必须用 TCP?

一句话回答:实时游戏常用 UDP 的目的是控制延迟而不是「UDP 更好」:状态更新可以丢旧保新,TCP 的有序可靠交付会让所有后续数据一起等待重传,死扣历史状态没有意义;但登录、聊天、交易等要求可靠有序的场景仍应使用 TCP(或可靠的传输通道)。服务端如何得知数据到达属于操作系统 I/O 模型问题,在《操作系统基础面试精讲》第 20 题展开,本文不重复。

TCP 不适合实时状态流的具体机制:队头阻塞(一个段丢失,后续字节全部等待重传)、重传不可跳过(迟到的旧状态没有价值)、丢包时拥塞窗口收缩(小流量但也减速)。UDP 的优势是把这些决策交还应用:每个数据报独立,可以自定义「哪些重传、哪些丢弃、要不要冗余」。

UDP 的代价也要说清:没有内建可靠性、顺序、流控与拥塞控制,应用须按消息需要补可靠/顺序能力,并自行限制发送速率(第 19 题);部分网络对 UDP 限制更严(企业防火墙、代理),若业务要求覆盖这些网络,须规划 TCP/WebSocket 等可用回退;HTTP/3 同样依赖 UDP。

现实做法通常是分通道:高频状态走 UDP(无可靠、新覆盖旧),命令与事件走可靠通道,资源下载走 HTTP。也可以评估 KCP、ENet、GameNetworkingSockets、WebRTC 数据通道、QUIC 等 UDP 之上的传输方案;它们提供的可靠、无序或部分可靠能力各不相同,应按消息语义选用,避免重复造轮子。

消息类型建议通道理由
位置、朝向等高频状态UDP(无可靠)旧数据无意义,丢失不重传,新快照覆盖
输入命令、技能释放按语义确认/去重,必要时重传重要事件须处理丢失与重复;并非所有连续输入都要逐包可靠
登录、聊天、商店TCP / HTTPS语义要求可靠持久,与现有基础设施兼容
资源下载、热更新HTTP/2 或 HTTP/3流量大,复用缓存与断点续传能力
心跳、延迟探测UDP 或与状态同流轻量、允许偶尔丢失

一个典型的对战客户端:例如某些对战客户端用 UDP 主通道以 20–60Hz 收发输入与快照,TCP/WebSocket 承载大厅、匹配、聊天;若事先实现了备用通道,UDP 受限时可回退 WebSocket 并调整更新频率。这套「分通道 + 回退」结构,比单纯回答「游戏都用 UDP」更能体现工程判断。

易错点

  • 「游戏必须用 UDP」:回合制、棋牌、大量 Web 游戏用 TCP/WebSocket 完全可行;
  • 「UDP 不会拥塞」:协议没有内建控制,不等于可以乱发——不限速会让丢包更严重(拥塞塌陷);
  • 「TCP 延迟一定高」:低丢包网络中 TCP 延迟也很低,差异主要在丢包/抖动场景;
  • 「UDP 穿透一定成功」:对称 NAT、企业网络可能失败(第 5 题),需要中继;
  • 「用了 UDP 就不需要重连与状态管理」:连接与状态恢复照样要自己实现(第 20 题)。

适用边界:这是工程权衡而非协议规定:哪些消息可靠、更新频率多高都是产品与玩法决策;NAT 行为、运营商策略、平台网络库限制(主机平台的网络约束)都会影响方案可行性。

与已有资料的关系:网易互娱面经 1 的「UDP 可靠性改进」与 C++八股.md 的 TCP/UDP 对比在本题与第 19 题合并展开;方法论上承接第 6 题的「按四个维度选传输」。

通用原则:传输选择按消息语义分频道:先标注每条消息的时效/可靠/有序/大小需求,再匹配通道。技术选型避免教条,并为降级留好回退路径。

怎么在 UDP 上实现「需要的那部分」可靠性?

一句话回答:在 UDP 上做可靠性是应用层协议设计:给消息编号与时间戳,用 ACK(可带位图)确认到达情况,按 RTT 估算超时重传「仍有意义」的数据,过期状态直接丢弃;关键包可用冗余/前向纠错提高到达概率。这不是 UDP 的能力,而是应用补上的传输协议——也是 QUIC、KCP 等方案的共同思想。

四个核心部件:① 序号——检测丢失、乱序、重复与回绕;② 确认——逐条、累积或位图 ACK,可捎带在数据里;③ 超时重传——按 RTT 估计自适应,避免固定值;④ 过期策略——每条消息定义「最晚有用时间」,超时后不再重传。对可独立解码的完整状态快照:可只发最新、不重传;增量快照还须处理基线丢失;对必须执行的关键命令:需要确认与有界重传,配合服务器去重(幂等);普通连续输入可以按时效放弃旧包。

可靠性预算:带宽和延迟都是成本。关键消息可以重复发送(冗余)或加纠错码(FEC)来避免等待重传;发送速率要响应丢包与 RTT(简易拥塞控制),否则重传反而加剧拥塞。

与 TCP 的本质差别是控制权:可以「故意不可靠」——某些消息放弃重传,某些消息宁可多发几遍也不等确认。这使关键事件能在连接可用期间更可靠地交付;断线后的最终结果仍需应用层持久化、确认与恢复。

自研可靠层的收发循环

  1. 发送方给需要可靠送达的关键命令分配递增序号与发送时间,放入待确认队列;
  2. 接收方收到后更新 ACK 位图(最近 N 条哪些到了)并回发(可捎带在下一个数据包里);
  3. 发送方发现某关键命令超时未确认且仍在有效期内 → 有界重传;超出时限则交给业务查询/恢复,不能无限重发;
  4. 对独立完整的状态快照:生成新快照时丢弃未发送的旧快照,只发最新(无重传);
  5. 丢包率升高 → 降低发送频率、增加冗余或减小包大小,让出带宽;
  6. 长时间收不到任何 ACK → 判定连接失效,交给重连流程(第 20 题)。

类 FPS 客户端的通道划分:关键输入/事件带序号、有 ACK 与有限重传;完整世界快照可带序号、不重传(新覆盖旧),接收端对缺失的快照做插值补间;击杀确认等关键事件走可靠通道。三条通道共用一套底层协议,但可靠性等级不同——这正是「需要的那部分」的含义。

易错点

  • 「在 UDP 上重传就变回了 TCP」:可以按消息类型分别处理,且不阻塞后续消息、可主动放弃;
  • 「ACK 本身不会丢」:回程同样会丢,需要周期重发或捎带确认;
  • 「重传越多越可靠」:带宽与拥塞成本,只有关键消息值得重传/冗余;
  • 「序号可以随便递增」:要考虑回绕与窗口比较(参照 TCP 序列号比较的做法),否则旧包会被误认为新包;
  • 「FEC 是免费的」:用冗余换延迟容错,要按丢包分布选择策略(均匀和突发丢包对 FEC、交织与重传的成本不同,需结合丢包模式和时限验证)。

适用边界:这是应用协议自由设计的领域:KCP、ENet、GameNetworkingSockets、QUIC 各有取舍,没有标准要求哪种。可靠性等级是业务定义,不受协议规范约束;跨平台网络库与服务器容量会影响实现选择。

与已有资料的关系:网易互娱面经 1 的答案给的是概念清单,本题补「收发状态机 + 过期丢弃 + 幂等」的具体设计;与第 16 题 QUIC 的流级重传对照,可以看出同一思想在标准化协议与其上层的不同实现。

通用原则:可靠性是用带宽和延迟预算换来的:先为每条消息定义「最晚有用时间」,过期即弃是实时系统与普通可靠传输最大的区别。任何自研协议先写清「什么必须到、什么可以丢」,再实现重传策略。

心跳、断线重连和状态恢复要注意什么?

一句话回答:心跳用于检测「连接已死但双方都没收到通知」的情况(NAT 映射过期、对端进程消失);重连要用会话令牌恢复身份、以退避加抖动的方式重试,并按「服务器快照为准」重建状态;在服务端权威的游戏里,断线期间客户端可有限度预测/呈现,但不得把本地预测当成权威结果。

为什么需要心跳:TCP 半开连接(对端消失,FIN/RST 不会到达)、NAT 映射有存活时间,长时间没有流量会导致路径失效,需要定期小包保活;应用层心跳还可以捎带 RTT 与服务器时间等度量。若依赖心跳维持 NAT 映射,应按目标网络的空闲超时留余量,同时权衡流量与耗电;不同映射的超时不能套用一个常数。

重连流程:检测断开(心跳超时、写失败、RST)→ 指数退避加随机抖动重试(避免服务器重启时被重连风暴打垮)→携带会话票据请求恢复 → 服务器校验票据(有效期、防重放)→ 下发全量快照或从已知基线的增量 → 客户端以快照为准重建本地表现。

断线期间:可短时间继续本地预测以维持表现,但避免越权判定、UI 给出提示、记录待核实的未确认操作(仅在有幂等/去重机制时重放);若会话确实不可恢复(如票据过期或重启后会话状态丢失),再走完整登录流程。移动端还要把 Wi-Fi/蜂窝切换作为常态设计——IP 变了、四元组变了,靠会话票据而不是连接本身恢复对局。

一次断线重连的完整状态机

  1. 连续 N 次心跳未回 → 标记连接不可用,暂停依赖网络的交互;
  2. 用指数退避 + 随机抖动重试连接(例如 0.5s、1s、2s……设上限);
  3. 连接成功后携带上次的会话票据/令牌请求恢复;
  4. 服务器校验票据:有效则下发全量快照,或在双方确认同一基线时发送差异状态;单有快照序号不足以恢复;
  5. 客户端以权威快照为准重建表现;未收到确认不等于服务器没执行,只对有幂等/去重保护且仍有效的操作重试并与服务器结果对账;
  6. 若恢复失败(票据过期/服务器重启)→ 全新登录并告知玩家;服务器对重复令牌要有防重放。

手机游戏切后台/切网络:从 Wi-Fi 切到蜂窝后 IP 变化,TCP 连接通常已断;客户端用「快速重连 + 会话恢复」在数秒内回到对局,并用快照与插值把断线期间的游戏时间「快进」追上。对局内提示、重连倒计时都是这套状态机对玩家的可见部分。

易错点

  • 「TCP keepalive 就够了」:默认空闲数小时才探测,适合服务器管理连接,不适合实时游戏的快速感知;
  • 「心跳越频繁越好」:增加流量与耗电,间隔要参考 NAT 超时与业务容忍度;
  • 「重连就是重新登录」:有会话恢复可以更快恢复对局,体验差异很大;
  • 「掉线后客户端可以自己续算」:判定权威在服务器,客户端只做表现;越权判定会造成回档与作弊面;
  • 「立即重试就行」:无退避重试会形成重连风暴,服务器恢复瞬间被自己打垮。

适用边界:心跳间隔、退避曲线、票据有效期都是产品与架构选择;NAT 映射超时因设备而异(常见为几十秒到几分钟);引擎或平台 SDK(如各主机/移动平台的网络库)可能已封装部分能力,选型时核对清楚再自己实现。

与已有资料的关系:与第 5(NAT)、17(延迟度量)、18–19(通道设计)题衔接;实际项目经验可在复盘时按本题结构回填。

通用原则:网络一定会断:把「连接」当作会失效的资源,把「会话」当作需要显式恢复的状态。设计时先回答三个问题:断线后谁负责恢复、恢复到哪个基线、重复消息如何去重。

参考来源

以下 RFC 用于核对本文中与协议标准、版本相关的断言;通用机制以教材级共识为准。引文只用于确认语义,细节与版本变化请以来源页面为准;游戏网络部分为工程实践参考,不是标准规范。