65.9K
CodeProject 正在变化。 阅读更多。
Home

Java、C# 和 VB 开发者 SIP 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (50投票s)

2010 年 7 月 30 日

CPOL

11分钟阅读

viewsIcon

147722

本文档是对会话发起协议 (SIP) 的技术概述,面向希望快速了解该协议工作原理和细节的 Java、C# 和 VB 程序员。

会话发起协议 (SIP)

会话发起协议 (SIP) 从底层开始设计,旨在连接任何时间、任何地点的任何人或设备,以进行(可能很长的)信息交换。现有的协议,如 HTTP 和 SMTP,并非为这种基本的人类活动而设计,因此 SIP 应运而生以填补这一空白。然而,SIP 大量借鉴了这两种协议:从 HTTP 的消息交换模式、消息格式和编码,到 SMTP 的 URI 方案。

2002 年,SIP 标准的一个修订版本被正式确立为互联网工程任务组 (IETF) 的标准化流程,即 RFC3261。由于 IETF 标准化过程的开放性、SIP 是文本 기반 且与现有规范共享许多特性,因此它易于理解、扩展和实现。

自问世以来,SIP 已作为即时消息(例如 Windows Messenger)和 VoIP(例如除 Skype 之外的大多数知名平台)的促进者而受到欢迎。

实体

SIP 环境由若干连接的实体组成

  • 用户代理 (UA) 是代表客户端设备中最终用户的实体。它通常有两种模式:用户代理客户端 (UAC) 发送初始请求消息并处理响应;用户代理服务器 (UAS) 接受请求并发送响应。
  • 代理服务器 负责将 SIP 消息路由到正确的端点。有状态代理有时会利用用户代理 在一个逻辑实体中,称为背靠背用户代理 (Back-To-Back-User-Agent)。
  • 重定向服务器 为接收者提供新的地址或不同的路由路径。服务器可能会利用位置服务器 来持久化位置信息。
  • 注册服务器 作为客户端连接到网络的当前存储库。

用户代理通常驻留在最终用户设备上。其他实体在许多场景中提供重要的支持服务。

消息

SIP 消息有两种类型

  • 请求:从客户端发送到服务器,定义了客户端寻求的操作。
  • 响应:从服务器发送到客户端,提供该请求的状态。

请求

SIP 请求类似于 HTTP,被定义为一个“方法”,因为它请求其他用户代理或服务器执行操作。RFC3261 定义了六种方法(表 1 中的前六种),后续标准定义了其余的扩展方法(从 INFO 开始)。

表 1. SIP 方法
方法 描述
INVITE 用于建立 SIP 会话。会话参数在此处协商。
REGISTER 对用户代理进行身份验证,并向网络提供当前位置。
BYE 终止已建立的会话。
ACK 确认对 INVITE 的成功响应。三方握手的第三步。
CANCEL 取消一个开放的请求。BYE 用于取消(终止)一个已建立的请求。
OPTIONS 查询通信方的能力。
扩展方法
INFO 提供通话中的与会话相关的信息。很少使用。
MESSAGE 用于传输即时消息。
NOTIFY 发布事件的结果。与 SUBSCRIBE 请求结合使用。
PRACK 临时响应确认。确认收到临时响应。
PUBLISH 发布状态信息。用于即时消息状态服务。
REFER 将请求传递给更合适的人处理的机制。
SUBSCRIBE 用于请求接收未来的 NOTIFY 或 PUBLISH 请求。
更新 修改通话中的会话参数。

响应

SIP 响应消息始终作为对请求的回复发送。它们将状态更新、确认、方向和错误代码传达回发起请求的 UAC。响应被归类为临时响应或最终响应,每个响应都必须由一个 3 位数字代码标识。

响应类型

已定义了六类响应,并使用 3 位数字代码进行分类。前五类借鉴自 HTTP;第六类是 SIP 新增的。

表 2. 响应类别
描述
1xx 临时 确认收到请求,并且处理仍在进行中。对 INVITE 的临时响应从不被 ACK。
2xx 成功 请求已收到、处理并接受。
3xx 重定向 提供位置信息或要尝试的备用服务。
4xx 请求失败 请求包含错误或服务器无法处理。
5xx 服务器失败 由于内部错误,服务器无法满足请求。
6xx 全局失败 找不到可以满足请求的服务。

