Chinaunix首页 | 论坛 | 博客
  • 博客访问: 965933
  • 博文数量: 86
  • 博客积分: 2283
  • 博客等级: 大尉
  • 技术积分: 861
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-04 09:02
个人简介

没什么好说的。

文章分类

全部博文(86)

文章存档

2014年(1)

2013年(5)

2012年(7)

2011年(26)

2010年(20)

2009年(27)

分类: 系统运维

2011-12-19 15:56:56

拨号方案基础
拨号方案是Asterisk系统的核心,因为它定义了Asterisk如何处理来话和去话。简单的说,它由指令和步骤列表组成,Asterisk按步骤来执行这些指令。
 
1. 拨号方案语法
 
Asterisk的拨号方案在文件extensions.conf中定义。该文件通常在/etc/asterisk目录下。但是其位置可以改变,这取决于asterisk的安装方式。其它常见的位置包括/usr/local/asterisk/etc/和/opt/asterisk/etc/。
拨号方案由4部分组成:contexts、extensions、priorities和applications。
配置文件实例
在安装astersik的时候,如果你安装了样例配置文件的话,你就会有extensions.conf存在。我们建议使用自己的extensions.conf文件。
Context
Dialplans被分成几个段,这些段称为context。context用来对extension的组命名。简单来说,它把拨号方案的不同部分进行分离,免得彼此交织在一起。在一个context中定义的extension完全独立于在另一个context中定义的extension,除非明确允许有交织。
context的表示方法是把名字放在方括号([])中间。这个名字可以由A-Z(大小写都可以)、0-9以及连字号-和下划线_组成。
所有放在context定义之后的指令都是这个context的一部分,直到下一个context定义的开始。
例如:
[incomming]
在拨号方案开始有二个特殊的context,名字分别是[general]和[globals]。
context的一个重要用途就是加强安全性。正确使用context可以让某些主叫方能够访问别人不能访问的功能。如果没有仔细设计拨号方案,可能会造成别人盗用你的系统。
Asterisk源程序包括一个非常重要的文件,叫做SECURITY,它概括了几个步骤来保证你的ASTerisk系统的安全。阅读并理解这个文件相当的重要。
Extension
在每个context内,可以定义一个或者多个extension。extension是Asterisk要执行的指令,由来电或者通道上所拨数字来触发。extension规定了在呼叫通过拨号方案时采取哪些处理。
extension在传统意义上被用来定义分机。
extension的语法是单词exten后面跟着一个由等号和大于号组成的箭头,如下所示:
exten=>
之后是extension名字。在与电话系统打交道的时候,我们把extension看作是呼叫另一部电话所拨的号码。在astersik上,还意味着更多的东西,例如,extension的名字可以是字母和数字的组合。
一个完整的extension由三部分组成:
* extension的名字和号码
* priority 每个extension的步骤
* 应用,针对呼叫完成一些动作
这三部分用英文逗号分开,格式如下:
exten=>name,priority,application()
例子:
exten=>123,1,Answer()
其中123是名字,1是priority,answer()是应用
priority
每个extension可以有多个步骤,称作priority。每个priority都按顺序编号,从1开始。每个priority执行一个规定的应用。例如:
exten=>123,1,Answer()
exten=>123,2,Hangup()
你必须确保priority是从1开始并且是连续编号。如果中间跳过一个priority,asterisk有可能不会越过它。如果在给定的extension中发现astersik没有遵从priority,你要确认是否跳过了某个序号或者标号混乱。
application
每个应用针对当前通道完成规定的动作,比如播放声音、接受音频拨号输入,或者挂断电话等。
无序号的priority
在asterisk1.2版本中,prority编号引入了n prority,表示“下一个”的意思。每次asterisk遇“n”这个priority的时候,就取出前一个prority的编号加上1.这样就便利拨号方案修改起来很方便,不必要去记忆每个步的编号。例如:
exten=>123,1,Answer()
exten=>123,n,do something
exten=>123,n,do something else
exten=>123,n,do one last thing
exten=>123,n,Hangup()
有时候,也会看到使用管道符当作操作符用在参量之间,而不是逗号。这里我们在应用参量之间使用逗号。
 
2. 一个简单的拨号方案
 
