团队博客

180 还是 183?

在 FreeSWITCH 中怎么配置回 180 还是 183,是一个经常被问到的问题。

要明白怎么配置,首先需要明白 180 和 183 的来龙去脉。

在 SIP 通信中,所有 1 开头的响应叫临时响应,常见的有 100,180 和 183。这些响应一般是对INVITE请求的响应。使用场景是这样的:主叫用户(A)在发起一个呼叫时,会向被叫用户(B)发起一个INVITE请求,被叫用户在收到这个请求后,会给主叫用户一个响应,一般的响应流程是回 100,紧接着回 180,或(和)183。

INVITE

A <-----------> B

180/183

其中,100 主要是信令层的,它的作用是 B 告诉 A 它收到了 A 的INVITE请求。关于它的作用实际也可以讲一讲的,但那就到信令的底层了,大家一般可以不用关注。

180/183 的作用是 B 告诉 A,你可以听回铃音了。

什么是回铃音?这要从更早的模拟电话时代讲起。A 给 B 打电话,B 的话机会振铃,同时 A 的话机回放回铃音(嘟嘟声),这样,A 就知道 B 的话机振铃了。

所以,回铃音是模拟电话时代的事情,它的作用就是给 A 一个提示,对方的电话正在振铃,这样,A 就可以放心地等待 B 接电话。当然,随着技术的进步和时代的发展,回铃音也在进步,典型地,除了嘟嘟声以外,电信运营商也会利用这段等待的时间给用户放一些音乐,甚至是广告。这些音乐或广告就称为彩铃,这些是在 A 与 B 正式通话前播放的,因此不对 A 收费。但是由于彩铃也是资源,因此运营商可能会对 B 收费(B 放音乐提高自己的逼格,或者放广告提升自己的形象甚至获取商业利益,收费也是有道理的)。

到了 SIP 时代,就需要在 SIP 中描述这些回铃音或彩铃,这些回铃音或彩铃是真正的声音数据,称为媒体(Media)。但为了将它与真正的媒体(即 A 与 B 真正的通话数据)相区别,将其为早期媒体,即Early Media

SIP 的全称是(Session Initiation Protocol,即会话初始协议),它仅仅是完成会话的协商,但是实际的媒体如何传输却需要另外一个协议来协商,负责描述媒体的协议称为 SDP(Session Description Protocol),即会话描述协议。不过,SDP 寄生在 SIP 中,典型地,它寄生在INVITE消息和 183 消息中。当 A 向 B 发起呼叫时,它需要把自己的 SDP 放到INVITE消息中发给 B,同时,B 在 183 消息中放入自己的 SDP,回送给 A。当双方都知道对方的 SDP 后,真正的媒体数据就可以传输了,这时,A 才听到 B 的Early Media

为了帮助大家理解 SDP,我们再进一步。假设 B 接听了电话,B 会响应 200 OK 消息,该消息也带了 SDP(可能跟 183 中的相同也可能不同),用于建立 A 与 B 真正的通话,毕竟 A 给 B 打电话是为了跟 B 通话,而不是为了听Early Media中的音乐或广告。

所以,不管是 183 中的 SDP,还是 200 OK 中的 SDP,都是为了建立媒体服务的。区别只是一个叫Early Media,另一个叫 Media。其实Early MediaMedia差别除了一个是广告一个是真正的 B 的声音外,差别也不大,非要说差别的话,那就是运营商的收费方面有差别,因为Early Media是不收费的。

那 180 和 183 又有什么区别呢?

180 和 183 之间就差个 3。

这些都是 RFC 定义的,RFC 没定义他们的区别。

不过,RFC 划出道来大家就要走啊,因此,大家就都按照自己的理解实现了 180 和 183。

现在最流行的实现方式是:180 不带 SDP,183 带 SDP。FreeSWITCH 也遵守这种约定。

