VoIP从敲门到入门——基于SIP的音视频通信(一)

背景:
从事音视频相关工作有大半年时间了,一直有把自己在这方面学习到的东西系统的梳理一下,写成一个系列博客的打算,但一直没有付诸行动。今天开始,拔掉一个许久以前立下的flag。

本文及后面几篇文章将主要介绍基于SIP协议的VoIP通信流程,包括SIP信令的交互过程以及RTP媒体数据连接的建立过程,主要涵盖以下几个方面:

1.对SIP协议的理解——SIP报文格式解析
2.SIP基本信令交互流程;
3.简要介绍SDP;
4.SIP服务的直连模式与路由模式;
5.媒体流的发送时机;
6.SIP穿越ALG NAT时的处理;
7.介绍一种识别RTP报文的算法及其局限性分析;
8.端口复用时的RTP报文格式;
9.一个栗子:使用OpenSIPS快速搭建本地SIP音视频环境。

这个系列的博客主要是面向从事网络音视频行业或者对VoIP感兴趣的人群,默认读者具备一定的基础网络知识、理解VoIP的相关概念、了解ALG与NAT技术,所以在讲述时,不再对以上概念作详细的介绍。另外,对于SIP信令和RTP媒体流,主要讲解对7层的认识,3层和4层可能会有简单涉及,但不重点讨论。另外本系列主要介绍音视频在网络传输方面的知识,对于音视频的编解码不做讨论。对编解码感兴趣的朋友,推荐阅读雷霄骅雷神的音视频专栏

下面进入正题,开始第一篇文章。

谈谈对SIP协议的理解

学习SIP的话,最好是先读一遍RFC3261,建议读英文原版的,因为现在网上很多中文版的RFC的翻译水平都只限于Google翻译的水平,很多地方读起来容易让人根本不知道在说些什么。

当然,这里并不打算做一份RFC的翻译,更多的是分享一些我自己对SIP的理解,会尽量写得通俗一些。

SIP——Session Initiation Protocol(会话初始协议),是一种应用层的信令控制协议,可用于创建、修改和终止一个或多个参与者的多媒体会话,类似于HTTP协议,SIP也是基于文本的。

初读RFC3261的话,会发现它向我们提到了几个关键概念:user agents(用户代理)、proxy-server(代理服务器),再往后又会有UAC、UAS等概念,如果每个概念非要对应一个中文含义的话,那么从字面上,暂且可以将UAC和UAS称作“用户代理客户端”和“用户代理服务器”,再往后看,又会看到“用户登记服务器”、“位置服务器”、“重定向服务器”等,乍一看这么多分的这么细致的协议概念,可能会很头疼,但是,还好有个但是,但是我们现在完全不用去在意这些细节,你可以简单的把上面这堆概念简单分为两类:SIP客户端,以及SIP服务器。这样一来是不是就感觉很亲切了——对啊,SIP协议它本身就是CS模型的,SIP服务器是什么——答:就是个服务器。

至于“用户登记服务器”、“位置服务器”、“重定向服务器”,这些属于逻辑上的概念,而当我们真正动手配置一台SIP服务器的时候,你就会发现,这些东西完全可以是在一台终端(PC或者机架式服务器之类的)上的。进一步的,如果你能看到第九篇文章(如果我能写到)的话,就回发现,原来这么多“服务器”,只需要在一个Linux系统上部署一个OpenSIPS就都搞定了。

解决了概念上令人头疼的问题,后面的事情就很好办了。

SIP协议栈

在这里插入图片描述

SIP消息(SIP Messages)

SIP消息分为从客户端到服务器的请求消息和服务器到客户端的响应消息。请求和响应的结构类似:都包含一个起始行(start-line)、一个或多个头域(header fields)、一个表示头域结束的空行,以及一个可选消息体。

generic-message = start-line
*message-header
CRLF
[ message-body ]
start-line = Request-Line / Status-Line

RFC里面定义了不少(不少的意思是我偷懒没有挨个去数究竟有几个)SIP请求方法,这里介绍几个常用的:

REGISTER:登记请求,SIP客户端上线时,需要向它的代理服务器登记,登记成功后,才能进行后续的交互流程;
INVITE:发起呼叫,意即给谁打电话,就INVITE谁;
ACK:用于证实用户代理客户端已经收到了对于INVITE请求的最终响应,和INVITE配套使用;
BYE:用来结束一个正在进行中的呼叫;
CANCEL:用于取消一个尚未完成的请求,对于已完成的请求则无影响:比如用户A通过INVITE向用户B发起了一次呼叫,而此时B还没有应答,那么A此时可以通过发送CANCEL来取消这次呼叫,而如果B已经接听了这个呼叫,那么A就不能CANCLE这个INVITE了,此时如果想要挂断电话,需要使用BYE;
OPTIONS:用于询问服务能力;

响应状态码:
1xx:表示请求正在处理,常见响应有 100 Trying(the request has been received by the next-hop server and that some unspecified action is being taken on behalf of this call)、180 Ringing(The UA receiving the INVITE is trying to alert the user);
2xx:成功;
3xx:重定向;
4xx:Client Error,可能是客户端发出的请求中包含了错误的语法,或者缺少了关键字段,或者…总之就是Client发出来的包有问题,Server处理不了;
5xx:Server Error,此处不展开描述,感兴趣请参考RFC3261第21.5节;
6xx:Global Failures,此处不展开描述,感兴趣请参考RFC3261第21.6节;

抓包分析SIP消息中的几个重要头字段

在这里插入图片描述
先抛出来RFC对头字段的格式定义:

header = “header-name” HCOLON header-value *(COMMA header-value)

RFC中指定了6个必须包含的头字段,分别是:Call-ID、CSeq、From、To、Via、Max-Forwards,下面分别介绍:

