在IMS/VoNR中,初始INVITE建立SIP会话并完成第一次SDP协商。但通话过程中媒体参数并非一成不变——用户可能发起呼叫保持、添加视频流、切换编解码器、或网络侧发起QoS调整。这些场景的核心承载机制就是SIP re-INVITE。
re-INVITE与初始INVITE共享同一方法名(INVITE),但它在已建立的Dialog内部发送,携带新的SDP Offer来修改会话参数。3GPP TS 24.229 Section 5.4.2.1明确定义了UE发起的会话修改过程,而TS 24.610则专门规范了基于re-INVITE的Communication Hold补充业务。
关键设计意图:re-INVITE复用Dialog状态(Call-ID、From/To tag、CSeq序列),避免重新建立完整的注册-认证-会话链路,将"修改"的开销压到最低。同时,SDP Offer/Answer模型(RFC 3264)保证了双方对媒体参数变更的严格同步。
| 场景 | 触发方 | SDP变更 | 规范依据 |
|---|---|---|---|
| 呼叫保持(Hold) | UE-A | a=sendonly或a=inactive |
TS 24.610 Section 4 |
| 呼叫恢复(Resume) | UE-A | a=sendrecv |
TS 24.610 Section 4 |
| 音频→视频添加 | UE | 新增m=video行 |
TS 26.114 Annex A.11 |
| 编解码器切换 | UE/AS | 修改m=audio的codec列表 |
TS 24.229 Section 5.4.2.1 |
| QoS降级(网络侧) | PCRF→P-CSCF→UE | 修改带宽b=AS/b=RR/b=RS |
TS 29.214 Rx接口 |
| MMTel业务触发 | AS(经S-CSCF/ISC) | 添加/删除媒体流 | TS 24.623 |
| 会话定时器刷新 | 任一方 | SDP不变,仅刷新Dialog | RFC 4028 |
| 紧急呼叫补充信息 | E-CSCF | 更新位置信息 | TS 24.229 Section 5.4.3 |
以下以UE-A发起呼叫保持→UE-B响应→恢复为典型场景,展示完整的IMS信令链路:
sequenceDiagram
participant UE_A as UE-A (发起方)
participant P_CSCF_A as P-CSCF-A
participant PCF_A as PCF/PCRF-A
participant S_CSCF_A as S-CSCF-A
participant AS as MMTel AS
participant S_CSCF_B as S-CSCF-B
participant P_CSCF_B as P-CSCF-B
participant PCF_B as PCF/PCRF-B
participant UE_B as UE-B (接收方)
Note over UE_A,UE_B: === 阶段1: 呼叫保持 (Hold) ===
UE_A->>P_CSCF_A: re-INVITE (SDP Offer: a=sendonly)
Note right of UE_A: Call-ID不变<br/>CSeq递增<br/>SDP: m=audio 5004 RTP/AVP 96<br/>a=sendonly
P_CSCF_A->>PCF_A: AAR (媒体变更授权)
PCF_A-->>P_CSCF_A: AAA (QoS策略更新)
Note over P_CSCF_A,PCF_A: Rx接口:因方向变更<br/>需重新授权QoS
P_CSCF_A->>S_CSCF_A: re-INVITE (转发)
S_CSCF_A->>AS: re-INVITE (ISC触发)
Note over S_CSCF_A,AS: iFC匹配: HOLD业务<br/>AS执行业务逻辑
AS-->>S_CSCF_A: re-INVITE (AS透传/修改)
S_CSCF_A->>S_CSCF_B: re-INVITE (转发)
S_CSCF_B->>P_CSCF_B: re-INVITE (转发)
P_CSCF_B->>PCF_B: AAR (媒体变更授权)
PCF_B-->>P_CSCF_B: AAA (QoS策略更新)
P_CSCF_B->>UE_B: re-INVITE (SDP Offer: a=sendonly)
UE_B-->>P_CSCF_B: 200 OK (SDP Answer: a=recvonly)
Note left of UE_B: SDP应答方向:<br/>对端sendonly→本端recvonly
P_CSCF_B-->>S_CSCF_B: 200 OK
S_CSCF_B-->>S_CSCF_A: 200 OK
S_CSCF_A-->>AS: 200 OK
AS-->>S_CSCF_A: 200 OK (透传)
S_CSCF_A-->>P_CSCF_A: 200 OK
P_CSCF_A-->>UE_A: 200 OK (SDP Answer: a=recvonly)
UE_A->>P_CSCF_A: ACK
P_CSCF_A->>S_CSCF_A: ACK
S_CSCF_A->>AS: ACK
AS-->>S_CSCF_A: ACK
S_CSCF_A->>S_CSCF_B: ACK
S_CSCF_B->>P_CSCF_B: ACK
P_CSCF_B->>UE_B: ACK
Note over UE_A,UE_B: === 阶段2: 呼叫恢复 (Resume) ===
UE_A->>P_CSCF_A: re-INVITE (SDP Offer: a=sendrecv)
Note right of UE_A: 恢复双向媒体<br/>SDP version递增
P_CSCF_A->>PCF_A: AAR (恢复双向QoS)
PCF_A-->>P_CSCF_A: AAA
P_CSCF_A->>S_CSCF_A: re-INVITE
S_CSCF_A->>AS: re-INVITE
AS-->>S_CSCF_A: re-INVITE
S_CSCF_A->>S_CSCF_B: re-INVITE
S_CSCF_B->>P_CSCF_B: re-INVITE
P_CSCF_B->>PCF_B: AAR (恢复双向QoS)
PCF_B-->>P_CSCF_B: AAA
P_CSCF_B->>UE_B: re-INVITE (SDP Offer: a=sendrecv)
UE_B-->>P_CSCF_B: 200 OK (SDP Answer: a=sendrecv)
P_CSCF_B-->>UE_A: 200 OK (经全链路返回)
UE_A->>UE_B: ACK (经全链路)
re-INVITE的核心在于SDP方向属性的协商。RFC 3264 Section 6与RFC 6337定义了方向属性的组合规则:
| UE-A Offer方向 | UE-B Answer方向 | 媒体流效果 | 典型场景 |
|---|---|---|---|
a=sendonly |
a=recvonly |
UE-A发→UE-B收 | 呼叫保持+放音 |
a=sendonly |
a=inactive |
双向无媒体 | 保持+静音 |
a=inactive |
a=inactive |
双向无媒体 | 深度保持 |
a=sendrecv |
a=sendrecv |
双向恢复 | 呼叫恢复 |
a=recvonly |
a=sendonly |
UE-B发→UE-A收 | 网络侧放音 |
a=sendrecv |
a=sendonly |
单向(异常) | 不对称协商 |
RFC 3264规定:每次SDP Offer必须将o=行的<session-version>字段递增。这是接收方判断SDP是否变更的唯一依据:
// 初始会话
o=- 123456 1 IN IP4 10.1.1.1
// 第一次re-INVITE (Hold)
o=- 123456 2 IN IP4 10.1.1.1
// ^ 递增
// 第二次re-INVITE (Resume)
o=- 123456 3 IN IP4 10.1.1.1
// ^ 再次递增
关键细节:<session-id>(123456)在会话生命周期内保持不变,只有<session-version>递增。如果版本号未变,RFC 3264 Section 5要求接收方将SDP视为不变,可能忽略本次Offer。
UE-A发起Hold的re-INVITE消息体:
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP [2001:db8::1]:5060;branch=z9hG4bK-hold123
Call-ID: a84b4c76de667010@2001:db8::1
From: <sip:[email protected]>;tag=1928301774
To: <sip:[email protected]>;tag=a6c85cf
CSeq: 314162 INVITE ← CSeq必须递增
Contact: <sip:ue_a@[2001:db8::1]:5060>
Content-Type: application/sdp
Content-Length: ...
v=0
o=- 123456 2 IN IP4 10.1.1.1 ← version从1→2
s=-
c=IN IP4 0.0.0.0 ← HOLD方案一:地址置0
t=0 0
m=audio 5004 RTP/AVP 96 97
a=rtpmap:96 EVS/16000/1
a=rtpmap:97 AMR-WB/16000/1
a=fmtp:96 evs-mode-switch=1; bw=16000
a=sendonly ← 核心变更:方向改为sendonly
a=ptime:20
a=maxptime:240
两种Hold实现方案的博弈:
| 方案 | SDP表示 | 兼容性 | 优势 | 劣势 |
|---|---|---|---|---|
| 方案一 | c=IN IP4 0.0.0.0 + a=sendonly |
传统设备友好 | RFC 2543遗留兼容 | 不符合RFC 6337推荐 |
| 方案二 | c=IN IP4 10.1.1.1(保持原地址)+ a=sendonly |
RFC 6337推荐 | 标准化方向明确 | 部分旧设备不识别 |
| 方案三 | a=inactive |
最保守 | 完全停止RTP | 无法接收保持音 |
TS 24.610 Section 4.2明确要求IMS UE应使用a=sendonly方向属性表示Hold,而非使用0.0.0.0地址。但P-CSCF必须同时支持两种方案以兼容遗留设备。
当UE-A和UE-B同时在同一个Dialog内发送re-INVITE时,产生Glare(碰撞)。因为两个re-INVITE都试图修改同一会话状态,无法同时处理。RFC 3261 Section 14.2与RFC 6141 Section 4.3定义了Glare的处理机制。
sequenceDiagram
participant UE_A as UE-A
participant P_CSCF as P-CSCF/S-CSCF
participant UE_B as UE-B
Note over UE_A,UE_B: Glare场景:双方同时发re-INVITE
UE_A->>P_CSCF: re-INVITE (SDP Offer-A1)
UE_B->>P_CSCF: re-INVITE (SDP Offer-B1)
P_CSCF->>UE_B: 转发 re-INVITE (Offer-A1)
P_CSCF->>UE_A: 转发 re-INVITE (Offer-B1)
Note over UE_A: UE-A收到对端re-INVITE<br/>但自己还有未完成的re-INVITE
UE_A-->>P_CSCF: 491 Request Pending
Note right of UE_A: Retry-After: 3<br/>(随机值2.1~4s)
UE_B-->>P_CSCF: 491 Request Pending
Note left of UE_B: Retry-After: 1.5<br/>(随机值0~2s)
Note over UE_A,UE_B: 双方各自等待Retry-After后重试
Note over UE_B: UE_B先到时(1.5s < 3s)
UE_B->>P_CSCF: re-INVITE (SDP Offer-B1 重试)
P_CSCF->>UE_A: re-INVITE (Offer-B1)
UE_A-->>P_CSCF: 200 OK (SDP Answer-A1)
P_CSCF-->>UE_B: 200 OK (Answer-A1)
UE_B->>UE_A: ACK
RFC 3261 Section 14.1定义了Retry-After值的计算规则:
| 角色 | Retry-After范围 | 设计意图 |
|---|---|---|
| UAC(主叫方/Dialog创建者) | 2.1s ~ 4.0s(随机) | 较大范围,优先让步 |
| UAS(被叫方/Dialog非创建者) | 0s ~ 2.0s(随机) | 较小范围,优先重试 |
设计博弈:非Dialog创建者(通常是MT侧)拥有更短的重试等待时间,这确保了一方先发,避免无限Glare循环。这种非对称设计使得碰撞在一次重试后几乎必然解决。
RFC 6141修复了RFC 3261在re-INVITE处理中的多个歧义:
| 问题 | RFC 3261行为 | RFC 6141修正 |
|---|---|---|
| 未确认的re-INVITE | 允许叠加新re-INVITE | 必须先完成/取消当前re-INVITE |
| re-INVITE失败 | 未明确会话状态 | 明确:会话参数不变,回退到修改前 |
| 1xx响应中的SDP | 歧义 | re-INVITE的1xx不携带SDP(与初始INVITE不同) |
| CSeq冲突 | 未明确 | glared re-INVITE重试时CSeq必须递增 |
IMS中修改会话有两种SIP方法:re-INVITE和UPDATE。理解二者的区别对协议工程师至关重要。
| 维度 | re-INVITE | UPDATE |
|---|---|---|
| SIP方法 | INVITE(Dialog内重发) | UPDATE |
| 影响范围 | 会话参数 + Dialog状态 | 仅会话参数 |
| 早期Dialog支持 | 否(Dialog必须已Established) | 是(可在1xx之后发送) |
| 需要ACK | 是(需ACK确认) | 否(200 OK即完成) |
| 触发振铃 | 可能触发(某些实现) | 不触发 |
| Session Timer刷新 | 是(同时刷新定时器) | 否 |
| RFC依据 | RFC 3261 Section 14 | RFC 3311 |
| TS 24.229章节 | Section 5.4.2.1 | Section 5.4.2.2 |
flowchart TD
A[需要修改会话参数] --> B{Dialog是否已Established?}
B -->|否, 早期Dialog| C[使用UPDATE]
B -->|是| D{是否需要同时刷新Dialog?}
D -->|是| E[使用re-INVITE]
D -->|否| F{对端是否支持UPDATE?}
F -->|支持| G[优先使用UPDATE<br/>更轻量无需ACK]
F -->|不确定| H[使用re-INVITE<br/>兼容性更好]
G --> I{是否涉及媒体方向变更<br/>如Hold/Resume?}
I -->|是| J[多数实现用re-INVITE]
I -->|否| K[使用UPDATE]
实战经验:虽然RFC 3311允许UPDATE修改已建立会话的SDP,但大多数IMS厂商(华为、爱立信、诺基亚)在Hold/Resume场景下使用re-INVITE而非UPDATE。原因是re-INVITE能同时刷新Session Timer,减少一次额外的定时器刷新信令。UPDATE更多用于初始INVITE未完成前的SDP更新(如Preconditions场景中的资源预留确认),这一场景在VoNR SIP Preconditions文章中已有详细分析。
re-INVITE修改媒体参数后,P-CSCF必须通过Rx接口(TS 29.214)向PCF/PCRF发起QoS重授权。这是re-INVITE全链路中容易被忽视但至关重要的环节。
当P-CSCF检测到re-INVITE中的SDP变更时:
sequenceDiagram
participant UE as UE
participant P_CSCF as P-CSCF
participant PCF as PCF/PCRF
participant SMF as SMF
participant UPF as UPF
UE->>P_CSCF: re-INVITE (SDP变更)
Note over P_CSCF: 1. 解析SDP变更<br/>2. 提取新的带宽/方向参数
P_CSCF->>PCF: AAR (Diameter)
Note right of P_CSCF: Media-Component-Description<br/>含新的带宽/方向
PCF->>PCF: 策略决策<br/>评估新QoS需求
PCF-->>P_CSCF: AAA (授权结果)
Note left of PCF: 含授权的QoS参数
PCF->>SMF: SM策略更新(N7)
SMF->>UPF: N4 Session Modification
Note over SMF,UPF: 更新GTP-U过滤器<br/>调整QoS Flow参数
P_CSCF->>UE: 转发re-INVITE到下一跳
| AVP名称 | Hold场景值 | Resume场景值 | 说明 |
|---|---|---|---|
Media-Component-Status |
0 (UNCHANGED但方向变) | 0 | 组件未增删 |
Flow-Status |
2 (DISABLED) 或 3 (ENABLED-DOWN) | 1 (ENABLED) | 控制媒体方向 |
RR-Bandwidth |
0 (不接收RTCP) | 恢复原值 | Hold时降为0 |
RS-Bandwidth |
0 (不发送RTCP) | 恢复原值 | Hold时降为0 |
Max-Requested-Bandwidth |
降低 | 恢复 | 带宽适配 |
TS 26.114 Annex A.11定义了在已建立的音频通话中通过re-INVITE添加视频流的完整流程。这是VoLTE/VoNR中最复杂的re-INVITE场景之一。
v=0
o=- 123456 4 IN IP4 10.1.1.1 ← version递增到4
s=-
c=IN IP4 10.1.1.1
t=0 0
m=audio 5004 RTP/AVP 96 97 ← 音频流保持
a=rtpmap:96 EVS/16000/1
a=rtpmap:97 AMR-WB/16000/1
a=sendrecv
m=video 5006 RTP/AVPF 98 99 ← 新增视频流
a=rtpmap:98 H265/90000
a=rtpmap:99 H264/90000
a=fmtp:98 profile-id=1;tier-flag=0
a=fmtp:99 profile-level-id=42C01F
a=sendrecv
a=rtcp-fb:98 nack pli ← PLI关键帧请求
a=rtcp-fb:98 ccm fir ← FIR全帧内请求
| SDP字段 | 值 | 含义 |
|---|---|---|
m=video 5006 |
端口5006 | 视频RTP端口,与音频分离 |
RTP/AVPF |
AVPF配置文件 | 支持RTCP反馈(RFC 4585),视频必需 |
a=rtpmap:98 H265 |
H.265编解码 | 优先H.265,回退H.264 |
a=rtcp-fb:98 nack pli |
Picture Loss Indication | 丢包时请求关键帧 |
a=rtcp-fb:98 ccm fir |
Full Intra Request | 全帧内编码请求 |
如果对端不支持视频,返回的SDP Answer中m=video行端口为0:
m=video 0 RTP/AVPF 98 99 ← 端口=0表示拒绝该媒体流
RFC 3264 Section 5明确:端口为0表示拒绝该媒体流,且此时不应为该流分配任何资源。发起方收到m=video 0后,会话保持纯音频,不会因视频协商失败而中断原有音频通话。
stateDiagram-v2
[*] --> Stable: Dialog Established
Stable --> InProgress: 发送re-INVITE<br/>(CSeq递增)
InProgress --> Received491: 收到491<br/>(Glare)
Received491 --> WaitingRetry: 读取Retry-After
WaitingRetry --> InProgress: 定时器到期<br/>重发re-INVITE
InProgress --> Stable: 收到200 OK + 发送ACK<br/>(修改成功)
InProgress --> Stable: 收到4xx/5xx/6xx<br/>(修改失败,回退原参数)
InProgress --> ReceivedGlare: 收到对端re-INVITE<br/>(同时自己有待完成re-INVITE)
ReceivedGlare --> Sent491: 回复491 Request Pending
Sent491 --> Stable: 收到对端200 OK/失败
Stable --> [*]: BYE终止会话
# 过滤所有re-INVITE
sip.Method == "INVITE" && sip.CSeq > 1
# 过滤Hold场景的re-INVITE
sip.Method == "INVITE" && sdp.media_attr.contains("sendonly")
# 过滤491响应
sip.Status-Code == 491
# 过滤视频添加re-INVITE
sip.Method == "INVITE" && sdp.media_name.contains("video")
# 过滤特定Call-ID的所有re-INVITE
sip.Call-ID == "a84b4c76de667010" && sip.Method == "INVITE"
| 故障现象 | 根因分析 | 排查方法 |
|---|---|---|
| re-INVITE后单向通话 | SDP方向属性协商错误 | 检查a=sendonly/a=recvonly配对 |
| Hold后无法恢复 | Resume时SDP version未递增 | 检查o=行version是否递增 |
| 视频添加后黑屏 | 编解码协商不一致 | 检查a=rtpmap/a=fmtp是否匹配 |
| 491无限循环 | Retry-After随机值算法错误 | 抓包分析双方Retry-After值范围 |
| re-INVITE被拒绝(488) | P-CSCF QoS授权失败 | 检查Rx接口AAR/AAA日志 |
| CSeq不连续 | 中间节点错误修改CSeq | 逐跳检查CSeq值 |
| SDP无变更触发re-INVITE | 会话定时器到期 | 检查Session-Expires头域 |
| ## 11. 错误响应处理矩阵 |
| 响应码 | 含义 | UE行为 | TS 24.229章节 |
|---|---|---|---|
| 200 OK | 修改接受 | 发送ACK,应用新参数 | Section 5.4.2.1.2 |
| 488 Not Acceptable Here | SDP不可接受 | 保持原参数,可选降低要求重试 | Section 5.4.2.1.4 |
| 491 Request Pending | Glare碰撞 | 等待Retry-After后重试 | RFC 3261 Section 14.1 |
| 500 Server Internal Error | 服务端故障 | 重试(递增CSeq),达到上限后放弃 | Section 5.4.2.1.4 |
| 503 Service Unavailable | 服务过载 | 检查Retry-After头域 | Section 5.4.2.1.4 |
| 580 Precondition Failure | 前提条件不满足 | 检查资源预留状态 | Section 5.4.2.1.4 |
重要原则:re-INVITE失败后,原有会话参数不受影响(RFC 6141 Section 3.3)。这是re-INVITE的安全网——修改失败不会导致通话中断。
re-INVITE引起的QoS变更通过IMS核心网传递到5GC,影响链路为:
P-CSCF →(Diameter/Rx)→ PCF →(N7)→ SMF →(N4)→ UPF
| re-INVITE变更 | 5GC动作 | QoS Flow影响 |
|---|---|---|
| 音频→音频+视频 | PCF下发新PCC Rule | 新增QoS Flow(通常5QI=2 for video) |
| Hold(sendonly) | PCF修改Flow-Status | 下行QoS Flow可能释放 |
| 带宽变更 | PCF修改Bandwidth | QoS Flow的GBR/MBR调整 |
| 编解码切换 | PCF评估带宽需求 | 可能调整QoS参数 |
在VoNR场景下,视频添加会触发5GC建立专用的QoS Flow(5QI=2,对应Conversational Video),与语音QoS Flow(5QI=1)并行存在。SMF通过N4接口指示UPF为新的视频流配置GTP-U过滤器。
Q1: re-INVITE和UPDATE在已建立的会话中,IMS推荐用哪个做Hold/Resume? A: TS 24.610 Section 4明确使用re-INVITE实现Hold/Resume。原因是re-INVITE能同时刷新Session Timer,避免额外的定时器刷新信令。UPDATE虽然更轻量(无需ACK),但无法刷新Dialog定时器,且部分IMS设备对UPDATE的支持存在互操作性问题。实战中,95%以上的IMS网络Hold/Resume使用re-INVITE。
Q2: 491 Request Pending的Retry-After值为什么需要随机化? A: 如果双方使用固定的Retry-After值(如都是2秒),则重试时会再次碰撞。随机化确保一方先到时,打破Glare死锁。RFC 3261的设计是让Dialog创建者使用2.1~4秒范围、非创建者使用0~2秒范围,两个区间不重叠,保证一次重试即解决碰撞。
Q3: re-INVITE中SDP的o=行session-id可以改变吗?
A: 不可以。RFC 3264 Section 5规定<session-id>在会话生命周期内必须保持不变。只有<session-version>需要随每次Offer递增。如果session-id意外变化,对端会将此视为全新会话,可能导致媒体协商异常。这在IMS跨厂商互联时是需要重点检查的参数。
Q4: 视频添加re-INVITE被拒绝后,音频通话会中断吗?
A: 不会。RFC 3264 Section 5规定,如果Answer中m=video行的端口为0,表示拒绝该媒体流,但其他已建立的媒体流(音频)不受影响。如果re-INVITE返回4xx/5xx错误响应,则所有媒体参数保持不变,音频通话继续。
Q5: 为什么有些厂商Hold时用c=0.0.0.0有些用a=sendonly?
A: c=0.0.0.0是RFC 2543(SIP协议第一版)的遗留方案,通过将连接地址置零表示停止媒体流。RFC 3264引入了a=sendonly/a=inactive方向属性,RFC 6337进一步推荐使用方向属性。TS 24.610要求IMS UE使用a=sendonly,但网络侧必须兼容两种方案。华为设备在Hold时同时使用c=0.0.0.0和a=sendonly,这是为了最大化向后兼容。
本文由「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 等场景全覆盖。
