你有没有想过:你在手机上刷的视频,在空中飞的时候,是谁负责把它"加密锁好"、把"臃肿的头部瘦身"、再把"乱序的包裹排好"?答案就是PDCP——5G协议栈中最容易被忽视、却最不可或缺的"安全管家兼物流调度员"。
想象你是一家电商仓库的打包员。你的工作流程是这样的:
这五步,恰好对应了5G PDCP层的五大核心功能。PDCP全称 Packet Data Convergence Protocol(分组数据汇聚协议),位于RLC层之上、SDAP/RRC层之下,是Layer 2的"天花板"。
先看全貌,再逐个深入:
graph TB
subgraph 用户面["用户面(User Plane)"]
APP["应用层<br/>(视频/网页/游戏)"]
SDAP["SDAP层<br/>QoS流映射"]
PDCP_U["🔐 PDCP层<br/>加密 · 头压缩 · 重排序"]
RLC_U["RLC层<br/>分段/ARQ"]
MAC_U["MAC层<br/>调度/复用"]
PHY_U["PHY层<br/>调制/编码"]
end
subgraph 控制面["控制面(Control Plane)"]
NAS["NAS层<br/>核心网信令"]
RRC["RRC层<br/>无线资源控制"]
PDCP_C["🔐 PDCP层<br/>加密 · 完整性保护 · 重排序"]
RLC_C["RLC层"]
MAC_C["MAC层"]
PHY_C["PHY层"]
end
APP --> SDAP --> PDCP_U --> RLC_U --> MAC_U --> PHY_U
NAS --> RRC --> PDCP_C --> RLC_C --> MAC_C --> PHY_C
style PDCP_U fill:#ff6b6b,color:#fff,stroke:#c0392b
style PDCP_C fill:#ff6b6b,color:#fff,stroke:#c0392b
注意一个关键区别:用户面和控制面都经过PDCP,但职责不同:
| 功能 | 用户面(DRB) | 控制面(SRB) |
|---|---|---|
| 加密/解密 | ✅ | ✅ |
| 完整性保护 | ❌(可选) | ✅(必须) |
| 头压缩(ROHC) | ✅ | ❌ |
| 重排序 | ✅ | ✅ |
| 重复检测 | ✅ | ✅ |
| PDCP复制 | ✅ | ✅ |
为什么控制面不做头压缩? 因为RRC信令本身已经是精简的二进制编码(ASN.1 PER),没有臃肿的IP/UDP/RTP头部需要压缩。头压缩专为VoLTE/VoNR等小包业务设计,一个语音包有效载荷才几十字节,IP头部却占了40字节——不压缩太浪费空口资源了。
PDCP加密的本质是流密码异或(XOR):用密钥和一组参数生成一串伪随机 keystream,再与明文逐比特异或。
$$Ciphertext = Plaintext \oplus Keystream(KEY, COUNT, BEARER, DIRECTION)$$
其中: - KEY:从5G-AKA认证流程派生的密钥($K_{UPenc}$ 用于用户面,$K_{RRCP}$ 用于控制面加密) - COUNT:32位计数器 = HFN(超帧号)+ PDCP SN(序列号) - BEARER:无线承载标识(5位) - DIRECTION:方向位(上行=0,下行=1)
5G支持四种加密算法:
| 算法标识 | 名称 | 基础算法 | 说明 |
|---|---|---|---|
| NEA0 | 空加密 | 无 | 调试用,不提供安全保护 |
| NEA1 | 128-NEA1 | SNOW 3G | 继承自4G的流密码算法 |
| NEA2 | 128-NEA2 | AES-128 CTR | AES计数器模式 |
| NEA3 | 128-NEA3 | ZUC | 中国国密算法 |
关键设计细节:COUNT值的滚动
COUNT是一个32位的"永不回头"计数器。低12位(或18位)是PDCP SN,高位是HFN。SN溢出回绕时,HFN自动加1。这意味着每个PDCP包都对应唯一的COUNT值,同一个密钥绝不会对相同的keystream加密两次(否则就破解了——"两时间垫"攻击)。
graph LR
subgraph COUNT值结构["COUNT值(32位)"]
HFN["HFN(超帧号)<br/>20位 或 14位"]
SN["PDCP SN<br/>12位 或 18位"]
end
SN --"溢出回绕"--> HFN
subgraph 加密输入["加密算法输入"]
KEY["密钥 KEY<br/>128位"]
COUNT_IN["COUNT<br/>32位"]
BEARER["承载ID<br/>5位"]
DIR["方向<br/>1位"]
end
KEY --> CRYPTO["加密算法<br/>NEA1/2/3"]
COUNT_IN --> CRYPTO
BEARER --> CRYPTO
DIR --> CRYPTO
CRYPTO --> KS["Keystream<br/>伪随机序列"]
KS --> XOR["⊕ 明文"]
XOR --> CIPHER["密文"]
style CRYPTO fill:#e74c3c,color:#fff
style XOR fill:#f39c12,color:#fff
完整性保护与加密不同——它不是为了保密,而是为了防篡改。控制面信令(RRC消息)必须做完整性保护,防止中间人篡改切换命令、测量配置等关键信令。
完整性保护算法(NIA系列)在PDCP PDU末尾附加一个32位的 MAC-I(消息认证码):
$$MAC\text{-}I = NIA(KEY, COUNT, BEARER, DIRECTION, PDU_{header} + PDU_{payload})$$
接收端用同样的密钥和参数计算出X-MAC,与收到的MAC-I比对。如果不一致——对不起,这个包被篡改过,丢弃。
一个容易忽略的调试陷阱:如果基站和手机的COUNT值不同步(比如切换时SN续传出错),即使密钥正确,加密/完整性校验也会失败。这类问题在路测中表现为"加密失败"或"完整性校验失败",但根因往往是PDCP SN同步问题,而非密钥错误。
看一组数据:
| 业务类型 | 有效载荷 | IPv4/UDP/RTP头部 | 头部占比 |
|---|---|---|---|
| VoLTE语音 | 32字节 | 40字节 | 55.6% |
| VoLTE语音 | 16字节 | 40字节 | 71.4% |
| 视频流 | 1500字节 | 40字节 | 2.6% |
对于语音业务,超过一半的空口资源被"地址信息"吃掉了——这就像寄快递时,快递盒上的地址标签比里面的商品还大。ROHC就是来解决这个问题的。
IP/UDP/RTP头部里,大量字段是不变或可预测的:
| 字段类别 | 例子 | 变化规律 |
|---|---|---|
| 完全不变 | 源/目的IP、源/目的端口 | 整个会话期间不变 |
| 可预测增量 | RTP序号、RTP时间戳 | 每个包固定增量 |
| 随机变化 | IP ID(某些实现) | 需要每次传输 |
ROHC的哲学:既然你和我已经知道大部分信息,就不用每次都重复了。就像老朋友打电话,第一次说"我是北京市海淀区的张三",以后只需说"我"——双方都知道上下文。
stateDiagram-v2
[*] --> IR : 建立承载
IR --> FO : 收到确认<br/>建立部分上下文
FO --> SO : 收到确认<br/>完整上下文建立
SO --> FO : 检测到<br/>上下文变化
FO --> IR : 多次未确认<br/>上下文丢失
SO --> IR : 多次未确认<br/>上下文丢失
note right of IR
IR状态(初始化与刷新)
发送完整头部
压缩后约20-40字节
end note
note right of FO
FO状态(一阶压缩)
只发送变化字段
压缩后约5-10字节
end note
note right of SO
SO状态(二阶压缩)
只发送增量/序号
压缩后仅1-3字节!
end note
压缩率的演进:
| 状态 | 压缩后头部大小 | 压缩率(40字节原始头部) |
|---|---|---|
| IR状态 | ~20-40字节 | 0%-50% |
| FO状态 | ~5-10字节 | 75%-87.5% |
| SO状态 | 1-3字节 | 92.5%-97.5% |
ROHC中的"R"代表Robust(健壮的)。与早期的IP头压缩(RFC 2507)不同,ROHC能容忍一定的丢包而不会丢失上下文。它通过W-LSB编码(窗口最低有效位)技术,即使丢了几个包,接收端也能通过滑动窗口推断出正确的字段值。
不同业务用不同的压缩策略:
| Profile ID | 压缩对象 | 典型应用 |
|---|---|---|
| 0x0000 | 无压缩 | 调试/回退 |
| 0x0001 | RTP/UDP/IP | VoLTE/VoNR语音 |
| 0x0002 | UDP/IP | 视频流、游戏 |
| 0x0003 | ESP/IP | VPN隧道 |
| 0x0004 | IP | 纯IP数据 |
| 0x0006 | TCP/IP | 网页浏览 |
实战连接:在VoLTE优化中,如果发现语音MOS分异常低但无线质量良好,检查一下ROHC是否正确协商。如果ROHC Profile没有匹配上(比如应该用0x0001但协商成了0x0000),头部没有压缩,语音包在空口传输效率骤降,可能导致延迟和抖动增加。
数据包到达PDCP层时可能乱序,原因包括:
PDCP接收端维护一个重排序窗口,核心参数:
| 参数 | 含义 |
|---|---|
| RX_NEXT | 期望接收的下一个PDCP SN |
| RX_DELIV | 第一个未交付给上层的PDCP SN |
| t-Reordering | 重排序定时器 |
| reordering_Window | 窗口大小(SN长度的一半) |
重排序的工作逻辑:
flowchart TD
RECV["收到PDCP PDU<br/>SN = X"] --> CHECK1{"X 在窗口内?"}
CHECK1 -- 否 --> DROP["丢弃"]
CHECK1 -- 是 --> CHECK2{"X == RX_DELIV?<br/>正好是期望的序号"}
CHECK2 -- 是 --> DELIVER["立即交付给上层<br/>更新 RX_DELIV"]
CHECK2 -- 否 --> BUFFER["缓存该PDU"]
BUFFER --> CHECK3{"重排序定时器<br/>正在运行?"}
CHECK3 -- 否 --> CHECK4{"X > RX_DELIV?<br/>(发现了缺口)"}
CHECK4 -- 是 --> START["启动 t-Reordering<br/>等待缺失的包"]
CHECK3 -- 是 --> WAIT["继续等待"]
CHECK4 -- 否 --> WAIT2["不启动定时器<br/>(填入已有缺口后面的包)"]
TIMER["t-Reordering超时"] --> FORWARD["将 RX_DELIV 到<br/>第一个缓存包之前的<br/>所有PDU交付上层"]
style DELIVER fill:#27ae60,color:#fff
style START fill:#e67e22,color:#fff
style TIMER fill:#e74c3c,color:#fff
style FORWARD fill:#8e44ad,color:#fff
通俗理解:想象你在收编号1-100的快递。你已经收到1-5号,第6号没来但7、8号来了。你把7、8号先放着(缓存),等第6号(启动定时器)。如果定时器到了第6号还没来——算了,7、8号先上交,第6号当丢了。
| 承载类型 | SN长度 | 范围 |
|---|---|---|
| SRB(信令承载) | 5位 | 0-31 |
| DRB(数据承载) | 12位 | 0-4095 |
| DRB(数据承载) | 18位 | 0-262143 |
为什么有12位和18位两种选择? 12位的SN在高速业务下很快回绕(一个100Mbps的TCP连接,约0.4秒就会绕一圈),可能导致COUNT值快速增长。18位SN提供了更大的"缓冲",适合高速率场景。但SN越长,PDCP头部开销越大(12位SN头部1字节,18位SN头部3字节)。
这是5G NR新增的重要功能,专为URLLC(超可靠低延迟) 场景设计。
同一个PDCP PDU被复制两份,分别通过两个独立的RLC实体发送。这两条路径可以是: - 同一MAC实体下的不同载波(CA场景) - 不同MAC实体(DC场景,即双连接)
graph TD
SDU["PDCP SDU<br/>(原始数据包)"]
SDU --> ADD_SN["添加PDCP SN<br/>编号"]
ADD_SN --> ENCRYPT["加密"]
ENCRYPT --> COPY{"PDCP复制<br/>激活?"}
COPY -- 是 --> PATH1["路径1<br/>RLC实体1 → MAC1 → 载波1"]
COPY -- 是 --> PATH2["路径2<br/>RLC实体2 → MAC2 → 载波2"]
COPY -- 否 --> PATH_ONLY["单一RLC实体<br/>正常传输"]
PATH1 --> RECV["接收端 PDCP"]
PATH2 --> RECV
PATH_ONLY --> RECV
RECV --> DEDUP["重复检测<br/>丢弃副本"]
DEDUP --> UPPER["交付上层"]
style COPY fill:#e74c3c,color:#fff
style DEDUP fill:#27ae60,color:#fff
| 方面 | 有利 | 有代价 |
|---|---|---|
| 可靠性 | 两条路径独立,一条断了另一条还在 | 空口资源消耗翻倍 |
| 延迟 | 不用等重传,哪个先到用哪个 | 额外的PDCP头部和MAC CE开销 |
| 适用场景 | 工业控制、远程手术、车联网 | 不适合大流量业务(太浪费资源) |
激活方式:PDCP复制可以动态开关——通过MAC CE(Control Element)快速激活/去激活。当信道质量好时关闭复制节省资源,信道变差时瞬间打开复制保证可靠性。
切换是PDCP最繁忙的时刻之一。它要做这些事:
sequenceDiagram
participant UE as 手机(UE)
participant Source as 源gNB
participant Target as 目标gNB
Source->>UE: RRC Reconfiguration(切换命令)
Note over UE: 暂停上行发送<br/>保存PDCP状态
Source->>Target: 转发未确认的<br/>下行PDCP SDU
Source->>Target: 转发缓存的<br/>上行PDCP SDU
Note over Target: 按SN排序<br/>填补缺口
UE->>Target: RRC Reconfiguration Complete
Note over UE,Target: 密钥更新<br/>SN续传/重置
Target->>UE: 继续数据传输
切换时必须更新加密密钥,防止源基站继续解密用户在新基站的数据(前向安全)。5G通过密钥衍生函数,用源密钥和目标PCI/频率生成新密钥:
$$K_{NG-RAN^{*}} = KDF(K_{NG-RAN}, PCI \parallel ARFCN)$$
切换时PDCP SN有两种处理模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| SN续传 | 目标基站从源基站的SN继续编号 | AM模式DRB,保证数据连续性 |
| SN重置 | 目标基站从0开始重新编号 | UM模式DRB或SRB |
调试经验:切换后的第一个PDCP包最容易出问题。如果看到切换完成后立即出现"加密失败"或"头压缩上下文丢失",通常是因为: 1. COUNT值没有正确同步 2. ROHC上下文没有正确转移 3. 密钥更新时序出错
PDCP层还有一个容易被忽略的定时器:Discard Timer(丢弃定时器)。
每个PDCP SDU从上层到达时启动一个Discard Timer。如果这个包在定时器到期前还没被下层(RLC/MAC/PHY)成功发出去——直接丢弃,不发了。
为什么这样设计?
在实时业务(如语音、视频通话)中,迟到的数据包没有意义。如果一个语音包已经延迟了100ms才发出去,对方收到时已经过了播放时间,只会造成回声或卡顿。不如直接扔掉,让上层重传或跳过。
| 场景 | 建议Discard Timer值 |
|---|---|
| VoLTE/VoNR语音 | 20-40ms |
| 视频流 | 100-200ms |
| TCP数据(eMBB) | 不配置或很大 |
| URLLC工业控制 | 5-10ms |
PDCP有两种PDU:数据PDU和控制PDU。
┌──────────────────────────────────────────────────┐
│ PDCP Header │
│ ┌─────────┬────────────┬──────────┐ │
│ │ D/C │ R │ PDCP SN │ │ │
│ │(1b) │ │(12b/18b) │ │ │
│ └─────────┴────────────┴──────────┘ │
├──────────────────────────────────────────────────┤
│ Payload(加密后的数据/头压缩后的数据) │
├──────────────────────────────────────────────────┤
│ MAC-I(仅控制面,32位) │
└──────────────────────────────────────────────────┘
| 控制PDU类型 | 功能 |
|---|---|
| ROHC反馈 | 解压器告诉压缩机上下文状态 |
| PDCP Status Report | 切换时告知对方哪些SN已收到/未收到 |
| Ethernet头压缩反馈 | 5G新增加的以太网头压缩 |
Status Report在切换时尤其重要——目标基站通过它告诉手机"我已经收到了SN 1-100,请从101开始发"。这避免了切换后的数据重复和丢失。
现象:无线指标正常(RSRP>-90dBm,SINR>15dB),但语音MOS分低于3.0。
排查路径: 1. 检查PDCP层ROHC是否激活(Profile是否正确协商为0x0001) 2. 检查PDCP层丢包率(PDCP SDU discard rate) 3. 检查Discard Timer配置是否过小(可能误杀正常的语音包) 4. 检查12位SN是否够用(高速率场景应考虑18位SN)
现象:切换完成后,下行数据中断200-500ms。
排查路径: 1. 检查数据转发是否正常(源gNB是否转发了未确认的PDCP SDU) 2. 检查目标gNB的PDCP重排序窗口是否正确设置 3. 检查t-Reordering定时器是否过大(导致等待时间过长) 4. 检查密钥更新是否成功(COUNT值是否同步)
现象:工业控制指令偶尔丢失,端到端可靠性达不到99.999%。
排查路径: 1. 检查PDCP复制是否激活 2. 检查两条路径是否真正独立(不同载波/不同MAC实体) 3. 检查MAC CE动态激活/去激活时延 4. 检查重复检测逻辑是否正确(避免把重复包误当新包)
| 特性 | 4G LTE PDCP | 5G NR PDCP |
|---|---|---|
| 头压缩 | ROHC仅用于用户面 | ROHC + 以太网头压缩 |
| 完整性保护 | 仅控制面 | 控制面必须,用户面可选 |
| SN长度 | 5/7/12位 | 5/12/18位 |
| PDCP复制 | 不支持 | 支持(CA/DC) |
| 乱序交付 | 不支持 | 支持(outOfOrderDelivery) |
| 路由到多个RLC | 不支持 | 支持辅助RLC实体 |
| 加密算法 | EEA0/1/2/3 | NEA0/1/2/3(密钥体系不同) |
最大变化:5G PDCP新增了PDCP复制功能,这是4G完全没有的能力,直接服务于URLLC场景。同时18位SN的引入,使PDCP能适应更高的数据速率。
Q: PDCP加密和NAS加密有什么区别?谁保护的范围更大? A: NAS加密保护的是手机和核心网(AMF)之间的信令,只保护NAS消息。PDCP加密保护的是空口段(手机和基站之间),覆盖所有用户数据和RRC信令。两者是独立的加密层,5G采用"双重加密"理念——NAS信令经过NAS加密后,再经过PDCP加密,实现端到端+空口的双重保护。
Q: 为什么VoLTE的头压缩效果这么明显,但刷网页时几乎没有感觉? A: 头压缩的收益取决于"头部占总包的比例"。语音包很小(约32字节),40字节的IP/UDP/RTP头部占比超过50%,压缩到1-3字节后效率翻倍。而网页浏览的TCP包通常是1500字节,头部只占2.6%,压缩收益微乎其微。所以ROHC对VoLTE/VoNR是刚需,对大数据业务是锦上添花。
Q: PDCP重排序和RLC AM的按序交付有什么区别?不会重复吗? A: RLC AM确实保证按序交付给PDCP,但有几个场景RLC无法覆盖:(1) 切换时源基站和目标基站的数据合并;(2) PDCP复制场景下两条路径的数据合并;(3) RLC UM模式不保证顺序。PDCP的重排序是"最后一道关卡",覆盖所有乱序场景。
Q: t-Reordering定时器设置多大合适? A: 典型值为10-50ms。设置太小,正常延迟的包会被误判为丢失;设置太大,上层需要等待更久才能拿到数据。建议根据业务类型调整:语音业务用小值(10-20ms),数据业务可适当放大(30-50ms)。实际上,这个参数是网络侧通过RRC信令配置给手机的。
Q: PDCP复制会浪费一半的空口资源吗?什么时候该用? A: 从空口资源角度确实如此——每个包发两份。但URLLC业务(工业控制、远程手术)的数据量通常很小(一个指令可能只有几十字节),而可靠性的收益巨大。所以PDCP复制适合"小包、高可靠"场景,不适合视频流等大流量场景。5G的动态激活机制也允许按需开关——信道好时关闭,信道差时打开。
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