现在我们准备来创建第一个拨号方案。我们以一个简单的例子来开始。要设计的这个拨号方案在呼叫进来时,asterisk应答这个呼叫,播放声音文件,然后挂断。
为使用本例子工作正确,我们假定至少创建了一个zap通道,并且作了配置,所有进来的呼叫被送到[incoming]context中。
s Extension
当没有指定extension的呼叫(例如,正在振铃的FXO线路)进入context的时候,就由这个s extension来处理。(s表示start--开始,因为多数的呼叫都是从s extension开始)。
Answer()、Playback()和Hangup()应用
Answer()用于接听正在振铃的通道。这个并不为接受来话的通道进行初始化的设定。(确有少数应用不需要先应答通道,但是在完成其它动作之前进行适当的应答是个很好的习惯)。Answer()不需要任何参数。
Playback()用于在通道上播放事先录制好的语音文件。在播放期间,系统不理会来自用户的输入。Asterisk带有很多专业录制的语音文件,它们在默认的语音文件目录内(通常是/var/lib/asterisk/sounds)。这些文件是GSM格式,因此扩展名是.gsm。我们会在很多例子中使用这些文件。
若要使用playback(),要指定一个文件名(不需要扩展名)作为参数。例如Playback(filename)将播放文件名为filename.gsm的语音文件,并假定这个文件位于默认的语音文件目录内。你也可以在文件名上包括完整的路径,如:Playback(/home/john/sounds/filename)
如果所指定的目录包含多个使用不同扩展名的同名文件,Asterisk会自动选择播放最佳的文件。(Asterisk会根据文件转换代价来选择最佳文件,就是说,它选择占用CPU时间少的文件来把它转换成本地的音频格式。在启动asterisk时,它会计算不同音频格式之间的转换代价,系统不同,计算结果也不同,可以在asterisk命令行接口上输入命令show translation来查看这些转换代价)。
Hungup()完成挂断动作。主叫方将收到通话挂断音。
第一个拨号方案例子
[incoming]
exten => s,1,Answer()
exten => s,2,Playback(hello-world)
exten => s,3,Hangup()
 
3. 在拨号方案中加入逻辑
 
Background()和Goto()应用

构建交互式astersik系统的关键是Background()应用。与playback()应用不同的是,它也播放事先录制好的语音文件,不同的是,当主叫方按下电话键的时候,会中断语音播放,转到所按数字对应的extension。其语法与Playback()类似:
         exten =>123,1,Background(hello-world)
Background()通常用于创建语音菜单,很多公司使用语音菜单来引导主叫方到适当的分机,以免得接待员接听第一个电话。
Goto()用于把呼叫送到另一个context、extension以及priority。语法:
          exten =>123,1,Goto(context,extension,priority)
例子:
当用户呼叫进入拨号方案,会听到问候语“请输入分机号”,如果按1,会听到数字1,如果按2会听到数字2.并且听完后会要求重复输入。
[incoming]
exten=>s,1,Answer()
exten=>s,2,Background(enter-ext-of-person)
exten=>1,1,playback(digits/1)
exten=>1,2,Goto(incoming,s,1)
exten=>2,1,playback(digits/2)
exten=>2,2,Goto(incoming,s,1)
Goto()可以传递1或2或3个参数给就用,如果只传递一个,则系统认为被叫地是当前extension内的priority;如果传递二个,则系统认为被叫地是当前context的指定extenson和priority。

非法输入和超时处理

Asterisk使用i extension来处理非法输入,而用t extension处理超时输入(默认10s超时)
例子:
如果输入错误,则提示输入错误,再等待重新输入,如果输入超时,则提示“再见”,然后挂断。
[incoming]
exten=>s,1,Answer()
exten=>s,2,Background(enter-ext-of-person)
exten=>1,1,playback(digits/1)
exten=>1,2,Goto(incoming,s,1)
exten=>2,1,playback(digits/2)
exten=>2,2,Goto(incoming,s,1)
exten=>i,1,Playback(pbx-invalid)
exten=>i,2,Goto(incoming,s,1)
exten=>t,1,Playback(vm-goodbye)
exten=>t,2,Hangup()
 
Dial()应用

Dial()应用用于发起呼叫,语法比较复杂,Dial()需要4个参数。
第一个参数是被叫地,由呼叫所采用的(传输)技术、反斜线、远地资源(通常是通道名称和编号)等组成。
例如,在123到达拨号方案时,要asterisk对zap/1通道振铃:
exten=>123,1,Dial(Zap/1)             对FXS通道1振铃

