普通视图

发现新文章,点击刷新页面。
昨天 — 2026年2月26日首页

网络知识点 - TCP/IP 四层模型知识大扫盲

作者 齐生1
2026年2月26日 11:46

一、计网基础概念

第一章先总体回顾一下

1.1 OSI 七层模型与 TCP/IP 四层模型

OSI 模型 TCP/IP 核心职责 常见协议 iOS 关联
7.应用层 4. 应用层 提供应用服务接口,定义数据格式与交互语义 HTTP, HTTPS, DNS, WebSocket NSURL、URLSession
6.表示层 (并入应用层) 加密、压缩、格式转化 SSL/TLS, JSON, JPEG, UTF-8
5.会话层 (并入应用层) 会话管理、状态保持 TLS 握手、RPC、Session
4.传输层 3. 传输层 端到端通信、可靠传输 TCP、UDP、QUIC Socket
3.网络层 2. 网络层 路由寻址、分片 IP、路由、ICMP、ARP
2.数据链路层 1. 网络接口层 MAC寻址、帧封装 MAC、Wi-Fi、Ethernet
1.物理层 (并入网络接口层) 比特流传输 光线、电缆、无线信号

网络通信是一条从 应用层 → 内核网络栈 → 网络接口 → 传输介质 → 对端主机 的完整路径。

1.2 数据封装与解封装

数据在网络中是如何传递的?

  • 以 TCP 为例:
你的 App 数据:{"name":"张三"}
      │
      ▼ ④ 应用层:加 HTTP 头(方法、路径、Host、…)
┌──────────────────────────────────────┐
│ HTTP Header │ JSON: {"name":"张三"}   │ ← 消息(Message)
└──────────────────────────────────────┘
      │
      ▼ ③ 传输层:加 TCP 头(源端口、目的端口、序列号、…)
┌────────────────────────────────────┐
│ TCP Header │ HTTP Header │ JSON    │ ← 段(Segment)
└────────────────────────────────────┘
      │
      ▼ ② 网络层:加 IP 头(源IP、目的IP、TTL、协议号=6)
┌──────────────────────────────────────────────┐
│ IP Header │ TCP Header │ HTTP Header │ JSON  │ ← 包(Packet)
└──────────────────────────────────────────────┘
      │
      ▼ ① 网络接口层:加 MAC 头(源MAC、目的MAC) + 尾部校验(FCS)
┌────────────────────────────────────────────────────────────────┐
│ MAC Header │ IP Header │ TCP Header │ HTTP Header │ JSON │ FCS │ ← 帧(Frame)
└────────────────────────────────────────────────────────────────┘
  • 以 UDP 为例:
你的 App 数据:{"query":"example.com"}
      │
      ▼ ④ 应用层:加上应用层协议头(例如 DNS / 自定义协议)
┌──────────────────────────────────────────────┐
│ DNS Header │ JSON: {"query":"example.com"}   │ ← 消息(Message)
└──────────────────────────────────────────────┘
      │
      ▼ ③ 传输层:加 UDP 头(源端口、目的端口、长度、校验和)
┌──────────────────────────────────────────────┐
│ UDP Header │ DNS Header │ JSON               │ ← 数据报(Datagram)
└──────────────────────────────────────────────┘
      │
      ▼ ② 网络层:加 IP 头(源IP、目的IP、TTL、协议号=17)
┌────────────────────────────────────────────────────────┐
│ IP Header │ UDP Header │ DNS Header │ JSON             │ ← 包(Packet)
└────────────────────────────────────────────────────────┘
      │
      ▼ ① 网络接口层:加 MAC 头(源MAC、目的MAC) + 尾部校验(FCS)
┌────────────────────────────────────────────────────────────────────────────┐
│ MAC Header │ IP Header │ UDP Header │ DNS Header │       JSON       │  FCS │ ← 帧(Frame)
└────────────────────────────────────────────────────────────────────────────┘

接下来按照数据封装的顺序,依次回顾一下各个层级的知识。


二、应用层(应用层 + 表示层 + 会话层)

App 层逻辑,包括 DNS、HTTP、HTTPS、WebSocket、缓存、认证体系。

2.1 DNS 域名解析

作用:  将域名 https://some.com 解析为 IP 地址,是一切请求的起点。

  • 从 输入网址 -> 页面返回,过程是怎样的?

    • 解析顺序: 浏览器缓存 → OS 缓存 → hosts → 本地 DNS → 根服务器 → 顶级域服务器 → 权威服务器;
    • 返回 IP 后进行 TCP(三次握手)→ TLS 握手 → HTTP 请求。
    ┌──────────────────────────────────────┐
    │   访问 https://api.example.com/path   │
    └──────────────────────────────────────┘
    ① 用户输入网址
       └──> 浏览器解析 URL
             协议 = https、主机 = api.example.com、端口 = 443、路径 = /path
    
    ② DNS 解析域名(api.example.com → IP)
       │
       ├─ 1) 查 [本机缓存](浏览器 DNS 缓存 → OS DNS 缓存 → hosts 文件)
       │
       ├─ 2) 未命中 → 请求 [本地 DNS 服务器](路由器 / 运营商)
       │
       └─ 3) 仍未命中 → [本地 DNS 服务器] 发起 [迭代查询](由它代跑,浏览器只等结果)
             │
             ├─ 问 [根 DNS](.)
             │  └─ 回答:"去问 .com 的服务器"
             ├─ 问 [顶级域 DNS](.com)
             │  └─ 回答:"去问 example.com 的权威服务器"
             └─ 问 [权威 DNS](example.com)
                └─ 回答:"api.example.com = 203.0.113.8" ✓
                   └─ 本地 DNS 缓存结果(根据 TTL),返回给浏览器            
    
    ③ 建立 TCP 连接(三次握手)
    
    ④ TLS 握手(HTTPS 特有,在 TCP 之上建立加密信道)
    
    ⑤ 发送 HTTP 请求(数据从上到下逐层封装)
       └──> [1.2 数据封装与解封装] 知识点
    
    ⑥ 服务器处理请求并返回响应
       │
       ├─ 1) 解封帧(拆 MAC -> ... -> 拿到 HTTP 请求)
       │
       ├─ 2) 业务处理(路由匹配 -> 鉴权 -> 查库 -> 生成结果)
       │
       └─ 3) 构建 HTTP 响应,逐层封装,发回客户端
    
    ⑦ 浏览器接收响应
    
    ⑧ 连接关闭(四次挥手,或保持复用)
       ├─ HTTP/1.1 Keep-Alive → 连接放入连接池,后续请求复用
       ├─ HTTP/2 → 同一连接上继续多路复用
       └─ 不再需要时 → 四次挥手断开:
    
  • DNS 劫持

    • 现象:
      • App -> 运营商 DNS 服务器(可能被劫持)-> 返回错误的 IP
    • 解决方案:HTTPDNS(绕过运营商,App 直连 DNS 服务)
      • App -> 通过 HTTP 直接请求 HTTPDNS 服务器(绕过运营商)-> 返回正确的 IP