1.Call-ID: ——稍等
——突然感觉在说明Call-ID以前,有必要先解释一个词——Dialog。
Dialog直译是“会话”的意思,这里我们有必要搞清楚的是,这个会话发起和结束的时机是什么。在SIP中,一个会话是指持续一段时间的两个UA(User Agents,网络中的SIP节点,可以是客户端,也可以是服务器)之间端到端的SIP关系。一个会话由一个SIP请求消息建立,如客户端通过REGISTER请求去发起一个用户登记会话,然后服务器响应了一个200 OK,这就是一个Dialog的例子。应该注意的是,用“户A向服务器登记,然后登记成功了”,与在登记成功后“用户A向用户B发起了呼叫,然后用户B接听了”,这两个事物不属于同一个会话。能够正确区分开这两者的关系,是理解Dialog的关键。

一般地,对于“登记”来说,REGISTER——200 OK是一个会话;
而对于”发起并成功建立一次呼叫”这个过程中的INVITE——100 Trying——180 Ringing——200 OK——ACK,属于另一个会话。
思考一下:对上面的INVITE发起的呼叫,ACK之后就说明这个呼叫已经正常建立起来了,那么当后面的某个时刻,主叫或者被叫想要挂断电话,那么挂断时使用的BYE以及BYE的响应,应该是一个新的会话,还是和上面的“INVITE——100 Trying——180 Ringing——200 OK——ACK”同属一个会话呢?

在RFC2543等文档中,Dialog还有个别名,叫做call leg,其实两者是一个意思,不要被各种名词搞混了。

——理解了Dialog的概念后,讲明白Call-ID就很容易了:Call-ID就是用来标识一个SIP会话的,还应该注意的是,Call-ID并不是唯一一个用于标识SIP会话的字段,它和UAC在发出的请求消息中设置的From Tag以及UAS在其响应消息中设置的To Tag,三个字段一起作为一个SIP会话的Dialog-ID。

如果一时之间对Call-ID、From Tag、To Tag三者的作用和关系搞不清楚的话也没关系,抓几个包,根据SIP交互流程,分析一下它们在哪些报文里面是一样的,从哪个报文开始变化了。没有什么问题是抓包解决不了的,如果有,就多抓一会儿…

2.CSeq:
用于在同一个Dialog中标识与排序,以及区分新的请求与请求的重发。

3.From:
From事请求发起者的位置标识。与To类似,包含一个URI和一个可选的显示用户名。值得注意的是,对于REGISTER请求来说,From和To字段的值是一样的,对登记事务来说,From表示谁来负责这次登记。另外,From字段包含一个tag参数,在UAC发出一个会话建立请求时,必须设置一个唯一的tag参数,作为Dialog ID的一部分(上面Call-ID里也有提及)。

当时在读RFC的时候,有个疑惑的地方,现在可以拿出来和大家一起探讨一下:
RFC3261第8.1.1.3中说道:
在这里插入图片描述
然而在不只一家的SIP软件的抓包中,能看到From字段里面是包含了IP地址的,包括前面的抓包截图,这里的From中就有一个192.168.1.6的IP地址。
对此,我能想到的可能的解释有:
(1) RFC3261-8.1.1.3里说的"the host on which the UA is running"的“UA”单指UAC,因为上面抓包截图里的192.168.1.6是我的SIP服务器的地址,而不是客户端的地址,这样解释的话,这款软件没问题;
(2) 我的使用方法不对,应该和DNS配合使用,客户端软件的登记地址不能直接填服务器的真实地址,而应填域名,然后通过DNS将域名映射到192.168.1.6上面去;
(3)SIP客户端软件的设计问题:该软件没有严格遵守RFC的约束。更早一点的时候,我觉得这种可能性微乎其微,认为各个音视频厂商都会严格遵守协议标准去办事的,但是后来又遇到另外一个实现违背标准的例子,是我适配过的另一家音视频厂商——RFC3261 第10.2节中说道:
在这里插入图片描述
在对这家厂商做适配的时候,就发现到了某个信令流程的时候,往下就莫名不通了,然后定位到是这家公司的服务器把上面红框里“MUST NOT be present”的内容给present了。由此可见,并不是所有的厂商都会严格遵守标准的。

4.To
与From相对,To表示请求所希望到达的logical(这个词不知道该怎么翻译,逻辑位置?)。在REGISTER事物中,To表示要在Location Server中登记谁的地址。
To可以有一个tag参数,注意,与From不通,这里用的是“可以有”,因为当UAC发出请求时,dialog还没有建立,所以此时不包含to tag;然后当UAS收到INVITE请求时,再在响应中设置to tag。
To tag与From tag以及Call-ID一起,作为一个Dialog的唯一标识。
5.Via:
Via字段保存信令所经过的SIP节点(这里的节点可以是SIP UAC或者UAS)的主机名或者IP地址,又是也可能携带端口号,消息中所有Via头字段对请求消息而言,自下而上依次表示到当前所在的SIP节点为止,请求消息所经过的路径;对响应消息而言,自上而下依次表示从当前节点开始,响应所应遵循的路径(即为响应消息的路由提供了路径记录:从哪来的到哪去)。
6.Max-Forwards:
表示请求到达UAS的跳数限制。是一个整数,每经过一跳时减一。如果Max-Forwards已经减到零,但还没有到达目的地,则产生一个483(too many hops)响应。

另外还有其他一些不是必须的单也比较重要的字段,比如上面抓包截图里的Contact、Content-Length等,这些等后面聊交互流程的时候,用到哪个再做具体介绍。

呀,写这么久才刚差不多把格式解析说完,信令交互连个头还没开。。

咋办。。

有办法:

——临时决定把第一篇博文拆开了,这是第1-1篇,1-2再写交互流程——下回见。

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值