exten=>123,1,Dial(SIP/1234)          对SIP通道1234用户振铃
如果同时拨通多个通道,使用&符号把多个被叫地连接起来:
exten=>123,1,Dial(Zap/1&Zap/2&Zap/3)             同时对FXS通道1,2,3振铃
第二个参数是超时,单们为s。如果没有给定超时时间,Dial()会一直呼叫该通道,直到有人接听或者主叫挂机。
例子:exten=>123,1,Dial(Zap/1,10)
如果呼叫未超时,则通道就被正常桥接,如果被叫没有应答,Dial()会在超时后转到该extension的下一个priority。但是如果被叫通道忙,Dial()将转到priority n 101,如果其存在的话(其中n是调用Dial()的priority)。
exten=>123,1,Dial(Zap/1,10)
exten=>123,2,Playback(vm-nobodyavail)
exten=>123,3,Hangup()
exten=>123,102,Playback(tt-allbusy)
exten=>123,103,Hangup()
第三个参数是个可选的字符串。它包含一个或多个能够影响Dial()应用行为的字符。选项列表太长,这里不一一列出。常用的一个是字母r,表示在通知被叫通道有来电的时候,主叫方会听到振铃声音。
注意,在指示振铃时,r不总是必需的,因为在建立通道时asterisk会自动产生振铃音。不过即使没有试图连接的时候,也可以使用r选项强制astersik指示振铃。
第四个参数是一个URL。如果被叫通道支持在呼叫的同时接受url,那么所指定的url将会被发送(例如,如果你有一个能够接受rul的ip电话,它将会显示在显示屏上,同样,如果使用的是软件电话,url会在屏幕上弹出来)。这个参数很少使用。
如果要在fxo zap通道上产生一个去话,可以使用下面的语法:
exten=>123,1,Dial(Zap/4/5551212)
如果要在sip或iax类型的通道上产生去话,则:
exten=>123,1,Dial(sip/1234)

exten=>123,1,Dial()
注意:任何参数都可以为空。例如,假设要指定一个选项,但是不超时,只要保留超时参数为空即可:
exten=>123,1,Dial(Zap/1,,r)
 
给内部呼叫增加Context

几乎所有的asterisk拨号方案中都会有多个context.下面的例子中,我们建立二个内部电话extension,加到拨号方案里面去,然后配置这二个extension可以彼此呼叫。
假定:FXS Zap通道已经配置好,并且配置了zapata.conf,任何来自Zap/1的呼叫都从[internal]context开始。也假定FXO zap通道已经配置成Zap/4,对于来到这个通道的呼叫将被送到[incoming]context中。还假定你有至少一个SIP通道(SIP/jane),被配置为[internal]context。
拨号方案为:
[incoming]
exten=>s,1,Answer()
exten=>s,2,Background(enter-ext-of-person)
exten=>101,1,Dial(Zap/1,10)
exten=>101,2,Playback(vm-nobodyavail)
exten=>101,3,Hangup()
exten=>101,102,Playback(tt-allbusy)
exten=>101,103,Hangup()
exten=>102,1,Dial(SIP/Jane,10)
exten=>102,2,Playback(vm-nobodyavail)
exten=>102,3,Hangup()
exten=>102,102,Playback(tt-allbusy)
exten=>102,103,huangup()
exten=>i,1,Playback(pbx-invalid)
exten=>i,2,Goto(incoming,s,1)
exten=>t,1,Playback(vm-goodbye)
exten=>t,2,Hangup()
[internal]
exten=>101,1,Dial(Zap/1,,r)
exten=>102,1,Dial(SIP/jane,,r)
上例使用二个extension,这样使用zap/1的人可以拿电话拨102来拨打通道sip/jane上的人。同样,注册为SIP/jane的电话可以拨101来拨打Zap/1的电话。
例子中使用extensions 101和102是随意确定的,你也可以自由选择为extension使用什么编号,并没有限制一定要使用3位的extension,可以多,也可以少,(但extension一定不能多于80个字符,也不能使用单字符,这是保留的)。还可以使用名字,见下面:
[internal]
exten=>101,1,Dial(Zap/1,,r)
exten=>john,1,Dial(Zap/1,,r)
exten=>102,1,Dial(SIP/jane,,r)
exten=>jane,1,Dial(SIP/jane,,r)
 
使用变量