2.2 HTTP 协议

HTTP(80)、HTTPS(443)

HTTP 是一种基于 “请求-响应” 的无状态的应用层协议,每次请求都是独立的。

最初就是为浏览器与 Web 服务器设计的。

2.2.1 HTTP 基本信息

  • HTTP 请求 = 请求行 + 请求头 + 空行 + 请求体
POST /api/users HTTP/1.1                ← 请求行:方法、路径、版本
Host: api.example.com                   ← 请求头:多行参数
...
...
                                        ← 空行:分隔头和体
{"name":"张三","email":"z@example.com"}  ← 请求体
  • HTTP 响应 = 状态行 + 响应头 + 空行 + 响应体
HTTP/1.1 201 Created                     ← 状态行:版本、状态码、描述
Content-Type: application/json           ← 响应头
...
...
                                         ← 空行
{"id":456,"name":"张三","created":true}   ← 响应体


2.2.2 HTTP 常见的请求方法

方法 语义 幂等? 安全? 有请求体? 典型场景
GET 获取资源 通常没有 获取用户列表、详情页
POST 创建资源/提交数据 注册、下单、上传
DELETE 删除资源 通常没有 删除订单
HEAD 同 GET 但只要头部 没有 检查资源是否更新
  • 幂等: 执行 1 次和执行 N 次效果相同。
    • GET 请求 10 次,拿到的是同一份数据,幂等✅;
    • DELETE 删 10 次,资源还是被删了(第 2 次返回 404),幂等✅;
    • 但 POST 创建订单 10 次,可能创建 10 哥订单,不幂等❌;
  • 安全: 不会修改服务器资源。
    • GET、HEAD 不会修改服务器资源,只读,安全✅;
    • POST/DELETE 有写操作,不安全❌;

思考: 实际项目中,为什么大部分是 POST 而非 GET?大部分场景不是只需要 “读” 吗?

  • 为安全、加密、签名、防重放、复杂度等。
  1. 请求体加密

    • 很多项目会对请求参数做 AES 等对称加密,将整个参数序列化后加密放在 request body 中传输。GET 请求没有 body,参数只能拼在 URL query string 里,无法做 body 级别的加密。
  2. 签名机制与 HTTP 方法绑定

    NSString *source = [NSString stringWithFormat:@"POST&%@&%@", pathEncoding, paramEncoding];
    NSString *hash = [self HmacSha1:kAppKey data:source];
    
    • 常见的 API 签名方案会把 HTTP 方法作为签名原文的一部分,客户端和服务端按同一规则生成签名并校验。一旦签名协议绑定了 POST,改用 GET 会导致签名不一致、请求被拒绝。
  3. 公共参数太多,URL 长度受限

    • 每个请求通常会自动携带大量公参(设备信息、版本号、签名、时间戳、MD5、...),GET 的请求参数在 URL 里:
      • URL 容易超长,超出 中间代理/CDN 的长度限制;
      • 参数结构复杂时,编码笨重;
  4. 敏感信息不易暴露在 URL 中

    • GET 请求的参数在 URL 里,会被以下环节明文记录:
      • 服务端 access log
      • CDN 日志
      • 浏览器/WebView历史记录
      • 网络抓包/运营商等
    • 而用户凭证(uid/sid/token)、设备指纹、签名等都是敏感数据。
  5. 防重放机制的需要

    • 为了防止请求被截获后重放,通常每个请求都会带上 nonce(随机数)和 millisecond(时间戳),并参与签名计算,GET 请求的缓存机制反而会造成干扰。
    • POST 天然不会被缓存,与防重放设计更契合。
  6. 统一方案降低复杂度

    • 如果 GET 和 POST 混用,就需要【签名逻辑、加密方案、服务端解析逻辑、网关/中间件的安全策略】 等都需要区分两套,维护成本升高。

2.2.3 HTTP 常见的状态码

|状态码 | 说明 |
| --- |  -- |
|`1xx`(上传前试探)|`100`: 服务器说"继续发吧",用于大文件上传前的试探<br>`101`: 协议升级,WebSocket 握手就用这个|
|`2xx`(成功)|`200`: 最常见的 OK|
|`3xx`(重定向)|`301`: 永久重定向|
|`4xx`(客户端错误)|`400`: 请求格式错误<br>`401`: 没登录或Token过期<br>`403`: 登录了但没权限<404>资源不存在|
|`5xx`(服务端错误)|`500`: 服务器崩了|

2.3 HTTP 缓存机制

HTTP 缓存分为两阶段机制: 强缓存(freshness)→ 协商缓存(validation)

“先看时间(强缓存),再问服务器(协商缓存)。”

  1. 首次请求
    • 服务器返回资源 + 缓存控制信息(如 Cache-Control, ETag, Last-Modified)。
  2. 强缓存阶段(freshness)
    • 客户端检查本地缓存是否仍在有效期(由 Cache-Control: max-age 或 Expires 判断)。
    • 若未过期 → 直接使用本地副本,不访问服务器。
  3. 协商缓存阶段(validation)
    • 强缓存过期或被标记需验证,则 客户端带验证头请求服务器
      • If-None-Match: <etag>
      • 或 If-Modified-Since: <time>
    • 服务器判断资源是否变化:
      • 未变化 → 304 Not Modified(仅返回头部,客户端复用旧内容);
      • 已变化 → 200 OK(新资源内容)。
  • 常见 Header:
分类 Header 作用
强缓存 Cache-Control: max-age=3600 指定可直接使用的秒数
Expires: 老式写法,被 Cache-Control 覆盖
协商缓存 ETag / If-None-Match 内容标签验证
Last-Modified / If-Modified-Since 修改时间验证
其他 Vary 声明缓存与哪些请求头有关
private / public 是否允许代理缓存
  • 常见配置:
场景 Header 示例
静态资源 Cache-Control: public, max-age=31536000, immutable
动态接口 Cache-Control: no-cache + ETag
敏感数据 Cache-Control: no-store

2.4 Cookie、Session、Token —— 认证三兄弟

HTTP 是无状态协议 ———— 服务器不记得你是谁。每次请求都是独立的,但是有很多场景需要 “记住用户”(登录态、用户身份等),于是就有了他们仨。

名称 存储位置 主要作用 特点
Cookie 浏览器 / 客户端 存放少量数据,携带 Session ID 每次请求自动携带到服务器
Session 服务端 保存用户状态(如登录态) 有状态,需要共享。
依赖 Cookie 或 URL 中的 Session ID
Token 客户端 身份凭证(常为加密签名) 服务端无状态验证,跨端通用

