在很多SIP呼叫应用中,我们经常会遇到这样的使用场景。当一端客户对另外一端呼叫发起呼叫时,SIP服务器端按照系统的设置,可能同时对另外一端的办公室桌面电话发起呼叫,也可能同时对你的手机app分机发起呼叫。类似于一个呼叫经过代理服务器处理以后,进行了分叉处理(和叉子相似)
还有一种可能就是服务器端同时对多台不同的终端不同用户进行呼叫。但是,如果有多台终端被同时呼叫时,代理服务器如何才能知道哪台是终端第一个应答的呼叫,如何处理那些没有应答呼叫的终端或者稍晚应答的呼叫的终端。为了回答这个问题,我们需要详细地分析关键参数-branch为大家进行比较详细地剖析。
01 Forking呼叫背景知识
在前面的很多文章中笔者已经讨论过Forking 呼叫的具体流程,为了让读者能够快速了解Forking呼叫,我们再次做一个简单的回顾。Forking呼叫事实上是相对于并行按序呼叫来说的。一端发起呼叫后,通过SIP服务器端可以同时对多台终端进行呼叫,首先接听的开始通话。其他终端呼叫则被取消。
在具体的应用环境中,用户可以发起一个呼叫振铃组,或者分机随行等功能。如何实现这个具体的功能,每个厂家的产品不同可能配置命令有所不同。在Asterisk中,可以通过以下语法来实现同时对三台SIP分机进行呼叫。
exten => s,1,Dial(SIP/s1&SIP/s2&SIP/s3,10)
以上语法简单来说,就是通知系统同时呼叫分机s1,s2,和s3。
02 Branch定义和神奇的七个字符
在进行具体的讨论之前,我们这里需要介绍一下branch。在RFC3261中,branch是这样定义的:
TheViaheaderfieldvalueMUSTcontainabranchparameter.Thisparameterisusedtoidentifythetransactioncreatedbythatrequest.Thisparameterisusedbyboththeclientandtheserver.
https://www.rfc-editor.org/rfc/rfc3261.txt
通过以上规范,我们可以理解,branch是用来确认事务的,并且branch会被使用在终端和服务器端。另外,每个branch id必须是唯一的,以字符串“z9hG4bK”开头。在RFC3261规范说明中,仅简单说明使用这7个神奇的字符这是为了防止和rfc2453的规范冲突,rfc2453不使用这个值。为了进一步了解为什么使用这七个字符,笔者也查阅了很多文章和论文,没有看到为什么使用这七个神奇字符的具体的原因。
03 Fork初始化
根据我们上面介绍的关于分叉处理的机制,我们可以看到,一般来说,分叉呼叫大概的流程是这样的。一个SIP终端发起呼叫,通过了SIP服务器以后,分别对两个终端进行了呼叫,并且分别进行分叉处理。现在的问题是,如果第一个分机首先应答了这个呼叫后,服务器端怎么才能知道哪个终端执行了应答。
为了解决这个问题,我们需要借助branch id来处理。具体的语法格式如下:
Via:SIP/2.0/UDPerlang.bell-telephone.com:5060;branch=z9hG4bK87asdks7 Via:SIP/2.0/UDP192.0.2.1:5060;received=192.0.2.207;branch=z9hG4bK77asjd
https://www.rfc-editor.org/rfc/rfc3261.txt
04 在呼叫时插入branch id
SIP服务器端在对其他分机进行呼叫时,分别添加了自己的branch id号(这里简化表达),并且生成了一个专门对每一个终端的branch id。这样就确认了终端和服务器端的关系。
当第一个SIP分机首先接听时,服务器端收到200 OK,服务器端会根据branch id来确定是第一个分机的应答。应答以后,双方开始其他的流程处理。
05 取消其他没有接通的呼叫
接通了双方通话以后,接下来,SIP服务器端会根据branch判断其他未接听的终端,分别对其终端发送Cancel。
这里,读者需要注意,SIP 服务器发送到Cancel仍然使用的是同样的branch id。如果是这样的话,这里的处理是否和RFC3261的定义冲突?因为一般情况下,这个branch id是唯一的。答案显然不是。在RFC3261中还有这样的一个规定:
ThebranchparametervalueMUSTbeuniqueacrossspaceandtimeforallrequestssentbytheUA.TheexceptionstothisruleareCANCELandACK for non-2xxresponses.Asdiscussedbelow,aCANCELrequestwillhavethesamevalueofthebranchparameterastherequestitcancels.
https://www.rfc-editor.org/rfc/rfc3261.txt
因此,在Cancel请求发送时,仍然可以使用原来同样的branch id,否则SIP服务器端没有办法根据其唯一性来取消其他服务。
06总结
通过以上介绍,笔者可能基本了解了如何处理分叉呼叫的机制。其过程主要借助了branch id的方式来确认每个分支的身份确认信息。服务器端在Via头中增加一个branch id来识别其处理身份。在远端用户应答率呼叫后,服务器端可以通过ID来识别其终端,然后转发会呼叫方进行下一步处理。如果没有在第一时间应答的用户则会根据id发送Cancel请求。
以上过程就是一个简单的分叉呼叫的流程,另外,笔者提醒读者,需要特别注意到还有一个branch id的七个神奇字符的问题,笔者没有看到比较权威的解释。在处理Cancel时,用户一定要注意,Cancel需要使用同一branch id,而不是再使用新的唯一的id。
参考资料:
Multiple Registration and Call Forking With SIP
https://tools.ietf.org/html/rfc3261#section-8.1.1.7
https://mailarchive.ietf.org/arch/msg/sip/_5zV4Vh41XqgTBYfpzZP19fLpOM