SIP协议入门指南
前置声明:本文是本人关于 SIP 资料的一个梳理结果,80%的内容都是摘自各个参考资料的一部分,20%是自己在 12-14 年间 SIP 相关工作的理解感悟,然后根据自己拟定的目录大纲进行整合。本文所有版权不归我,如有侵权,请告知,必删。
为什么要写这么一篇文章?
我司(Legrand)核心产品是楼宇视频对讲设备,在业界的名气 还不错。
视频通话或者视频会议技术栈通常是 SIP 标准协议,我们当然也不例外。
自从我专做 iOS 开发之后,我就很久不玩 SIP 了,但是这些 SIP 的东西还是没有完全忘记。最近领导给我们开了 Q2 业绩会,提出老旧系统改造,包括 SIP Server 升级,NAT 打洞啊,目的是为了提高系统的稳定性,节省服务器的资源开销。
这些工作跟我没啥关系,因为我不做服务器,但是勾起了我多年的回忆。随即产生一个想法,就是给当年完全不懂 SIP 的自己写一篇入门文章,当做是对自己过去工作、技术的一个总结。
OK,废话不多数,正文开始。
SIP 的概念
SIP,英文全称 Session Initiation Protocol,中文翻译叫会话初始协议,是一个控制发起、修改和终结交互式多媒体会话的信令协议。它是由 IETF(Internet Engineering Task Force,Internet 工程任务组)定义的,最早发布于 1999 年 3 月的 RFC 2543,后来在 2002 年 6 月发布了新的标准 RFC 3261。
SIP 是一个基于文本的协议,在这一点上与 HTTP 和 SMTP 相似,易于读取和调试。
SIP URI
我们来对比一个简单的 SIP 请求与 HTTP 请求:
- SIP:
INVITE sip:alice@sipserver.com SIP/2.0
- HTTP:
GET /index.html HTTP/1.1
在 HTTP 中, 请求由三部分组成,GET
指明一个获取资源(文件)的动作,而 /index.html
则是资源的地址,最后是协议版本号。而在 SIP 中,同样也有三部分,INVITE
表示发起一次请求,alice@sipserver.com
为请求的地址,称为 SIP URI,最后也是版本号。
其中,SIP URI 很类似一个电子邮件,其格式为“协议:名称@主机”。与 HTTP 和 HTTPS 相对应,有 SIP 和 SIPS,后者是加密的;名称可以是一串数字的电话号码,也可以是字母表示的名称;而主机可以是一个域名,也可以是一个 IP 地址。
对等协议
SIP 是一个对等的协议,类似 P2P。不像传统电话那样必须有一个中心的交换机,它可以在不需要服务器的情况下进行通信,只要通信双方都彼此知道对方地址(或者,只有一方知道另一方地址)。
例如,bob 的 IP 地址是 192.168.1.10,端口是 5000,alice 的 IP 地址是 192.168.1.11,端口是 6000。当 bob 呼叫 alice 时,他只需要直接呼叫 alice 的 SIP 地址:sip:alice@192.168.1.11:6000
,反过来 alice 呼叫 bob 时,只需要呼叫 bob 的 SIP 地址:sip:bob@192.168.1.10:5000
。
下面是 bob 呼叫 alice 的完整流程:
bob alice
| |
| INVITE |
|-------------------->|
| 100 Trying |
|<--------------------|
| 180 Ringing |
|<--------------------|
| 200 OK |
|<--------------------|
| ACK |
|-------------------->|
| |
|<---RTP------------->|
|<---RTP------------->|
|<---RTP------------->|
| ... |
| |
| BYE |
|<--------------------|
| 200 OK |
|-------------------->|
| |
UA
在 SIP 网络中,alice 和 bob 都叫做用户代理(UA, User Agent)。UA 是在 SIP 网络中发起或响应 SIP 处理的逻辑功能。UA 是有状态的,也就是说,它维护会话(或称对话)的状态。
UA 有两种功能:一种是 UAC(UA Client 用户代理客户端),它是发起 SIP 请求的一方,如 bob。另一种是 UAS(UA Server),它是接受请求并发送响应的一方,如 alice。由于 SIP 是对等的,如果 alice 呼叫 bob 时(有时候 alice 也主动叫 bob 一起吃饭),alice 就称为 UAC,而 bob 则执行 UAS 的功能。一般来说,UA 都会实现上述两种功能。
Proxy Server
设想 bob 和 alice 是经人介绍认识的,而他们还不熟悉,bob 想请 alice 吃饭就需要一个中间人(M)传话,而这个中间人就叫代理服务器(Proxy Server)。
Redirect Server
还有另一种中间人叫做重定向服务器(Redirect Server),它类似于这样的方式工作 ── 中间人 M 告诉 bob,我也不知道 alice 在哪里,但我老婆知道,要不然我告诉你我老婆的电话,你直接问她吧,我老婆叫 W。这样,M 就成了一个重定向服务器,而他老婆 W 则是真正的代理服务器。这两种服务器都是 UAS,它们主要是提供一对欲通话的 UA 之间的路由选择功能。具有这种功能的设备通常称为边界会话控制器(SBC,Session Border Controller)。
Register Server
还有一种 UAS 叫做注册服务器。试想这样一种情况,alice 还是个学生,没有自己的手机,但它又希望 bob 能随时找到她,于是当她在学校时就告诉中间人 M 说她在学校,如果有事打她可以打宿舍的电话;而当她回家时也通知 M 说有事打家里电话。只要 alice 换一个新的位置,它就要向 M 重新“注册”新位置的电话,以让 M 能随时找到她,这时候 M 就是一个注册服务器。
Alice SIP Server
| |
| REGISTER |
|------------------------------->|
| SIP/2.0 401 Unauthorized |
|<-------------------------------|
| REGISTER |
|------------------------------->|
| SIP/2.0 200 OK |
| |
B2BUA
最后一种叫做背靠背用户代理(B2BUA,Back-to-Back UA)。需要指出,其实 RFC 3261 并没有定义 B2BUA 的功能,它只是一对 UAS 和 UAC 的串联。FreeSWITCH 就是一个典型的 B2BUA,事实上,B2BUA 的概念会贯穿本书始终,所以,在此我们需要多花一点笔墨来解释。
我们来看上述故事的另一个版本:M 和 W 是一对恩爱夫妻。M 认识 bob 而 W 认识 alice。M 和 W 有意搓合两个年轻人,但见面时由于两人太腼腆而互相没留电话号码。事后 bob 相知道 alice 对他感觉如何,于是打电话问 M,M 不认识 alice,就转身问老婆 W (注意这次 M 没有直接把 W 电话给 bob),W 接着打电话给 alice,alice 说印象还不错,W 就把这句话告诉 M, M 又转过身告诉 bob。 M 和 W 一个面向 bob,一个对着 alice,他们两个合在一起,称作 B2BUA。在这里,bob 是 UAC,因为他发起请求;M 是 UAS,因为他接受 bob 的请求并为他服务;我们把 M 和 W 看做一个整体,他们背靠着背(站着坐着躺着都行),W 是 UAC,因为她又向 alice 发起了请求,最后 alice 是 UAS。其实这里 UAC 和 UAS 的概念也不是那么重要,重要的是要理解这个背靠背的用户代理。因为事情还没有完,bob 一听说 alice 对他印象还不错,心花怒放,便想请 alice 吃饭,他告诉 M, M 告诉 W, W 又告诉 alice,alice 问去哪吃,W 又只好问 M, M 再问 bob…… 在这对年轻人挂断电话这前, M 和 W 只能“背对背”的工作。
背靠背呼叫流程图如下:
FreeSWITCH
Alice (B2BUA Server) Bob
| | | |
| INVITE F1 | | |
|------------------->| | |
| 100 Trying F2 | | |
|<-------------------| | INVITE F3 |
| | |------------------->|
| | | 100 Trying F4 |
| | |<-------------------|
| | | 180 Ringing F5 |
| 180 Ringing F6 | |<-------------------|
|<-------------------| | |
| | | 200 OK F7 |
| 200 OK F8 | |<-------------------|
|<-------------------| | ACK F9 |
| ACK F10 | |------------------->|
|------------------->| | |
| RTP Media | | RTP Media |
|<==================>| |<==================>|
| BYE F11 | | |
|------------------->| | BYE F12 |
| 200 OK F13 | |------------------->|
|<-------------------| | 200 OK F14 |
| | |<-------------------|
| | | |
从上图可以看出,四个人其实全是 UA。从上面故事可以看出,虽然 FreeSWITCH 是 B2BUA,但也可以经过特殊的配置,实现一些代理服务器和重定向服务器的功能,甚至也可以从中间劈开,两边分别作为一个普通的 UA 来工作。这没有什么奇怪的,在 SIP 世界中,所有 UA 都是平等的。具体到实物,则 M 和 W 就组成了实现软交换功能的交换机,它们对外说的语言是 SIP,而在内部,它们则使用自己家的语言沟通。bob 和 alice 就分别成了我们常见的软电话,或者硬件的 SIP 电话。
回铃音与 Early Media
A ------ |a 交换机 | ---X--- | 交换机 b| -------- B
为了便于说明,我们假定 A 与 B 不在同一台服务器上(如在 PSTN 通话中可能不在同一座城市),中间需要经过多级服务器的中转。
假设上图是在 PSTN 网络中,A 呼叫 B,B 话机开始振铃,A 端听回铃音(Ring Back Tone)。在早期,B 端所在的交换机只给 A 端交换机送地址全(ACM)信号,证明呼叫是可以到达 B 的,A 端听到的回铃音铃流是由 A 端所在的交换机生成并发送的。但后来,为了在 A 端能听到 B 端特殊的回铃音(如“您拨打的电话正在通话中…” 或 “对方暂时不方便接听您的电话” 尤其是现代交换机支持各种个性化的彩铃 - Ring Back Color Tone 等),回铃音就只能由 B 端交换机发送。在 B 接听电话前,回铃音和彩铃是不收费的(不收取本次通话费。彩铃费用一般是在 B 端以月租或套餐形式收取的)。这些回铃音就称为 Early Media(早期媒体)。它是由 SIP 的 183(带有 SDP)消息描述的。
理论上讲,B 接听电话后交换机 b 可以一直不向 a 交换机发送应答消息,而将真正的话音数据伪装成 Early Media,以实现“免费通话”。
INVITE 消息示例
下面是一个完整的 INVITE 消息的数据内容,应该很好理解。整个消息就两个部分:Header + SDP,其中 Header 跟 HTTP 的 Header 没啥两样,SDP 也完全就可以当做 HTTP 的 Body 来看,两者使用换行来分割。
INVITE sip:some@192.168.31.131:50027 SIP/2.0
Via: SIP/2.0/UDP 192.168.31.131:51971;rport;branch=z9hG4bKiYblddPPX
Max-Forwards: 70
To: <sip:some@192.168.31.131:50027>
From: <sip:null@null>;tag=Prf3c3Xc
Call-ID: cenXTa4i-1423587756904@appletekiAir
CSeq: 1 INVITE
Content-Length: 215
Content-Type: application/sdp
Contact: <sip:null@192.168.31.131:51971;transport=UDP>
v=0
o=user1 685988692 621323255 IN IP4 192.168.31.131
s=-
c=IN IP4 192.168.31.131
t=0 0
m=audio 49432 RTP/AVP 0 8 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
空行之后的就是 SDP 内容,描述一些音视频信息。
SIP 的优点
SIP 的优点是简单灵活。
尤其对开发人员来说很友好,你能看得懂 HTTP 协议,基本上就能很快理解 SIP 协议,全赖于它基于文本的协议。
SIP 非常容易扩展,RFC 3261 的核心内容其实并不多。
SIP 既不是会话描述协议,也不提供增加供会议控制功能。
为了描述消息内容的负载情况和特点,SIP 使用 SDP 来描述终端设备的特点。SIP 自身也不提供服务质量 (QoS),它与负责语音质量的资源保留设置协议 (RSVP) 互操作。它还与若干个其他协议 进行协作,包括负责定位的轻型目录访问协议 (LDAP)、负责身份验证的远程身份验证拨入用户服务 (RADIUS) 以及负责实时传输的 RTP 等多个协议。
应用场景
SIP 的应用场景有很多,比如交互式游戏、音乐、视频点播,音视频会议通话,PPT 展示,呼叫中心,语音短信,IM 等等。
开源解决方案
目前 SIP 相关的开源方案基本上都是 C 语言写的,一是为了跨平台、要兼容小设备,第二个是大部分开源作者本身是从事通信、嵌入式相关工作的,他们的背景决定了当前的语言选择。从作者们写下第一行代码开始,这些开源方案都有 10 年多、甚至 20 年的开发时间。
下面我们分别从服务端和客户端两个方向来介绍一下当前主流的开源方案。
服务端方案
服务端的开源方案有很多,老牌知名的 Asterisk,后起之秀 FreeSWITCH,代理界扛把子 Kamailio。
本文只介绍 FreeSWITCH 和 Kamailio。
FreeSWITCH
FreeSWITCH 是一个开源的电话交换平台,支持 Windows、Linux、Mac 等平台。