Web 用 Session,App 常用 Token 鉴权。

  • Cookie

    • 本质: HTTP 头中由服务器通过 Set-Cookie 下发的 键值对。客户端保存后,在后续请求 自动携带 Cookie 头。
    • 示例:
      • Set-Cookie: session_id=abc123; HttpOnly; Secure; Max-Age=3600
      • Cookie: session_id=abc123
  • Session(服务端状态)

    • 流程:

      1. 用户登录 -> 服务端验证成功,生成唯一 Session ID;
      2. 服务端保存登录信息(uid、权限等)到内存或 redis;
      3. Session ID 下发给客户端(通常通过 Cookie);
      4. 后续请求客户端自动携带 Session ID, 服务器查表恢复状态。
    • 特点:

      • 状态保存在服务器(有状态);
      • 适合小规模 / 单机服务;
      • 分布式时需要共享 Session (如 Redis 集中存储)。
  • Token(无状态身份验证)

    • 原理: 服务端不保存状态,只验证 Token 的合法性。
    • 常见类型:
      • JWT(JSON Web Token):Header.Payload.Signature 三段式 Base64 编码。
    • 验证流程:
      1. 登录成功后生成 Token(带用户信息 + 过期时间 + 签名)。
      2. 客户端保存(如 iOS Keychain).
      3. 每次请求带头部: Authorization: Bearer <token>
      4. 服务端验签(是否过期、签名是否匹配)。
    • 特点:
      • 无需服务器保存状态;
      • 一旦签发,撤销复杂(需要黑名单机制);
      • Token 通常短期有效,需要配合 Refresh Token 使用。

Session 与 Token 对比

项目 Session Token
状态保存 服务端 客户端(无状态)
可扩展性 弱(需共享 Session) 强(验证即可)
安全性 依赖 Cookie 保护 依赖签名/加密
登出控制 服务端可立即失效 需黑名单或等待过期
常见场景 Web 登录态 移动端 / API 鉴权

2.5 HTTPS 与 TLS

TLS 是 SSL 的后继协议,SSL 已被淘汰。

HTTPS = HTTP + TLS。TLS 在 TCP 之上、HTTP 之下,提供三大安全保障:

保障 含义 实现方式
加密(Encryption) 防窃听 对称加密(AES)
认证(Authentication) 防伪造 数字证书 + CA 体系
完整性(Integrity) 防篡改 MAC(消息认证码)

HTTPS = HTTP + 加密 + 认证 + 完整性

TLS 握手流程:

image.png

  1. 客户端发送 随机数与算法列表
  2. 服务端返回 证书与随机数
  3. 客户端 验证证书,生成 会话密钥
  4. 使用 非对称算法 安全交换 对称密钥,后续双方使用 对称密钥加密通信

image.pngTLS 1.3 优化了流程:

  • 握手仅需 1-RTT(一次往返),更快;
  • 默认强加密算法(如 AES-GCM、ChaCha20);
  • 支持 0-RTT 快速重连。

iOS 强制使用 TLS1.2+(ATS),支持证书 Pinning。

类型 用途 特点
非对称加密(RSA、ECDHE) 握手阶段,用于密钥协商 安全但慢
对称加密(AES、ChaCha20) 传输阶段,用同一密钥加解密数据 快速

2.6 WebSocket 协议

HTTP 是"你问我答"(请求-响应)模式。客户端不问,服务器不答。但很多场景需要服务器 主动推送.

  • 在 WebSocket 之前,人们用各种"土办法"模拟:
    方案 原理 缺点
    轮询(Polling) 客户端定时发 HTTP 请求(如每 3 秒一次) 浪费带宽和电量,延迟高
    长轮询(Long Polling) 客户端发请求,服务器有数据时才响应,否则挂起直到超时 服务器资源开销大
    SSE(Server-Sent Events) 服务器单向推送 只能服务器→客户端,不支持双向

而 WebSocket 才是真正的解决方案:全双工、持久连接、双方可以随时发数据。

  • iOS 实践:  使用 URLSessionWebSocketTask。

WebSocket 握手

  • 本质: 就握手本质就是一个 HTTP 请求,只不过请求目的不是获取数据,而是 请求协议升级
  • 步骤:
    1. 客户端发送一个特殊的 HTTP 请求 GET /chat HTTP/1.1 ← 还是普通的 HTTP 请求 Host: server.example.com Upgrade: websocket ← "我想升级为 WebSocket 协议" Connection: Upgrade ← "这是一个升级请求" Sec-WebSocket-Key: dGhlIHNhbXBsZQ== ← 随机 Base64 值(防伪造) Sec-WebSocket-Version: 13 ← WebSocket 协议版本 Origin: example.com ← 来源(可选,用于安全校验)
    2. 服务器同意升级 HTTP/1.1 101 Switching Protocols ← 101 = "我同意切换协议了" Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGz... ← 基于 Key 计算的值(验证对方确实懂 WebSocket)
    3. 协议升级完成
      • 从此刻起,这条 TCP 连接不再说 HTTP 了,双方改说 WebSocket 的帧协议(Frame Protocol),双向随时发数据,没有请求-响应的限制。
