13. 发起会话 (Initiating a Session)
13.1 概述 (Overview)
当用户代理客户端希望发起会话 (例如, 音频、视频或游戏) 时, 它会制定INVITE请求。INVITE请求要求服务器建立会话。此请求可能由代理转发, 最终到达一个或多个可能接受邀请的UAS。这些UAS通常需要查询用户是否接受邀请。经过一段时间后, 这些UAS可以通过发送2xx响应来接受邀请 (意味着要建立会话)。如果邀请未被接受, 则会发送3xx、4xx、5xx或6xx响应, 具体取决于拒绝的原因。在发送最终响应之前, UAS还可以发送临时响应 (1xx) 以通知UAC联系被叫用户的进度。
在可能接收到一个或多个临时响应后, UAC将获得一个或多个2xx响应或一个非2xx最终响应。由于接收INVITE的最终响应可能需要很长时间, INVITE事务的可靠性机制与其他请求 (如OPTIONS) 不同。一旦收到最终响应, UAC需要为它收到的每个最终响应发送ACK。发送此ACK的程序取决于响应的类型。对于300到699之间的最终响应, ACK处理在事务层完成并遵循一组规则 (参见第17节)。对于2xx响应, ACK由UAC核心生成。
对INVITE的2xx响应建立会话, 它还在发出INVITE的UA和生成2xx响应的UA之间创建对话。因此, 当从不同的远程UA接收到多个2xx响应时 (因为INVITE被分叉), 每个2xx建立一个不同的对话。所有这些对话都是同一呼叫的一部分。
本节提供有关使用INVITE建立会话的详细信息。支持INVITE的UA还必须支持ACK、CANCEL和BYE。
13.2 UAC处理 (UAC Processing)
13.2.1 创建初始INVITE (Creating the Initial INVITE)
由于初始INVITE表示对话外的请求, 其构造遵循第8.1.1节的程序。INVITE的特定情况需要额外的处理。
推荐的头字段
Allow头字段 (第20.5节) 应该出现在INVITE中。它指示在对话期间可以在发送INVITE的UA上在对话内调用哪些方法。例如, 能够在对话内接收INFO请求的UA [34] 应该包含列出INFO方法的Allow头字段。
Supported头字段 (第20.37节) 应该出现在INVITE中。它列举UAC理解的所有扩展。
Accept头字段 (第20.1节) 可以出现在INVITE中。它指示UA在其接收的响应以及在INVITE建立的对话内发送给它的任何后续请求中可接受哪些Content-Type。Accept头字段对于指示对各种会话描述格式的支持特别有用。
Expires头字段 (第20.19节) 可以由UAC添加以限制邀请的有效性。如果达到Expires头字段中指示的时间并且没有收到INVITE的最终答复, UAC核心应该按照第9节为INVITE生成CANCEL请求。
UAC还可能发现添加Subject (第20.36节)、Organization (第20.25节) 和User-Agent (第20.41节) 等头字段很有用。它们都包含与INVITE相关的信息。
消息体
UAC可以选择向INVITE添加消息体。第8.1.1.10节处理如何构造描述消息体所需的头字段 (其中包括Content-Type)。
对于包含会话描述的消息体有特殊规则 - 它们对应的Content-Disposition是"session"。SIP使用提供/应答模型 (Offer/Answer Model), 其中一个UA发送称为提供 (Offer) 的会话描述, 其中包含会话的建议描述。提供指示所需的通信方式 (音频、视频、游戏)、这些方式的参数 (例如编解码器类型) 以及从应答者接收媒体的地址。另一个UA使用称为应答 (Answer) 的另一个会话描述进行响应, 该描述指示接受哪些通信方式、适用于这些方式的参数以及从提供者接收媒体的地址。提供/应答交换在对话的上下文中进行, 因此如果SIP INVITE导致多个对话, 则每个都是单独的提供/应答交换。提供/应答模型定义了何时可以进行提供和应答的限制 (例如, 在进行提供时不能进行新的提供)。这导致了提供和应答在SIP消息中出现位置的限制。在本规范中, 提供和应答只能出现在INVITE请求和响应以及ACK中。
提供/应答规则
对于初始INVITE事务, 规则是:
-
初始提供必须在INVITE中, 或者如果不在INVITE中, 则在从UAS返回到UAC的第一个可靠的非失败消息中。在本规范中, 那是最终的2xx响应。
-
如果初始提供在INVITE中, 则应答必须在从UAS返回到UAC的与该INVITE相关的可靠非失败消息中。对于本规范, 那仅是对该INVITE的最终2xx响应。相同的确切应答也可以放在应答之前发送的任何临时响应中。UAC必须将它接收到的第一个会话描述视为应答, 并且必须忽略对初始INVITE的后续响应中的任何会话描述。
-
如果初始提供在从UAS返回到UAC的第一个可靠非失败消息中, 则应答必须在该消息的确认中 (在本规范中, 是对2xx响应的ACK)。
-
在发送或接收到第一个提供的应答后, UAC可以根据为该方法指定的规则在请求中生成后续提供, 但仅当它已收到任何先前提供的应答, 并且尚未发送任何尚未获得应答的提供时。
-
一旦UAS发送或接收到初始提供的应答, 它不得在对初始INVITE的任何响应中生成后续提供。这意味着仅基于本规范的UAS在完成初始事务之前永远无法生成后续提供。
具体来说, 上述规则为仅符合本规范的UA指定了两种交换 - 提供在INVITE中, 应答在2xx中 (并且可能也在1xx中, 具有相同的值), 或者提供在2xx中, 应答在ACK中。支持INVITE的所有用户代理必须支持这两种交换。
所有用户代理必须支持会话描述协议 (SDP, Session Description Protocol) (RFC 2327 [1]) 作为描述会话的方法, 并且其用于构造提供和应答的使用必须遵循 [13] 中定义的程序。
刚刚描述的提供-应答模型的限制仅适用于Content-Disposition头字段值为"session"的消息体。因此, INVITE和ACK都可能包含消息体 (例如, INVITE携带照片 (Content-Disposition: render) 而ACK携带会话描述 (Content-Disposition: session))。
如果Content-Disposition头字段缺失, Content-Type为application/sdp的消息体意味着disposition"session", 而其他内容类型意味着"render"。
一旦创建了INVITE, UAC遵循为在对话外发送请求定义的程序 (第8节)。这导致构造最终将发送请求并将响应传递给UAC的客户端事务。
13.2.2 处理INVITE响应 (Processing INVITE Responses)
一旦INVITE传递给INVITE客户端事务, UAC等待INVITE的响应。如果INVITE客户端事务返回超时而不是响应, TU的行为就像收到了408 (Request Timeout) 响应一样, 如第8.1.3节所述。
13.2.2.1 1xx响应
在接收到一个或多个最终响应之前, 可能会到达零个、一个或多个临时响应。INVITE请求的临时响应可以创建"早期对话" (Early Dialog)。如果临时响应在To字段中有标签, 并且如果响应的对话ID与现有对话不匹配, 则使用第12.1.2节中定义的程序构造一个。
仅当UAC需要在初始INVITE事务完成之前在对话内向其对等方发送请求时, 才需要早期对话。临时响应中存在的头字段只要对话处于早期状态就适用 (例如, 临时响应中的Allow头字段包含在对话处于早期状态时可以在对话中使用的方法)。
13.2.2.2 3xx响应
3xx响应可能包含一个或多个Contact头字段值, 提供可能到达被叫方的新地址。根据3xx响应的状态代码 (参见第21.3节), UAC可以选择尝试这些新地址。
13.2.2.3 4xx、5xx和6xx响应
可能会为INVITE接收到单个非2xx最终响应。4xx、5xx和6xx响应可能包含指示可以找到有关错误的其他信息的位置的Contact头字段值。必须忽略后续的最终响应 (仅在错误条件下到达)。
所有早期对话在接收到非2xx最终响应时被视为终止。
在接收到非2xx最终响应后, UAC核心认为INVITE事务完成。INVITE客户端事务处理响应的ACK生成 (参见第17节)。
13.2.2.4 2xx响应
由于分叉代理, 单个INVITE请求可能会在UAC收到多个2xx响应。每个响应由To头字段中的标签参数区分, 每个代表一个不同的对话, 具有不同的对话标识符。
如果2xx响应中的对话标识符与现有对话的对话标识符匹配, 则对话必须转换到"已确认" (Confirmed) 状态, 并且必须使用第12.2.1.2节的程序基于2xx响应重新计算对话的路由集。否则, 必须使用第12.1.2节的程序构造处于"已确认"状态的新对话。
注意, 重新计算的唯一状态是路由集。其他状态 (例如在对话内发送的最高序列号 (远程和本地)) 不会重新计算。仅为了向后兼容性而重新计算路由集。RFC 2543不强制要求在1xx中镜像Record-Route头字段, 仅在2xx中。但是, 我们不能更新对话的整个状态, 因为对话中期请求可能已在早期对话内发送, 例如修改序列号。
UAC核心必须为从事务层接收的每个2xx生成ACK请求。ACK的头字段的构造方式与在对话内发送的任何请求相同 (参见第12节), 但CSeq和与身份验证相关的头字段除外。CSeq头字段的序列号必须与被确认的INVITE相同, 但CSeq方法必须是ACK。ACK必须包含与INVITE相同的凭证。如果2xx包含提供 (基于上述规则), 则ACK必须在其消息体中携带应答。如果2xx响应中的提供不可接受, UAC核心必须在ACK中生成有效的应答, 然后立即发送BYE。
一旦构造了ACK, 就使用 [4] 的程序来确定目标地址、端口和传输。但是, 请求直接传递给传输层进行传输, 而不是客户端事务。这是因为UAC核心处理ACK的重传, 而不是事务层。每当触发ACK的2xx最终响应的重传到达时, 必须将ACK传递给客户端传输。
UAC核心在接收到第一个2xx响应后64*T1秒后认为INVITE事务完成。此时, 所有尚未转换到已建立对话的早期对话都将终止。一旦UAC核心认为INVITE事务完成, 就不再期望有新的2xx响应到达。
如果在确认对INVITE的任何2xx响应后, UAC不想继续该对话, 则UAC必须通过发送BYE请求 (如第15节所述) 来终止对话。
13.3 UAS处理 (UAS Processing)
13.3.1 处理INVITE (Processing of the INVITE)
UAS核心将从事务层接收INVITE请求。它首先执行第8.2节的请求处理程序, 该程序适用于对话内外的请求。
假设这些处理状态在不生成响应的情况下完成, UAS核心执行以下额外的处理步骤:
-
如果请求是包含Expires头字段的INVITE, UAS核心为头字段值中指示的秒数设置计时器。当计时器触发时, 邀请被认为已过期。如果邀请在UAS生成最终响应之前过期, 则应该生成487 (Request Terminated) 响应。
-
如果请求是对话中期请求, 首先应用第12.2.2节中描述的方法独立处理。它也可能修改会话; 第14节提供详细信息。
-
如果请求在To头字段中有标签, 但对话标识符与任何现有对话都不匹配, UAS可能已经崩溃并重新启动, 或者可能已经收到针对不同 (可能失败的) UAS的请求。第12.2.2节提供了在这种情况下实现鲁棒行为的指南。
从这里开始的处理假设INVITE在对话之外, 因此用于建立新会话。
INVITE可能包含会话描述, 在这种情况下, UAS正在为该会话提供提议。用户可能已经是该会话的参与者, 即使INVITE在对话之外。当用户被多个其他参与者邀请到同一多播会议时, 可能会发生这种情况。如果需要, UAS可以使用会话描述中的标识符来检测此重复。例如, SDP在起源 (o) 字段中包含会话ID和版本号。如果用户已经是会话的成员, 并且会话描述中包含的会话参数没有改变, UAS可以静默地接受INVITE (即, 在不提示用户的情况下发送2xx响应)。
如果INVITE不包含会话描述, 则要求UAS参与会话, 并且UAC已要求UAS提供会话的提议。它必须在返回给UAC的第一个非失败可靠消息中提供提议。在本规范中, 那是对INVITE的2xx响应。
UAS可以指示进度、接受、重定向或拒绝邀请。在所有这些情况下, 它使用第8.2.6节中描述的程序制定响应。
13.3.1.1 进度 (Progress)
如果UAS无法立即应答邀请, 它可以选择向UAC指示某种进度 (例如, 指示电话正在振铃)。这是通过101到199之间的临时响应完成的。这些临时响应建立早期对话, 因此除了第8.2.6节的程序外, 还遵循第12.1.1节的程序。UAS可以发送任意数量的临时响应。每个都必须指示相同的对话ID。但是, 这些不会可靠地传递。
如果UAS希望延长时间来应答INVITE, 它将需要请求"扩展"以防止代理取消事务。当事务中的响应之间有3分钟的间隙时, 代理可以选择取消事务。为了防止取消, UAS必须每分钟发送一个非100的临时响应, 以处理临时响应丢失的可能性。
当用户被置于保持状态时, 或者当与PSTN系统互通时, INVITE事务可以持续很长时间, 这些系统允许在不应答呼叫的情况下进行通信。后者在交互式语音响应 (IVR) 系统中很常见。
13.3.1.2 INVITE被重定向 (The INVITE is Redirected)
如果UAS决定重定向呼叫, 则发送3xx响应。300 (Multiple Choices)、301 (Moved Permanently) 或302 (Moved Temporarily) 响应应该包含一个Contact头字段, 其中包含一个或多个要尝试的新地址的URI。响应传递给INVITE服务器事务, 该事务将处理其重传。
13.3.1.3 INVITE被拒绝 (The INVITE is Rejected)
一个常见的场景是被叫方当前不愿意或无法在此端系统接听其他呼叫。在这种情况下应该返回486 (Busy Here)。如果UAS知道没有其他端系统能够接受此呼叫, 则应该发送600 (Busy Everywhere) 响应。但是, UAS通常不太可能知道这一点, 因此通常不会使用此响应。响应传递给INVITE服务器事务, 该事务将处理其重传。
拒绝INVITE中包含的提议的UAS应该返回488 (Not Acceptable Here) 响应。此类响应应该包含Warning头字段值, 解释为什么拒绝提议。
13.3.1.4 INVITE被接受 (The INVITE is Accepted)
UAS核心生成2xx响应。此响应建立对话, 因此除了第8.2.6节的程序外, 还遵循第12.1.1节的程序。
对INVITE的2xx响应应该包含Allow头字段和Supported头字段, 并且可以包含Accept头字段。包含这些头字段允许UAC确定UAS在呼叫期间支持的功能和扩展, 而无需探测。
如果INVITE请求包含提议, 并且UAS尚未发送应答, 则2xx必须包含应答。如果INVITE不包含提议, 则2xx必须包含提议 (如果UAS尚未发送提议)。
一旦构造了响应, 它就传递给INVITE服务器事务。但是请注意, INVITE服务器事务将在收到此最终响应并将其传递给传输后立即被销毁。因此, 有必要定期将响应直接传递给传输, 直到ACK到达。2xx响应以从T1秒开始的间隔传递给传输, 并且对于每次重传加倍, 直到达到T2秒 (T1和T2在第17节中定义)。当收到对响应的ACK请求时, 响应重传停止。这与用于发送响应的任何传输协议无关。
由于2xx是端到端重传的, 因此UAS和UAC之间可能有使用UDP的跳。为了确保在这些跳上可靠传递, 即使UAS的传输是可靠的, 也会定期重传响应。
如果服务器在64*T1秒内重传2xx响应而没有收到ACK, 则对话被确认, 但应该终止会话。这是通过BYE完成的, 如第15节所述。
INVITE事务流程图
UAC 代理 UAS
| | |
|---INVITE-------->| |
| |---INVITE-------->|
| | |
| |<--100 Trying-----|
|<--100 Trying-----| |
| | | (振铃)
| |<--180 Ringing----|
|<--180 Ringing----| |
| | | (接受)
| |<--200 OK---------|
|<--200 OK---------| |
| | |
|---ACK----------->| |
| |---ACK----------->|
| | |
|<=====媒体流=====>|<=====媒体流=====>|
| | |
提供/应答模式总结
| 场景 | 提供位置 | 应答位置 |
|---|---|---|
| 场景1 | INVITE | 2xx响应 (也可能在1xx中) |
| 场景2 | 2xx响应 | ACK |
所有UA必须支持这两种场景!
重要注意事项
-
ACK的特殊性: 对2xx的ACK由UAC核心生成和重传, 不由事务层处理。对非2xx的ACK由事务层处理。
-
多个2xx响应: 由于分叉, 可能收到多个2xx响应, 每个建立不同的对话。
-
早期对话: 1xx响应可以建立早期对话, 用于在会话建立前进行通信。
-
会话描述: 必须支持SDP, 并遵循提供/应答模型。
-
重传机制: 2xx响应由UAS重传, 直到收到ACK或超时 (64*T1秒)。
-
Expires头字段: 可用于限制INVITE的有效期, 过期后应生成CANCEL。
本章小结:
第13章详细描述了使用INVITE方法发起SIP会话的完整过程。INVITE是SIP中最复杂的方法, 因为它建立对话、需要特殊的ACK处理、支持提供/应答模型, 并且具有独特的可靠性机制。理解INVITE的处理对于实现SIP会话功能至关重要。UAC和UAS都必须正确处理临时响应、最终响应、ACK以及提供/应答交换。