在每个类别中,都预先确定了许多响应代码 - 有些是从 HTTP 复制的。

表 3. 示例响应代码
# 原因短语 描述
100 正在尝试 (Trying) 下一个节点已收到请求。
180 正在振铃 (Ringing) 正在尝试提醒用户。
182 排队 (Queued) 暂时不可用,请求已入队(未拒绝)。
200 好的 请求已成功。
301 永久移动 (Moved Permanently) 用户已不再可用于请求 URI 中提供的地址。
302 暂时移动 (Moved Temporarily) 在 Contact 头中提供的新的地址重试请求。
400 错误请求 (Bad Request) 无法正确理解或处理该请求。
401 未授权 (Unauthorised) 请求的身份验证失败或需要更多信息。
403 禁止 (Forbidden) 服务器拒绝处理该请求。请勿重试。
404 未找到 (Not Found) 服务器无法在其域中识别用户。
408 请求超时 (Request Timeout) 服务器未能在合理时间内处理该请求。
415 不支持的媒体 (Unsupported Media) 服务器不支持该格式。
480 暂时不可用 (Temporarily Unavailable) 被叫方当前不可用。
485 歧义 (Ambiguous) 请求 URI 存在歧义。
486 忙于此处 (Busy Here) 被叫方当前不愿意或无法接听电话。
500 服务器内部错误 (Server Internal Error) 服务器遇到了意外情况。
513 消息过大 (Message Too Large) 消息长度超过了确定的限制。
603 拒绝 (Decline) 用户明确拒绝接受该请求。

警告头字段 (Warning Header Field)

警告头字段用于携带有关响应状态的附加信息。该头定义了一个介于 300 到 399 之间的 3 位数字代码、主机名和警告文本。

Warning: 307 isi.edu "Session parameter 'foo' not understood".

消息结构

每个 SIP 消息都以起始行 (Start-Line) 开始,后跟一系列头信息 (headers),并通过回车换行符序列 (CRLF) 与消息正文 (body) 分隔。

  • 起始行:对于请求,格式为请求行 (Request-Line);对于响应,格式为状态行 (Status-Line)。
  • 头信息:提供有关消息的附加信息的命名属性。
  • 分隔符行:分隔头信息和正文。
  • 正文:二进制或文本负载。通常是会话描述协议 (SDP) 或消息文本。

起始行、每个头行和分隔符行都以 [CRLF] 序列终止。

起始行

起始行传达了消息类型和协议版本。对于请求(请求行)和响应(状态行),起始行包含三个用空格分隔的元素。

  • 请求行:包含方法、URI,并以协议版本(“SIP/2.0”)结尾。
  • INVITE sip:bob@897s.aarhus.com SIP/2.0
  • 状态行:以协议版本开头,后跟数字状态码,最后是简短的文本原因。
  • SIP/2.0 200 OK

Headers

头信息的格式与 HTTP 的通用头格式相同。每个头信息由一个不区分大小写的 ASCII 编码的名称和冒号组成,后跟一个值,该值有时是 UTF8 编码的,通常区分大小写。每个头信息都可以有一个或多个由分号分隔的参数附加到值上,提供额外的标签和功能。

header-name: header-value(;parameter-name=parameter-value)*[CRLF]

可以使用 [CRLF][TAB 或 SPACE] 序列将每个头信息折叠到不同行(称为折叠)。此外,具有相同名称的多个头信息,例如Contact,可以出现在不同的行上,或者可以将它们放在同一行上,用逗号分隔。例如

Contact: <sip:alice@atlanta.com>
Contact: <sip:alice1@chicago.com>

可以表示为

Contact: <sip:alice@atlanta.com>, <sip:alice1@chicago.com>

或者使用折叠

Contact: <sip:alice@atlanta.com>,
      <sip:alice1@chicago.com>

正文

消息正文描述会话(使用 SDP),或者包含不透明的文本或二进制正文部分,其中包含与会话相关的负载(例如,MIME 或 Message 格式)。正文可以出现在请求或响应消息中。

头字段

在以下示例中,Alice 使用 Bob 的 SIP URI 'sip:bob@897s.aarhus.com' 调用 Bob。Bob 用成功的响应回复 Alice。该消息是一个包含 SDP 消息的 INVITE 请求示例,并用“200”OK 响应进行回复。