握手前(HTTP):                  握手后(WebSocket):
┌──────────────┐               ┌──────────────┐
│   HTTP/1.1    │  ── 升级 ──→  │   WebSocket   │
│   文本协议     │               │   二进制帧协议  │
│   请求-响应    │               │   全双工       │
│   头部几百字节  │               │   帧头 2~14字节 │
└──────────────┘               └──────────────┘
       ↑                              ↑
  还是跑在 TCP 之上              还是同一条 TCP 连接
  (如果 wss:// 则还有 TLS)     (连接没断,只是协议变了)
  • Sec-WebSocket-Key / Sec-WebSocket-Accept 是做什么的?
    • 简单的 防伪造机制, 不是加密,验证对方是否是 WebSocket 服务器。

WebSocket vs HTTP 对比

维度 HTTP WebSocket
通信模式 请求-响应(半双工) 全双工
连接 短连接或 Keep-Alive 持久连接
头部开销 每次请求带完整头部(几百字节) 握手后每帧只有 2~14 字节头部
服务器推送 不支持(只能客户端发起) 支持
URL 协议 http:// / https:// ws:// / wss://
适用场景 API 调用、资源获取 实时通信、推送、游戏

iOS 一般使用原生的 URLSessionWebSocketTask(iOS 13+)。

为什么有些项目使用自定义协议(例如[包长度|protobuf序列化数据]),不使用 WebSocket?

WebSocket 自定义协议
建连开销 TCP 握手 + HTTP 升级握手(多一个 RTT) TCP 握手后直接发数据
帧头开销 2~14字节(opcode/mask/长度) 4字节(只写长度就够了)
心跳 固定的 ping/pong 自定义 Protobuf 心跳(可带业务数据)
压缩 整个连接统一压缩策略 不同消息可选不同的压缩策略
传输层 只能 TCP 可以 TCP / KCP 等随时切换
浏览器兼容 ✅这是它存在的意义 ❌但是 App 不需要

2.5 HTTP 版本演进(HTTP/1.0 -> HTTP/1.1 → HTTP/2 → HTTP/3)

  • HTTP/1.0

    • 引入请求头、状态码。
    • 问题:
      • 最初版本,每次请求都需要重新建立连接。
  • HTTP/1.0 -> HTTP/1.1()

    • 问题: HTTP/1.0 每次请求都要新建 TCP 连接,用完就断开,资源浪费;
    • 改进:
      • 持久连接(Keep-Alive): 默认不断开,复用同一个 TCP 连接发送多个请求;
      • 管道化(Pipelining): 连续发送 多个请求,不用等待上一个响应回来。
    • 仍有问题:
      • 对头阻塞(HTTP 层): HTTP/1.1 规定响应必须按照请求顺序返回,如果第 1 个请求慢了,后续全阻塞。
  • HTTP/1.1 -> HTTP/2(大幅性能提升,显著减少延迟与连接数量)

    • 改进:
      • 二进制分帧: 不再是文本协议,把数据拆成带有 Stream ID 标记的帧(Frame)传输;
      • 多路复用: 一个 TCP 连接上 并行传输 多个请求和响应的帧,互不阻塞;
        • ‼️每个帧都标记了 Stream ID,接收方根据 ID 把帧重新组装成各自的响应。帧可以交错发送,谁先好就先发谁—— HTTP 层的队头阻塞 解决了。
      • 头部压缩(HPACK): HPACK 用静态表+动态表+哈夫曼编码大幅压缩头部;
      • 服务器推送(Server Push): 服务器主动推送资源;
    • 仍有问题:
      • ‼️多路复用虽然解决了 HTTP 层的队头阻塞,但底层 TCP 的对头阻塞还在——TCP 丢一个包,整个连接上所有 Stream 都得等重传。
        • ‼️因为底层用的还是 一条 TCP 连接,TCP 的特性 保证字节流严格有序到达所有 Stream 共享一条字节流,空洞就等,等到补齐为止。
  • HTTP/2 -> HTTP/3(基于 QUIC,连更低的头部延迟与更快的握手)

    • 改进:

      特性 HTTP/2 (TCP) HTTP/3 (QUIC/UDP)
      传输层 TCP UDP(自己实现可靠传输)
      队头阻塞 TCP 层有 完全消除(各 Stream 独立)
      握手延迟 TCP 握手 + TLS 握手 QUIC 内置 TLS,1-RTT
      重连 全新握手(WiFi→蜂窝) 0-RTT 恢复(快速重连)
      连接迁移 基于四元组,换 IP 就断 Connection ID 标识,换 IP 不断
      • ‼️解决 TCP 层对头阻塞
        • 不用 TCP 了,在 UDP 上自己造一个传输层,让每个 Stream 都有独立的序号和重传机制,每个 Stream 有自己独立的字节流,互不干扰。
      • 对 iOS 端的意义: 用户在 WiFi 和蜂窝之间可以做到无感切换
      传统 TCP(HTTP/2):
      用户走出公司(WiFi)→ 走上街(蜂窝) → IP 地址变了 → TCP 连接断了 → 重新握手
      QUIC(HTTP/3):
      用户走出公司(WiFi)→ 走上街(蜂窝) → IP 变了但 Connection ID 没变 → 连接无缝切
      

三、传输层:TCP、UDP、QUIC、KCP

3.1 TCP

TCP 是一个 面向连接、可靠、有序、全双工 的传输协议。在建立连接前,双方需要达成 3 个共识:

  1. 确认通信能力: 双向通信,双方都能收能发。
  2. 交换初始序列号(ISN): 每个字节都有序号,双方要各自告知自己的起始序号。
  3. 防止历史连接的干扰: 网络中的旧报文不能让新连接误认为是有效的。

三次握手就是为了解决这三件事。


3.1.1 TCP 三次握手

image.png

  • 过程:
    • 第 1 次: C -> 连接请求 (SYN) -> S
    • 第 2 次: S -> 收到请求后,发送连接请求的确认请求 (SYN+ACK) -> C
    • 第 3 次: C -> 收到请求后,发送确认请求的确认请求 (ACK) -> S

常见问题:

1. 为什么不是 2/4 次握手,而是 3 次握手?

  • 2 次握手,双端状态可能不一致,存在 “半开连接” 和 “旧连接干扰” 问题。

  • 3 次握手 的作用:

    1. 确认双向通信可达:
      • 3 次握手保证 C、S 双方相互得知对方的收发能力正常;
      • 如果只有 2 次握手,S 无法得知 C 的接收能力是否正常。
    2. 防止旧连接干扰:
      • 3 次握手通过新的 ISN 和确认机制,可以识别出过期连接并丢弃。
      • 如果只有 2 次握手,S 可能错误建立 “脏连接”,造成状态混乱。
    3. 建立可靠传输基础:
      • 3 次握手双方交换初始序列号,建立有序的、基于字节流的协议。

2. 如果第 3 次握手丢失,服务端、客户端分别如何处理?

第 3 次握手丢失会造成短暂的 “半开连接”,但 TCP 的 重传与超时机制 最终会恢复一致性。

  • 客户端
    • 进入 ESTABLISHED,认为连接成功,继续等待 S 响应或者发送数据。
  • 服务端
    • 仍在 SYN_RECEIVED,等待 ACK,定时器到期后会重传 SYN+ACK。
    • C 收到重传的 SYN+ACK 后,会再次发送 ACK;
    • 若多次重传仍无回应,S 会放弃连接,并释放 半连接资源
    • C 尝试发数据,若 S 未进入 ESTABLISHED,可能收到 RST 响应。

3. 为什么 ISN(初始序列号)不固定?

  • 防止旧连接干扰:
    • 网络中可能残留旧包,若新连接的 ISN 与旧连接相同,旧报可能被误认为是当前连接的数据。
  • 增强安全性:
    • 防止攻击者通过预测序列号来伪造 TCP 报文(TCP 盲注入攻击、会话劫持);
    • RFC 要求 ISN 必须 随时间变化且不可预测,现代操作系统(Linux/iOS/macOS)也都采用 “时间戳+随机扰动” 动态生成方式。

4. 3 次握手的过程中是否携带数据?

标准的 TCP 行为 是不会在握手阶段携带数据的:

  • 前 2 次握手 不携带数据,是因为 连接尚未建立接收方未分配接收缓冲区,若携带数据,可能造成安全与资源浪费问题(攻击者伪造 SYN 报文携带大量数据)。
  • 第 3 次握手 理论上可以携带数据,因为 C 已经进入 ESTABLISHED 状态了。但是大多实现出于简化和安全考虑,也不在 ACK 中携带数据。
    • 实现简化:
      • S 的 接收缓冲区和应用层 socket 可能还没准备好,处理逻辑复杂。
    • 安全问题:
      • 防止 DoS 攻击和数据误处理(若 ACK 丢失或重传,可能导致同一份数据被重复处理,S 在连接未确认时读渠道未认证的数据,会产生安全隐患(伪造、注入等风险))。

特殊情况: TCP Fast Open (TFO) 允许在 SYN 报文中携带应用数据并提前发出,实现 “0- RTT 建连”,但需要 S 支持。

5. “半开连接” 是怎么产生的?

  • 概念: 半开连接(Half-Open Connection) 是指连接的两端状态不同步:一方认为连接已建立或仍存在,另一方实际上未建立或已断开。
  • 后果: 半连接堆积(如 SYN Flood 攻击)。
  • 检测手段: 内核(SYN Cookies、防火墙限速)、应用层(心跳检测、超时挥手)。
  • 握手阶段丢包: 第 3 次握手丢失,S 进入 SYN_RECEIVED,客户端已进入 ESTABLISHED
    • 服务端 会将半开连接存放在 半连接队列 中。
  • 已连接后异常断线: C 退出 App,S仍认为连接存在,处于 ESTABLISHED,等待数据。
    • 需要依赖 KeepAlive 心跳 检测。
  • 关闭阶段异常: 某端未正确完成四次挥手(如 FIN/ACK 丢失)。
    • 双方状态不一致(FIN_WAIT / CLOSE_WAIT)。

扩展一下

1. 全双工 vs 半双工

全双工: 指通信双方可以 同时 进行发送和接收数据。

半双工: 双方都可以发送数据,但 不能同时,只能 “你说完我再说”。

模式 含义 能否同时发送/接收 TCP 中体现
单工 单向通信 几乎不用
半双工 双向但不同时 早期物理层通信
全双工 双向同时 TCP 是全双工协议
半关闭 一方发送关闭,但可接收 仅一方向关闭 TCP 四次挥手中的状态

2. 全连接队列 vs 半连接队列

服务端 TCP 内核实现层面的关键概念,

全连接队列: 存放 3 次握手已完成,但还未被应用层 accept() 接收的连接。

半连接队列: 存放已收到客户端 SYN,但 3 次握手尚未完成 的连接。

握手过程:

步骤 状态 队列
C -> 发送 SYN S 进入 SYN_RECIEVED 加入 半连接队列
S -> 发送 SYN+ACK,等待 ACK C 进入 ESTABLISHED 半连接队列等待确认
C -> 回 ACK 包 S 进入 ESTABLISHED 半连接队列 移入 全连接队列

特点:

  • 如果第 3 次握手迟迟不到,连接会在队列中等待一段时间,知道超市或超过最大重传次数,就删除这个 半连接
  • 全连接队列 已🈵,新握手完成的连接会被 丢弃/拒绝
  • 应用层调用 accept() 是,才会将连接从 全连接队列 中取出。

3.1.2 TCP 四次挥手

image.png

  • 过程:
    • 第 1 次: C -> FIN -> S,客户端主动发送 FIN 关闭发送通道,服务端收到 FIN 后 关闭接收通道
    • 第 2 次: S -> ACK -> C,服务器仍可继续 发送剩余数据
    • 第 3 次: S -> FIN -> C,服务端发送完所有数据后,主动发送 FIN 关闭发送通道,客户端收到 FIN 后 关闭接收通道
    • 第 4 次: C -> ACK -> S,服务端收到 ACK 后 立即关闭连接,客户等待 2MSL 后 彻底关闭连接,确保最后的 ACK 能被对方收到并防止旧包干扰。

常见问题:

1. 为什么是 “4 次挥手”,而不是 “3 次”?

  • 因为 TCP 是 全双工协议,双方的发送和接收通道是 独立的,每个方向都必须 单独关闭,因此有了 “4 次” 挥手。
    • 当一方(主动方)发出 FIN,只表示 “不再发送数据”,但是 “还能接收数据”;
    • 另一方(被动方)可能还没有发完数据,所以不能立即 FIN;
    • 另一方(被动方)必须等到自己也准备关闭时,再单独发出 FIN;
    • 因此需要一次 FIN 一次 ACK,再一次 FIN 一次 ACK,总共四次,确保双方都能 安全、完整地 关闭。

2. TIME_WAIT 是什么?

  • 主动关闭连接的一方,在发送最后一个 ACK 后会先进入 TIME_WAIT 状态,等待一段时间(2MSL)再彻底关闭连接(进入 CLOSED)。
    • MSL(Max Segment Lifetime): 网络中一个 TCP 报文可能存在的最长时间。
  • 为什么不直接进入 CLOSED 呢?
    1. 保证 “最后一个 ACK” 可靠传输
      • 如果 C 立即关闭,S 没收到 ACK,会重发 FIN;
      • 若 C 已关闭,就无法回应,导致服务端一直处于 LAST_ACK 状态。
    2. 防止旧连接的干扰
      • 如果立即关闭,并重新使用相同四元组(IP、PORT、协议),网络中延迟处理的旧包可能被当作新连接的数据;
      • TIME_WAIT 期间确保旧包在网络中全部消失后才关闭。

3. 为什么 TIME_WAIT 要持续 2 x MSL?

  • 一段 MSL 是为了 确保本连接最后发送的 ACK 能到达对方
  • 另一段 MSL 是为了 确保旧连接中的报文在网络中彻底消失

4. TIME_WAIT 常见问题

问题 原因 对策
TIME_WAIT 太多,占用端口 客户端大量主动关闭连接 1. 调整 tcp_tw_reuse、tcp_tw_recycle(Linux 旧版);2. 使用长连接;3. 服务端主动关闭
TIME_WAIT 导致端口耗尽 并发短连接太多 增大临时端口范围;采用连接池或 HTTP keep-alive
服务端出现大量 CLOSE_WAIT 服务端未调用 close()正常关闭 检查应用层逻辑,确保及时释放

5. TIME_WAITCLOSE_WAIT 区别

状态 所在端 触发条件 表示意义
TIME_WAIT 主动关闭方 发完最后 ACK 等待 2MSL 等待旧包消失,保证连接彻底关闭
CLOSE_WAIT 被动关闭方 收到对方 FIN,尚未发送自己的 FIN 应用层未 close(),导致资源占用

6. RST 什么时候出现?

  • 已关闭的连接 发送数据;
  • 未建立的连接 发送请求;
  • 半连接超时被清理;
  • 异常关闭;

3.1.3 TCP vs UDP

一句口诀:

  • TCP 是一种面向连接、可靠的、一对一的、面向字节流的、首部 20-60 字节的、传输层通信协议。
  • UDP 是一种无连接的、不可靠的、任意连接个数的、面向报文的、首部 8 字节的、传输层通信协议。
维度 TCP UDP
连接性 面向连接(三次握手) 无连接的(不需要事先建立连接)
可靠性 可靠(数据包校验、包重排、丢弃重复数据包、ACK 机制、超时重传机制、拥塞控制、流量控制) 不可靠(可能丢包、乱序)
通信模式 一对一 一对任意数量(单播、多播、广播)
数据传输单元 面向字节流(无边界,可能粘包/拆包) 面向报文(有边界,保留报文长度)
首部开销 较大,20~60 字节 很小,固定 8 字节
传输效率 相对较低(需建立连接、确认、控制机制) 极高(无控制开销,延迟低)
适用场景 要求 数据准确完整 的场景,如 FTP、HTTP、SMTP、SSH 要求 实时性高、速度快 的场景,如音视频、通话、直播、DNS 查询、游戏等
  • TCP 特性详解
    1. 核心特性:

      • 面向连接: 三握四挥
      • 可靠传输: 一系列复杂机制确保数据 准确、有序、不重复 地送达
      • 全双工通信: 连接建立后,双方可同时进行数据收发。
    2. 可靠性保障机制: 0. 三次握手、四次挥手

      1. 序列号与确认应答(ACK): 每个字节都有唯一序列号,接收方通过返回 ACK 确认收到。
      2. 超时重传(RTO): 发送数据后启动计时器,若在 RTO(超时重传时间)内未收到 ACK,则重发数据。RTO 动态计算,通常略大于 RTT(往返时延)。
      3. 数据包排序: 利用序列号对数据包排序。
      4. 丢弃数据包: 根据序列号识别并丢弃重复包。
      5. 校验和: 校验数据在传输中是否出错,出错则丢弃等待重传。
      6. 流量控制: 通过接收方通告的窗口大小,动态调整发送速率,防止接收方缓冲区溢出。
      7. 拥塞控制: 通过拥塞窗口和四种算法(慢开始、拥塞避免、快重传、快恢复)感知并应对网络拥塞,降低整体丢包率。
        • RTT 往返时延
          • 由链路的传播时间、末端系统出的处理时间、路由缓存中的排队和处理时间组成。
          • 最后一个因素会 随着网络拥塞程度而变化,所以 RTT 一定程度上也反映网络拥塞程度
    3. 面向字节流与粘包问题

      • 字节流: TCP 将数据视为无结构的字节流,不保留消息边界。发送方多次写入数据可能被合并(粘包)或拆分(拆包)发送。
      • 粘包解决方案:
        • 先传包大小,再传包内容;
        • 固定包长度;
        • 设置结束标志;
    4. 核心机制:

      • 滑动窗口:
        • 实现流量控制的核心数据结构,它标识了在无需等待确认的情况下,发送方 能连续发送的数据范围,极大 提高传输效率
      • 流量控制:
        • 目的是 保护接收方,防止接收方被淹没,控制对象为端到端(rwnd),接收方通过 ACK 包中的 “窗口大小” 字段,告知发送方自己 剩余的缓冲区容量,从而 控制发送方的发送速率
      • 拥塞控制:
        • 目的是 保护网络,防止网络过载,控制对象为全网(cwnd),通过感知网络拥塞程度(如丢包、RTT增长),动态调整 “拥塞窗口” 的大小,控制 向网络注入数据的全局速率
    5. TCP 缓冲区

      • 发送缓冲区: 存储 已发送未确认、以及待发送 的数据,每个字节都有序列号,在收到 ACK 确认的数据才会从缓冲区移除。
      • 接收缓冲区: 存储 已接收但未被应用层读取,以及乱序到达 的数据,其剩余空间大小通过窗口通告给发送方,确保接收缓冲区不溢出。

  • UDP 特性详解
    1. 核心特性

      • 无连接: 直接发送数据,无需建立连接,开销小。
      • 不可靠传输: 不提供 ACK、重传、排序 等保障机制,数据可能 丢失、重复、乱序
      • 面向报文: 对应用层交下来的报文,既不合并也不拆分,一次发送一个完整的报文,保留消息边界。
    2. 优缺点

      • 优点:
        • 速度快、延迟低: 无连接、无控制开销。
        • 头部开销小: 仅 8 字节。
        • 支持多播/广播: 可以高效向多个目标发送数据。
        • 无阻塞控制: 在网络拥塞时仍能保持发送速率,适合实时应用。
      • 缺点:
        • 不保证可靠性: 需要由 应用层 自行处理 丢包、重复、乱序 等问题。
        • 易导致网络拥塞: 缺乏拥塞控制,若大量发送可能加剧网络拥塞。
    3. UDP 报文结构

      • 头部(8 字节):源端口(2)、目的端口(2)、长度(2)、校验和(2)。
    4. 不可靠性的应用层解决方案

      1. 增加序列号
      2. 引入ACK与重传机制
      3. 实现流量控制
    5. UDP缓冲区

      • 发送缓冲区: 发送时,将数据放入缓冲区后立即发送,并从缓冲区清除不停留;
      • 接收缓冲区: 接收时,将数据放入缓冲区供应用读取。

单独讲一下 拥塞控制 与 流量控制
  • 拥塞控制:

    • 本质: 拥塞控制的本质是发送方通过一个 拥塞窗口 变量,来动态探测并适应网络传输能力,尽可能高效利用可用带宽。
      • 拥塞窗口(cwnd):
        • 发送方根据自己 对网络拥塞程度的评估 而维护的一个窗口值,它代表了 “在当前网络情况下,我能安全发送多少数据,而不造成拥塞”。
      • 发送窗口的最终大小:
        • 发送方在任一时刻,实际能发送的数据了 = min(cwnd,rwnd)
      • 慢启动门限(ssthresh):
        • 一个状态切换的阈值。当 cwnd < ssthresh,使用慢开始算法;当 cwnd >= ssthresh,使用拥塞避免算法。
    • 影响: 网络拥塞时 TCP 继续发包可能会导致数据包丢失或时延,这时 TCP 就会重传,导致网络更加拥塞
    • 拥塞前兆:
      • 数据包延迟显著增加;
      • 丢包率上升;
      • 网络吞吐量下降;
        1. 超时重传(RTO 超时): 严重拥塞信号。
          • 网络非常拥堵,必须 “急刹车”,然后从最低速重新开始指数增长。
        2. 收到 3 个重复的 ACK: 轻度拥塞信号。
          • 网络只是轻度丢包,数据还在流动,可以适度减速。
    • 拥塞控制:
    初始状态
        ↓
    慢启动(指数增长)
        ↓ (cwnd >= ssthresh)
    拥塞避免(线性增长)
        ↓
    [网络事件发生]
        ├─ 超时重传 ──→ 慢启动重启(cwnd=1)
        └─ 3个重复ACK ──→ 快重传 + 快恢复 ──→ 拥塞避免
    

  • 流量控制:

    • 本质: 本质是控制供需平衡,解决收发双方的速率匹配问题,防止发送方发送速率超过接收方的处理能力,导致接收方缓冲区溢出和数据丢失
    • 核心机制:滑动窗口
    发送缓冲区(发送方维护)
    ┌───────────────────────────────────────────────────┐
    │ 已发送且已确认 │  已发送未确认 │  可发送未发送 │ 不可发送 │
    │  (可清除)     │  (等待ACK)   │  (在窗口内)  │(超出窗口)│
    ├───────────────────────────────────────────────────┤
    ◄── 已确认部分 ──►◄─────────── 发送窗口 ────────────►
    
    接收缓冲区(接收方维护)
    ┌──────────────────────────────────────┐
    │ 已接收已确认  │ 可接收未接收   │ 不可接收 │
    │ (待应用读取)  │  (在窗口内)   │(超出窗口)│
    ├──────────────────────────────────────┤
    ◄── 已使用部分 ─►◄─────── 接收窗口 ───────►
    

四、网络层:IP

4.1 作用

逻辑寻址 + 路由转发

  • 核心协议:IP、ICMP、ARP、NAT、路由协议(RIP/OSPF/BGP)

4.2 IP 基础

版本 地址长度 示例
IPv4 32 位 192.168.1.1
IPv6 128 位 2001:db8::1
  • IP 报文由 【头部 + 数据】 组成,头部含源 IP、目标 IP、TTL、协议号。
  • 若包太大,网络层负责分片与重组。
    • 负责寻址与分片;
    • TTL 防死循环;
    • 协议号区分上层(6=TCP,17=UDP)。

4.3 ICMP

  • Internet Control Message Protocol,用于诊断和错误报告。
  • 常见命令:ping(测试连通性)、traceroute(路由追踪)。

4.4 路由机制

  • 静态路由:手动配置;
  • 动态路由:路由器间自动交换信息(RIP、OSPF、BGP)。
  • 默认网关:未知目标的转发出口。

五、网络接口层(链路层、物理层):WiFi、蜂窝

5.1 职责

  • 封装帧、寻址、差错检测(FCS)、介质访问控制。
  • 负责同一局域网内的 节点到节点通信

5.2 MAC 地址与 ARP

  • 每个网卡唯一的 48 位地址(00-1C-42-7A-xx-xx)。
  • ARP(地址解析协议):根据 IP 查 MAC 地址。
    • 逆向为 RARP。

5.3 常见协议

类型 协议 作用
有线 Ethernet 主流局域网协议
无线 Wi-Fi(IEEE 802.11) 无线局域网标准
点对点 PPP 拨号链路协议

六、三方库:AFNetworking

6.1 AFNetworking

AFNetworking 本质上就是对苹果 NSURLSession 的面向对象封装,把 delegate 回调模式变成更易用的 Block 模式,同时加了一套序列化体系。

整体结构:

AFNetworking 4.0
│
├── 核心层
│   ├── AFURLSessionManager        会话管理器(核心中的核心)
│   └── AFHTTPSessionManager       HTTP 便捷管理器
│
├── 序列化层
│   ├── AFHTTPRequestSerializer    请求序列化(拼参数、设 Header)
│   │   ├── AFJSONRequestSerializer
│   │   └── AFPropertyListRequestSerializer
│   ├── AFHTTPResponseSerializer   响应序列化(解析数据)
│   │   ├── AFJSONResponseSerializer
│   │   ├── AFXMLParserResponseSerializer
│   │   └── AFImageResponseSerializer
│   └── AFSecurityPolicy           SSL 证书校验策略
│
├── 辅助层
│   └── AFNetworkReachabilityManager  网络状态检测
│
└── UIKit 扩展 (可选)
    ├── UIImageView+AFNetworking
    ├── UIButton+AFNetworking
    └── AFNetworkActivityIndicatorManager
  • AFURLSessionManager(一切的基础)
    • 持有:
      • NSURLSession *session ← 苹果原生会话
      • NSOperationQueue *operationQue
      • AFSecurityPolicy *securityPolicy
      • AFNetworkReachabilityManager *reachabilityManager
      • NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier (每个 Task 对应一个 delegate, 管理回调)
    • 做了什么?
      • 实现 NSURLSessionDelegate 全家桶
      • 把 delegate 回调 →转化成→ Block 回调
      • 给每个 NSURLSessionTask 配一个 AFURLSessionManagerTaskDelegate,这个内部 delegate 负责收集数据、计算进度、最终回调。
      • 管理 Task 的生命周期 (创建/取消/暂停/恢复)
      • SSL 证书校验 (通过 AFSecurityPolicy)
    • 关键方法:
      • 数据任务(用于普通 HTTP 请求,请求与响应体都在内存中): dataTaskWithRequest:completionHandler:
      • 上传任务(用于 “上传本地文件” 到服务器,底层基于 http(s)): uploadTaskWithRequest:fromFile:progress:completionHandler:
      • 下载任务(用于 “下载文件” 到本地磁盘): downloadTaskWithRequest:destination:progress:completionHandler:
  • AFHTTPSessionManger(继承自 AFURLSessionManager, HTTP的便捷入口)
    • 新增:
    • 提供 RESTful 风格方法:
    • 内部流程:

序列化体系

  • 这是 AFNetworking 最核心的设计模式 —— 请求/响应序列化器可替换
请求序列化器 (怎么把参数变成 HTTP 请求)
────────────────────────────────────────

AFHTTPRequestSerializer (默认)
  · Content-Type: application/x-www-form-urlencoded
  · 参数编码: key1=value1&key2=value2
  · 设置通用 Header: User-Agent / Accept-Language

AFJSONRequestSerializer
  · Content-Type: application/json
  · 参数编码: JSON 格式 {"key1":"value1"}
  
  响应序列化器 (怎么把响应数据变成对象)
────────────────────────────────────────
AFHTTPResponseSerializer (默认)
  · 不做任何解析, 直接返回 NSData

AFJSONResponseSerializer
  · 验证 Content-Type 是否为 JSON
  · NSJSONSerialization 解析 → NSDictionary / NSArray

AFImageResponseSerializer
  · 验证 Content-Type 是否为图片
  · 解析 → UIImage

6.1.1 一个请求在 AF 内部的完整流程

manager.POST(url, parameters, headers, success, failure):   业务调用
    
    
 AFHTTPRequestSerializer.request:          请求序列化
    
    
    返回 NSMutableURLRequest
    
    
 AFURLSessionManager.dataTask:           创建 Task,绑定 delegate,注册回调。
    
    
 task.resume()   请求发出去了
    
    
 NSURLSession delegate 回调 (AF 接管):        接收数据,回调 AFURLSessionManagerTaskDelegate
    
    
 AFURLSessionManagerTaskDelegate:        汇总数据
    
    
 AFJSONResponseSerializer.response:     响应序列化
    
    
    返回 NSDictionary
    
    
 dispatch_group:           回到主线程

6.1.2 AFNetworking 使用优化

  • AFHTTPSessionManager: 可以注入 Cronet 以支持 QUIC / HTTP2‘
  • AFHTTPRequestSerializer: 可以增加 AES128 加密等;
  • AFJSONResponseSerializer 可以增加对应的 AES128 加密、加 GZip/Brotli 解压逻辑等。

6.1.3 AFNetworking vs 直接使用 NSURLSession

NSURLSession AF 封装后
要自己实现 4 个 delegate 协议 Block 回调,几行代码搞定
要自己拼 URL、编码参数 manager.POST(url, params) 一行搞定
要自己管理 Task 生命周期 AF 自动管理
要自己解析 JSON responseSerializer 自动解析
要自己做 SSL 校验 AFSecurityPolicy 配置即可
要自己检测网络状态 AFNetworkReachabilityManager 现成的

6.1.4 AFNetworking vs Protobuf

  • AF 序列化: 把参数 → 变成 HTTP 协议能理解的格式
  • Protobuf: 把数据 → 变成一种紧凑的二进制编码格式

AF 序列化把字典变成 HTTP 能传的文本(HTTP 协议规范),Protobuf 把对象变成 Socket 能传的二进制。 前者为了兼容 HTTP 标准,后者为了追求极致的小和快。

AF 序列化 Protobuf
格式 文本 (JSON / 表单) 二进制
可读性 人能直接看 看不懂,需要 .proto 文件才能解
体积 小(1/3~1/5)
解析速度 快(10~100x)
Schema 无,运行时动态解析 有,编译时生成代码(GPBMessage 子类)
用在哪 HTTP 请求/响应 TCP 长连接的数据包
为什么选它 HTTP 标准就是 JSON/表单 长连接要求低延迟、省流量

七、总结

落实到 App 开发中,常见链路可能是这样的:

整体链路关系:

┌─────────────────────── 应用层 ───────────────────────────┐
│                                                         │
│  业务协议:      HTTP API / Protobuf / RTC                │
│  安全:          AES 加密 / HMAC-SHA1 签名   / 防重放       │
│  域名管理:            业务统一管理                         │
│  域名解析:           HTTPDNS / 系统 DNS                   │
│                                                         │
├─────────────────────── 传输层 ───────────────────────────┤
│  TCP (GCDAsyncSocket)                                   │
│  UDP + KCP (GCDAsyncUdpSocket + KCP)                    │
│  QUIC (Cronet)                                          │
│                                                         │
├─────────────────────── 网络层 ───────────────────────────┤
│  IP (v4/v6)                                             │
│                                                         │
├─────────────────────── 接口层 ───────────────────────────┤
│  WiFi / 蜂窝 / 断网检测                                   │
└─────────────────────────────────────────────────────────┘

HTTP 链路:

┌─────────────────────── 应用层 ───────────────────────────┐
                                                         
   业务入口                                              
     ObjC:  xxxxHTTP.facebookLogin(...)                  
     Swift: xxxxAPI(params).observable.subscribe(...)    
                                                        
                                                        
   调度中心                                              
     ObjC:  业务统一错误码处理等                             
     Swift: MoyaProvider  (插件链处理)                     
                                                         
                                                         
   请求模型 (两侧做同样的事, 语言不同)                       
       · 填充公共参数: uid, sid, device_id, version...     
       · 生成防重放:   nonce (UUID) + millisecond (时间戳)  
       · 计算签名:     token (HMAC-SHA1)                   
                      sign  (HMAC-SHA1)                   
       · 合并业务参数                                       
                                                         
                                                         
   序列化 & 加密                                          
     ObjC:  HTTPAESRequestSerialization                   
     Swift: MoyaAESHandler.prepare                        
       · 按路径判断是否需要 AES128 加密请求体                  
       · 设置请求头 X-ENCRYPTED-VERSION                     
                                                         
                                                         
   HTTP                                                
     ObjC:  AFNetworking    AFHTTPSessionManager         
     Swift: Alamofire       Session                      
                                                         
                                                         
   NSURLSession (两侧共用)                                
     · SessionConfiguration 注入 Cronet                    
     · Cronet 尝试 QUIC  降级 HTTP/2  降级 HTTP/1.1       
                                                          
├─────────────────────── 传输层 ───────────────────────────┤
                                                         
          QUIC (Cronet, UDP)  TCP (系统)                
                                                        
                                                        
                       TLS 握手                           
                                                         
├─────────────────────── 网络层 ───────────────────────────┤
                                                          
  IP 路由                                                  
                                                          
├─────────────────────── 接口层 ───────────────────────────┤
                                                          
  WiFi / 蜂窝                                             
                                                          
└──────────────────────────────────────────────────────────┘

TCP 链路:

┌─────────────────────── 应用层 ───────────────────────────┐
│                                                         │
│  ① 建连 (App启动 / 登录成功触发)                           │
│                          │                               │
│                          ▼                               │
│  ② 认证 (连接建立后)                                      │
│       · 构建请求: sid + deviceId + version                │
│                          │                               │
│                          ▼                               │
│  ③ 封包:Protobuf 等                                      │
│                          │                               │
│                          ▼                               │
│  ④ Socket 发送                                            │
│                          │                               │
│                          ▼                               │
├─────────────────────── 传输层 ────────────────────────────┤
│                                                          │
│  TCP (GCDAsyncSocket)  /  KCP + UDP (GCDAsyncUdpSocket)  │
│                                                          │
├─────────────────────── 网络层 ────────────────────────────┤
│                                                          │
│  IP 路由                                                  │
│                                                          │
├─────────────────────── 接口层 ───────────────────────────┤
│                                                          │
│  WiFi / 蜂窝                                             │
│                                                          │
└──────────────────────────────────────────────────────────┘

保护机制:

┌─────────────────────── 应用层 ───────────────────────────┐
│                                                         │
│  心跳保活                                                │
│     →  NSTimer 定时发送心跳包                             │
│                                                         │
│  守护进程 (ConnectorDaemon)                             │
│    ├── TCP 守护: 连续 n 次失败 → 切备用域名                │
│    └── KCP 守护: n 秒内 n 次超时 → 切回 TCP               │
│                                                        │
│  重连策略                                                │
│    TCP 断开      → 0.5s 自动重连                          │
│    bind 失败     → 1s 重试 bind                          │
│    域名故障      → 切备用域名                              │
│    KCP 故障      → 切回 TCP 连同一域名                     │
│    前后台切换    → 发探测包验证, 失败则重连                   │
│                                                          │
└──────────────────────────────────────────────────────────┘
❌
❌