在拨号方案中可以使用变量,如下所示:
变量赋值:    JOHN=Zap/1          
变量引用:    exten=>555,1,Dial(${JOHN},,r)
引用变量必须输入美元符号,紧接着是大括号,大括号内是变量名。
变量分为三种:全局变量、通道变量、环境变量
全局变量,适用于所有context里的所有extension,该变量在extensions.conf文件开始利用[globals]context中定义。也可能使用编程的方式定义,调用SetGlobalVar()应用。下面是拨号方案中定义全局变量的二种方法:
[global]
JOHN=Zap/1
[internal]
exten=>123,1,SetGlobalVar(JOHN=Zap/1)
通道变量,特定的呼叫相关的变量(如Caller *IDnumber),与全局变量不同的是,通道变量只能在当前呼叫存在期间定义,并只能用于参与该呼叫的通道。有很多预先定义的通道变量可以用于拨号方案,在Astersik源程序的doc子目录下的README文件中有详细说明。通道变量使用Set()应用来设置:
exten=>123,1,Set(MAGICNUMBER=42)
环境变量,是一种在Asterisk中访问操作系统环境变量的方法。这些变量以${ENV(var)}形式引用,其中的var是气概引用的操作系统环境变量。
在拨号中加入变量的例子:
[globals]
JOHN=Zap/1
JANE=SIP/jane
[incoming]
exten=>s,1,Answer()
exten=>s,2,Background(enter-ext-of-person)
exten=>101,1,Dial(${JOHN},10)
exten=>101,2,Playback(vm-nobodyavail)
exten=>101,3,Hangup()
exten=>101,102,Playback(tt-allbusy)
exten=>101,103,Hangup()
exten=>102,1,Dial(${JANE},10)
exten=>102,2,Playback(vm-nobodyavail)
exten=>102,3,Hangup()
exten=>102,102,Playback(tt-allbusy)
exten=>102,103,huangup()
exten=>i,1,Playback(pbx-invalid)
exten=>i,2,Goto(incoming,s,1)
exten=>t,1,Playback(vm-goodbye)
exten=>t,2,Hangup()
[internal]
exten=>101,1,Dial(${JOHN},,r)
exten=>102,1,Dial(${JANE},,r)
模式匹配
模式匹配可以使用一段代码来对应许多不同的extensions。
 
a.模式匹配语法
模式总以下划线_开始,它告诉Asterisk要做模式匹配,这不是一个extension名字。这意味着不能使用下划线作为extension名字的开始字符。如果在模式之前没有使用下划线,Asterisk会认为它是一个命名的extension,不会做任何模式匹配动作。
在下划线之后可以使用一个或者多个下列字符:
X  ----匹配0-9的任何数字
Z  ----匹配1-9的任何数字
N  ----匹配2-9的任何数字
[15-7] ----匹配任何数字或者指定的数字范围。此处匹配1,5,6或7.
.  ----(句点)通配符,匹配一个或多个字符
如果不小心,通配符可能会使得拨号网络做出你意想不到的事情。例如下面的模式匹配应该不会用到:
_.
实际上,如果你试图使用它,Asterisk会警告。如果可能,尽量使用下面的模式:
_X.
若要在拨号方案中使用模式匹配,只要把模式放在extension(或者号码)名字的位置
exten=>_NXX,1,Playback(auth-thankyou)
上例会匹配三位的extension,从200-999之间的任何数字。只要拨打200-999之间的任何数字,都会听到auth-thankyou的声音。
如果asterisk发现有多个模式与所拨的extension匹配,它会选择最精准匹配的那个:
exten=>_555XXXX,1,Playback(digits/1)
exten=>_55512XX,1,Playback(digits/2)
上例中,如果所拨号是888-551212,则会选择第2个extension;
 
b.匹配模式实例
_NXXXXXX    匹配任何第一位不是0和1的7位号码
 
c.使用${EXTEN}通道变量
一旦拨了某个extension,Asterisk会把通道变量${EXTEN}设置为所拨的数字。可以使用应用SayDigits()来检测出来:
exten=>_XXX,1,SayDigits(${EXTEN})
这里,SayDigits()会把所拨的3位extension读出来。
通常把extension的前面几位去掉对于处理${EXTEN}是很有用的。可以使用这样的语法来实现:${EXTEN:x},其中x是要去掉的位数。
例如:假设EXTEN的值是95551212,那么${EXTEN:1}等于5551212。再看另一个例子:
exten=>_XXX,1,SayDigits(${EXTEN:1}),在这个例子中,只是把所拨的extension的最后二位读出来。如果x是负数,则读出所拨extension的最后x位。下面例子中,SayDigits()只读出所拨的extension的最后1位:
exten=>_XXX,1,SayDigits(${EXTEN:-1})
 