注意:所有代码示例都基本与语言无关。最后为有兴趣进一步探索 SIP 的人提供了推荐的 Java、.NET 和 C++ API 列表。* * 所有代码都应该能与其中任何一个 API 配合使用,只需少量注释(在此示例中,我偶然使用了 Konnetic 的 SIP C# API)。

注意:Alice 调用 Bob 是 SIP 中经典的“Hello World”应用程序的等价物。

创建请求消息

  1. 添加请求行,表明该消息是发送到 'sip:bob@897s.aarhus.com' 的 INVITE 请求。
  2. Invite invite = new Invite(new SipUri("sip:bob@897s.aarhus.com"));
  3. 创建 Via 头;Via 头向接收者指示返回路径。
  4. invite.ViaHeaders.Add(new ViaHeaderField("122.181.8.8:11506",
                        SipTransportProtocol.Udp));
  5. 创建发送者和接收者的地址。SipUri 可以是 IP 地址,但推荐使用完全限定域名。也可以使用显示名称。出于安全原因,From 头允许匿名。
  6. invite.From.Uri = new SipUri("sip:bob@897s.aarhus.com");
    invite.From.DisplayName = "Bob";
    invite.From.Tag = "769122";
    invite.To.Uri = new SipUri("sip:alice@ml99.odense.com");
    invite.To.DisplayName = "Alice";
  7. 创建呼叫和会话的唯一标识符。CallId 是会话的唯一值。在后续请求中,此序列会递增。ToFromCall-ID 元组提供了一个唯一的呼叫键。
  8. invite.CallId.CallId = "afh7989asdfhf@ml99.odense.com"; 
    invite.CSeq.Sequence = 3434534; 
    invite.CSeq.Method = SipMethod.Invite;
  9. 创建发送者的备用联系信息。
  10. invite.ContactHeaders.Add(new ContactHeaderField(
                 new SipUri("sip:alice2@vejle.com")));
  11. 最后,添加内容定义。在本例中,我们将省略内容。
  12. invite.ContentType.MediaType = "application"; 
    invite.ContentType.MediaSubType = "sdp"; 
    invite.ContentLength = 136;

SIP 请求消息

生成的 SIP 请求消息应如下所示:

INVITE sip:bob@897s.aarhus.com SIP/2.0
Via: SIP/2.0/UDP 124.191.8.8:11506
Max-Forwards: 70
To: Bob <sip:bob@897s.aarhus.com> 
From: Alice <sip:alice@odense.com>;tag=769122
Call-ID: afh7989asdfhf@ml99.odense.com
CSeq: 3434534 INVITE
Contact: <sip:alice2@vejle.com>
Content-Type: application/sdp
Content-Length: 136

创建响应消息

如果您还记得,在本例中,Bob 用成功的响应回复 Alice。该消息是“200”OK 响应的示例。

  1. 添加状态行,表明请求已成功。
  2. Response okMessage = new Response(StandardResponseCode.Ok);
  3. 从请求消息中复制 Via 头。
  4. okMessage.ViaHeaders.Add(new ViaHeaderField("122.181.8.8:11506", 
                        SipTransportProtocol.Udp));
  5. 复制地址信息。To 和 From 字段与原始字段相同,只是 To 字段增加了一个 tag 参数。它们在响应中不会交换。您应该将它们视为原始的 To 和 From 字段。
  6. okMessage.From.Uri = new SipUri("sip:bob@897s.aarhus.com"); 
    okMessage.From.DisplayName = "Bob"; 
    okMessage.From.Tag = "769122"; 
    okMessage.To.Uri = new SipUri("sip:alice@ml99.odense.com"); 
    okMessage.To.DisplayName = "Alice";
    okMessage.To.Tag = "abgj67";
  7. 从请求消息中复制标识符。
  8. invite.CallId.CallId = "afh7989asdfhf@ml99.odense.com"; 
    invite.CSeq.Sequence = 3434534; 
    invite.CSeq.Method = SipMethod.Invite; 
  9. 添加备用联系信息。这次是 Bob 的。
  10. okMessage.ContactHeaders.Add(new ContactHeaderField(
                    new SipUri("sip:bob2@vejle.com")));
  11. 最后,添加内容定义。
  12. okMessage.ContentType.MediaType = "application";
    okMessage.ContentType.MediaSubType = "sdp";
    okMessage.ContentLength = 132; 
    

我应该指出,一个好的库将提供 API 来自动化上述许多样板代码。例如,将字段从请求复制到响应的操作将在库中完成。

SIP 响应消息

生成的 SIP 响应消息应如下所示:

SIP/2.0 200 OK
Via: SIP/2.0/UDP 124.191.8.8:11506
To: Bob <sip:bob@897s.aarhus.com>;tag=abgj67
From: Alice <sip:alice@odense.com>;tag=769122
Call-ID: afh7989asdfhf@mel99.odense.com
CSeq: 3434534 INVITE
Contact: <sip:bob2@vejle.com>
Content-Type: application/sdp
Content-Length: 132

呼叫流程示例

本节详细介绍了上述两个 SIP 用户代理之间的呼叫流程,并且可以使用相同的消息结构。成功的呼叫展示了初始信令、媒体会话的建立,然后是呼叫的终止。

SIP to SIP Call Flow

图 1. Alice 完成与 Bob 的 SIP 呼叫,交换媒体包,然后 Bob 终止呼叫。

会话设置

  • Alice 的 UA 向 Bob 的 SIP 地址(即 'sip:bob@897s.aarhus.com')发送一个 INVITE 消息。消息内容是描述预期媒体交换的会话描述协议消息。
  • Bob 的 UA 收到 INVITE 并回复一个 100 Trying 消息。
  • 然后 UA 尝试吸引 Bob 的注意,并同时向 Alice 发送一个 180 Ringing 消息。
  • Bob 回应,他的 UA 发送一个 200 OK 消息。200 OK 包含 Bob 同意的 SDP 消息。
  • 最后,Alice 的 UA 用 ACK 请求确认收到 OK。
  • 媒体流直接在 Alice 和 Bob 之间建立。

我们现在为另一个协议(如 RTP)铺平了道路,使其能够直接在 Alice 和 Bob 之间传输媒体数据,而无需 SIP 的进一步干预。这种传输发生在所谓的承载或传输平面 (Bearer or Transport Plane) 中,而 SIP 则在控制或信令平面 (Control or Signaling Plane) 中运行。

会话拆除

在呼叫结束时,SIP 用于拆除会话。例如,如果 Bob 结束呼叫,交换过程如下:

  • Bob 挂断电话,他的 UA 通过向 Alice 发送 BYE 请求来发起会话终止。
  • Alice 的 UA 以 200 OK 回复。

在上述示例中,Alice 和 Bob 进行了一般的“媒体”交换。然而,这很容易代表语音通话、视频会议或即时消息会话;过程将完全相同。

SIP 库和 API

此列表并非详尽无遗;它们只是我遇到的最好的。选择一个好的 SIP API 的合理标准是它是否提供对强类型头字段的支持,实现 SIP 事务,并封装了大多数 SIP URI 的内部细节。

表 4. 推荐的 SIP 堆栈
语言 链接 描述
Java NIST SIP 库 美国国家标准与技术研究院的参考 API。
C#、VB 等 Konnetic 的 SIP .NET 库 文档完善的低级 SIP 和 SDP 堆栈。
C#、VB 等 Microsoft 的统一通信服务器 高级 SIP、SDP 和 RTP 堆栈。
C++ PJSIP SIP 堆栈 轻量级但功能齐全且高度可移植的 SIP 堆栈。

测试工具

测试工具至关重要,因为任何与 SIP 应用程序和服务器交互的应用程序都必须遵循标准。

表 5. 推荐的 SIP 测试工具
工具 描述
SIPp 惠普的 SIP 流量生成器。
PROTOS 来自芬兰奥卢大学的测试应用程序。
ETSI TS 102 027-2 SIP 测试呼叫流程列表。
Torture Tests 压力测试。
Wireshark 可以说是现有的最佳网络分析器。(感谢 Ray Cassick 的提醒。)

延伸阅读

以上部分内容是 IETF 的“请求评论”(Request For Comment) 提交。不要因此而却步。大多数 RFC 都写得很好,而且容易理解。仅阅读前 10 页您就会学到很多东西。

相关文章

© . All rights reserved.