所以,180 与 183 的区别不是 3,也不是其它的,关键是看它们带不带 SDP,即 180 也可以带 SDP,183 也可以不带 SDP。为了防止思维混乱,我们下面认为 180 不带 SDP,而 183 带 SDP。如上面所说,这些符合绝大多数人的思维。

那么,带 SDP 的 183 我们上面讲过了,180 不带 SDP 有什么用呢?

SIP 终端属于智能终端,比以前的模拟电话可先进多了。其中的一点就是它能区别 180 和 183。如果 A 的 SIP 终端收到 183,它就协商媒体,将 B 端发过来的Early Media在自己的扬声器里放出来;但如果收到的是 180,没有 SDP 就没法协商媒体,因此,B 就没法给 A 发Early Media了。不能让主叫用户干等着,所以,A 的话机在这种情况下能自己产生一个回铃音,或任何用户在 A 话机上设置的音乐。

那么,如何在 FreeSWITCH 中配置回 180 还是 183 呢?

FreeSWITCH 是一个多功能的 SIP 服务器,在此,为了简单起见,我们先把它当成一个普通的 SIP UA。比方说,它就是 B。

在 FreeSWITCH 内部,FreeSWITCH 的行为靠一些称为Application的功能函数控制的。当一个呼叫到来时,FreeSWITCH 会查找拨号计划(Dialplan)来决定执行哪些Application。如,下面的Dialplan,当一个呼叫到来时,它首先执行Answer,给对方回 200 OK,然后执行playback给对方放一段声音(从声音文件中读取),然后挂机。

<action application="answer"/>
<action application="playback" data="/tmp/hello.wav"/>
<action application="hangup"/>

如果你自己测试这段Dialplan,就会发现,FreeSWITCH 不会回 180 也不会回 183,而是直接回 200。这里,answer想当于 B 摘机应答,在 SIP 中就直接回 200。

当然,“纸上得来终觉浅,绝知此事要躬行。” 学习 SIP 最好的办法不是看 RFC,而是照着这些(以及《FreeSWITCH 权威指南上的例子》)自己抓包去看。如果试着不对的话,也许是你的Dialplan里边东西太多,在这几个Application前执行了你不知道的其它的Application

那怎么让它回 183 呢?

在上面的Dialplan中把answer那一行去掉,就回 183 了。

因为playback的作用是向 A 播放一段声音,但,在 B 向 A 发送声音前要建立媒体通道。如果有answer,FreeSWITCH 会发送 200 OK,带 SDP 建立媒体通道。如果没有answer,那么 FreeSWITCH 就会发送 183,带 SDP 建立媒体通道,而这时,hello.wav的媒体内容就成了Early Media

所以,送不送 183 就看你在answer前还是answer后执行playback

那么 180 呢?也很简单,那就是在发送 180 前执行一个ring_ready,即:

<action application="ring_ready"/>
<action application="answer"/>
<action application="playback" data="/tmp/hello.wav"/>
<action application="hangup"/>

在上面的例子中,如果你抓包,就可以看到 180,但你很可能听不到回铃音。原因很简单,answer执行的太快了。尝试在ring_readyanswer之间停顿一下,就可以听到回铃音了。下面例子中的sleep可以在发送完 180 后暂停 2 秒钟(2000 毫秒)再发送 200 OK:

<action application="ring_ready"/>
<action application="sleep" data="2000"/>
<action application="answer"/>

我们已经知道怎么让 FreeSWITCH 发 180 还是 183 了,问题解决了吗?

显然没有,我知道你在实际应用中没这么简单。我们在这里把 FreeSWITCH 当成了 B,但实际你希望 FreeSWITCH 是 FreeSWITCH,B 是 B。什么意思呢?FreeSWITCH 实际上是一个B2BUA,它是一个中间人,你希望的拓扑结构是这样的:

A <--------> FreeSWITCH <---------> B