d.启动去话拨号
首先要做的事情是给[globals]context加个变量,用于定义那个通道可以用来向外拨号:
[globals]
JOHN=Zap/1
JANE=SIP/jane
OUTBOUNDTRUNK=Zap/4
接下来,在拨号方案中添加一个用于去话的context
在此,你可能会问,为什么要使用一个单独的context?这样做的目的是能够规定的控制谁可以拨打去话,以及可以拨打什么样的去话。
首先,建立一个用于本地电话的context.为了与传统电话交换机保持一致,我们在模式之前放上9,因此用户必须在呼叫外部号码之前拨0:
[outbound-local]
exten=>_9NXXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})
exten=>-9NXXXXXXX,2,Congestion()
exten=>_9NXXXXXXX,102,Congestion()
exten=>911,1,Dial(${OUTBOUNDTRUNK}/911)
exten=>9911,1,Dial(${OUTBOUNDTRUNK}/911)
我们再次假设,出于这些例子的原因,我们在美国或者加拿大。如果在此这外的其他地区,用当地的紧急电话替换掉911.在拨号方案中永远不要忘记这一点。
下面给拨号方案加一个用于长途电话的context:
[outbound-long-distance]
exten=>_91NXXNXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})
exten=>_91NXXNXXXXXX,2,Congestion()
exten=>_91NXXNXXXXXX,102,Congestion()
 
e. Includes
Asterisk允许在一个context中使用另一个context,通过include指令来实现。这用来授予访问权给不同的拨号方案段。使用使用include功能来让[internal]context中的用户能够拨打去话。首先介绍语法。
include语句的形式如下所示,其中的是我们要包含在当前context的远地context:
include=>context
在当前context中包含另外一个context时,必须注意包含的顺序。asterisk首先试图在当前context中匹配extension。如果不成功,会去尝试第一个包含进来的context,然后按照包含顺序再去尝试其他context。
目前为止,拨号方案有二个context用于去话。但是[internal]context中的人还不能够使用它们。我们用在[internal]context包含这两个去话context来实现使用,如下所示:
[globals]
JOHN=Zap/1
JANE=SIP/jane
OUTBOUNDTRUNK=Zap/4
[incoming]
exten=>s,1,Answer()
exten=>s,2,Background(enter-ext-of-person)
exten=>101,1,Dial(${JOHN},10)
exten=>101,2,Playback(vm-nobodyavail)
exten=>101,3,Hangup()
exten=>101,102,Playback(tt-allbusy)
exten=>101,103,Hangup()
exten=>102,1,Dial(${JANE},10)
exten=>102,2,playback(vm-nobodyavail)
exten=>102,3,Hangup()
exten=>102,102,Playback(tt-allbusy)
exten=>102,103,Hangup()
exten=>i,1,Playback(pbx-invalid)
exten=>i,2,Goto(incoming,s,1)
exten=>t,1,Playback(vm-goodbye)
exten=>t,2,Hangup()
[internal]
include=>outbound-local
include=>outbound-long-distance
exten=>101,1,Dial(${JOHN},,r)
exten=>102,1,Dial(${JANE},,r)
[outbound-local]
exten=>_9NXXXXXX,1,Dial(${OUTBOUNDTRUNK}/911)
exten=>_9NXXXXXX,2,Congestion()
exten=>_9NXXXXXX,102,Congestion()
exten=>911,1,Dial(${OUTBOUNDTRUNK}/911)
exten=>9111,1,Dial(${OUTBOUNDTRUNK}/911)
[outbound-long-distance]
exten=>_91NXXNXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})
exten=>_91NXXNXXXXXX,2,Congestion()
exten=>_91NXXNXXXXXX,102,Congestion()
这两个include语句让[internal]context内的主叫方可以拨打去话。应该注意到,出于安全的考虑,要确保[inbound]context永远不要允许打去话。(如果一旦给了这样的机会,外面的人可以拨入你的系统,然后再拨打收费电话出去。
阅读(2703) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~