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






4.91/5 (50投票s)
本文档是对会话发起协议 (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 开始)。
方法 | 描述 |
---|---|
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 新增的。
类 | 描述 |
---|---|
1xx 临时 | 确认收到请求,并且处理仍在进行中。对 INVITE 的临时响应从不被 ACK。 |
2xx 成功 | 请求已收到、处理并接受。 |
3xx 重定向 | 提供位置信息或要尝试的备用服务。 |
4xx 请求失败 | 请求包含错误或服务器无法处理。 |
5xx 服务器失败 | 由于内部错误,服务器无法满足请求。 |
6xx 全局失败 | 找不到可以满足请求的服务。 |
在每个类别中,都预先确定了许多响应代码 - 有些是从 HTTP 复制的。
# | 原因短语 | 描述 |
---|---|---|
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”应用程序的等价物。
创建请求消息
- 添加请求行,表明该消息是发送到 'sip:bob@897s.aarhus.com' 的 INVITE 请求。
- 创建 Via 头;Via 头向接收者指示返回路径。
- 创建发送者和接收者的地址。
SipUri
可以是 IP 地址,但推荐使用完全限定域名。也可以使用显示名称。出于安全原因,From 头允许匿名。 - 创建呼叫和会话的唯一标识符。
CallId
是会话的唯一值。在后续请求中,此序列会递增。To、From 和 Call-ID 元组提供了一个唯一的呼叫键。 - 创建发送者的备用联系信息。
- 最后,添加内容定义。在本例中,我们将省略内容。
Invite invite = new Invite(new SipUri("sip:bob@897s.aarhus.com"));
invite.ViaHeaders.Add(new ViaHeaderField("122.181.8.8:11506",
SipTransportProtocol.Udp));
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";
invite.CallId.CallId = "afh7989asdfhf@ml99.odense.com";
invite.CSeq.Sequence = 3434534;
invite.CSeq.Method = SipMethod.Invite;
invite.ContactHeaders.Add(new ContactHeaderField(
new SipUri("sip:alice2@vejle.com")));
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 响应的示例。
- 添加状态行,表明请求已成功。
- 从请求消息中复制 Via 头。
- 复制地址信息。To 和 From 字段与原始字段相同,只是 To 字段增加了一个 tag 参数。它们在响应中不会交换。您应该将它们视为原始的 To 和 From 字段。
- 从请求消息中复制标识符。
- 添加备用联系信息。这次是 Bob 的。
- 最后,添加内容定义。
Response okMessage = new Response(StandardResponseCode.Ok);
okMessage.ViaHeaders.Add(new ViaHeaderField("122.181.8.8:11506",
SipTransportProtocol.Udp));
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";
invite.CallId.CallId = "afh7989asdfhf@ml99.odense.com";
invite.CSeq.Sequence = 3434534;
invite.CSeq.Method = SipMethod.Invite;
okMessage.ContactHeaders.Add(new ContactHeaderField(
new SipUri("sip:bob2@vejle.com")));
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 用户代理之间的呼叫流程,并且可以使用相同的消息结构。成功的呼叫展示了初始信令、媒体会话的建立,然后是呼叫的终止。
会话设置
- 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 的内部细节。
语言 | 链接 | 描述 |
---|---|---|
Java | NIST SIP 库 | 美国国家标准与技术研究院的参考 API。 |
C#、VB 等 | Konnetic 的 SIP .NET 库 | 文档完善的低级 SIP 和 SDP 堆栈。 |
C#、VB 等 | Microsoft 的统一通信服务器 | 高级 SIP、SDP 和 RTP 堆栈。 |
C++ | PJSIP SIP 堆栈 | 轻量级但功能齐全且高度可移植的 SIP 堆栈。 |
测试工具
测试工具至关重要,因为任何与 SIP 应用程序和服务器交互的应用程序都必须遵循标准。
工具 | 描述 |
---|---|
SIPp | 惠普的 SIP 流量生成器。 |
PROTOS | 来自芬兰奥卢大学的测试应用程序。 |
ETSI TS 102 027-2 | SIP 测试呼叫流程列表。 |
Torture Tests | 压力测试。 |
Wireshark | 可以说是现有的最佳网络分析器。(感谢 Ray Cassick 的提醒。) |
延伸阅读
- H. Schulzrinne. (2010) Henning Schulzrinne. [在线]. http://www.cs.columbia.edu/~hgs/
- A. B. Johnston, SIP: Understanding the Session Initiation Protocol, 3rd ed. Boston, MA, USA: Artech House, 2007.
- J. Rosenberg et al., "SIP: Session Initiation Protocol" RFC3261 2002
- IETF. (2010) Multiparty Multimedia Session Control (mmusic). [在线]. http://datatracker.ietf.org/wg/mmusic/
- IETF. (2010) Session Initiation Protocol (SIP). [在线]. http://datatracker.ietf.org/wg/sip/
- H. Schulzrinne et al., "RTP: A Transport Protocol for Real-Time Applications" RFC3550 2003.
以上部分内容是 IETF 的“请求评论”(Request For Comment) 提交。不要因此而却步。大多数 RFC 都写得很好,而且容易理解。仅阅读前 10 页您就会学到很多东西。