中间人也是不好当的,因为,他可以有 N 种不同的策略。至于使用哪种策略,看 B 的心情,也要看 FreeSWITCH 的心情,没有标准答案。

首先,我们先看一种熟悉的情况。FreeSWITCH 可以假装它就是 B,这样,配置方法跟上面讲的基本一样,只是它在假装后还要假戏真做,要用bridge这个Application再去呼叫 B,并把电话接通。

<action application="ring_ready"/>
<action application="sleep" data="2000"/>
<action application="answer"/>
<action application="playback" data="/tmp/hello.wav"/>
<action application="bridge" data="user/B"/>

所以在上面的配置中,至于是回 180 还是 183,配置方式跟上面讲的一模一样,就没必要多说了。

其次,FreeSWITCH 心情好,想听听 B 的意见。如果它即不执行ring_ready,也不执行answer,而是直接用bridge去呼叫 B。

<action application="bridge" data="user/B"/>

这种情况其实也简单,那就是,如果 B 向 FreeSWITCH 回复 180,FreeSWITCH 就向 A 回 180;如果 B 回 183,FreeSWITCH 就向 A 回 183。这种情况其实就相当于 FreeSWITCH 不存在,所有消息都是透明的。(不过,要记住:FreeSWITCH 是一个B2BUA,即它是一个中间人,它不会直接拿 B 回给它的 180 或 183 消息“转”给 A,而是自己新产生了一个 180 或 183 消息回给 A。)

再次,FreeSWITCH 跟 B 这两天不大对付,什么事情都拧把。B 回 180,FreeSWITCH 就回 183,B 回 183,FreeSWITCH 就回 180,又是两种情况。

先看 B 回 180 的情况。FreeSWITCH 要想给 A 回一个 183,由于 B 的 180 中不带媒体,FreeSWITCH 就要“造”一个媒体出来,因此,它想了这种一种办法,在bridge之前造一个媒体:

<action application="set" data="ringback=/tmp/ring.wav"/>
<action application="bridge" data="user/B"/>

由于在执行bridge之前还没有 B,因此 FreeSWITCH 不知道什么时候 B 回 180 还是 183。通过在bridge之前使用 set 设置一个变量(ringback),实际上相当于 FreeSWITCH 给bridge下了一个套,到了bridge阶段,不管你什么时候 B 回 180,FreeSWITCH 都会向 A 播放事先“造”好的回铃音ring.wav。当然,FreeSWITCH 要向 A 发送媒体前要先用 183 建立媒体通道,这就完成了 180 到 183 的转换。

所以,这也是 FreeSWITCH 设计精巧之处——同是一个bridge,通过一个ringback变量改变了它的行为。

再看 183 变 180 的情况。

如果 B 向 FreeSWITCH 回了 183,FreeSWITCH 要向 A 回 180,那就不能把媒体信息送给 A。所以,实现也很简单,还是一个简单的bridge,只是,把 B 送来的Early Media忽略掉就行了:

<action application="ring_ready"/>
<action application="bridge" data="{ignore_early_media=true}user/B"/>

跟 set 不同。set 是一个Application,它作用于当前的Channel,即 A 那一个Channel(那时候还没有 B)。而{ignore_early_media=true}这种语法,在建立 B 端的Channel的同时,将ignore_early_media作用于 B。再强调一次,FreeSWITCH 是一个B2BUA,因此 A 跟 B 间的通话要产生两个Channel,即所谓的a-legb-leg

在建立 B 通道的时候,ignore_early_media也是给bridge下了一个套。即不管什么时候 B 回了 183,忽略它。由于我们选择了忽略,因此,为了让 A 仍能听到回铃音,我们用ring_readybridge前送一个 180。严格来说,它不是 183 变 180,因为 FreeSWITCH 收到 183 前就已经送出了 180,但是,如果不在 FreeSWITCH 内部看,谁知道什么时候变得呢?

Bark—一种GPT风格的TTS