180还是183?
在FreeSWITCH中怎么配置回180还是183,是一个经常被问到的问题。然而,答案却远没有你想象中的那么简单。
要明白怎么配置,首先你需要明白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 Media和Media差别除了一个是广告一个是真正的B的声音外,差别也不大,非要说差别的话,那就是运营商的收费方面有差别,因为Early Media是不收费的。
也许有人开始嫌我罗嗦了,但是,我确信,有些人真的不是从根本上理解我上面说的这些,包括一些开始嫌我罗嗦的人。不信,耐心点接着往下看。
好吧,183和200好像有点懂了,那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_ready和answer之间停顿一下,就可以听到回铃音了。下面例子中的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的心情。啊,说白了,又是没有标准答案。所以你还是要把你想要的告诉我。如果你不告诉我,我就得猜,或者把所有N种可能的情况都告诉你。
首先,我们先看一种熟悉的情况。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-leg和b-leg。
在建立B通道的时候,ignore_early_media也是给bridge下了一个套。即不管什么时候B回了183,忽略它。由于我们选择了忽略,因此,为了让A仍能听到回铃音,我们用ring_ready在bridge前送一个180。严格来说,它不是183变180,因为FreeSWITCH以收到183前就已经送出了180,但是,如果你不趴在FreeSWITCH内部看,谁知道什么时候变得呢?
N种情况讲了N种了,永远都会有N+1。既然FreeSWITCH位于中间,那它能不能把B发过来的广告(彩铃)换成它自己的广告呢?能是能,但我不教你怎么做。不过,不幸的是,如果你不是特别笨的话,我上面已经教会你了……
读到这里,你认为这个问题好答吗?