拨号计划是你的Asterisk系统的心脏。它定义了呼叫是如何流进和流出系统的。拨号计划用一种脚本语言写成的,Asterisk依照其中的指令响应外部触发。和传统电话系统相比,Asterisk的拨号计划是完全可定制的。
本章介绍Asterisk的基本概念。这里讲的内容对你理解拨号计划代码至关重要,同时也是你写任何拨号计划的基础。示例的设计是有前后承接关系的,我们建议你不要逃过本章的太多内容,因为本章对理解Asterisk十分重要。也请你明白本章不可能是对拨号计划的能力的完全阐述;我们的目标是基础知识。后面的章节我们会介绍拨号计划更高级的内容。我们鼓励你做试验。
Asterisk拨号计划在名为extensions.conf的配置文件中定义。
注:extensions.conf文件通常位于/etc/asterisk目录下,但它的位置会视你如何安装Asterisk而不同。这个文件的其他常见位置包括/usr/local/etc/asterisk和/opt/etc/asterisk。
拨号计划由四个主要概念构成:上下文、分机、优先级和应用程序。在解释完这些要素在拨号计划中各自扮演的角色后,我们将让你建立一个基本的但是能工作的拨号计划。
注:示例配置文件。如果你在安装Asterisk的时候安装了示例配置文件,你就很可能已经有了一个现成的extensions.conf文件。不要从示例文件喀什,我们建议你从空白开始建立你自己的extensions.conf文件。对于学习如何建立拨号计划来说,从示例文件开始不是最好的方式,也不是最容易的方式。
拨号计划被分成不同的部分,称为上下文。上下文使得拨号计划的不同部分之间不会发生交互。在某个上下文中定义的分机是跟任何其他上下文中的分机完全隔离的,除非明确指定有交互。我们会在本章的末尾讲到如何允许上下文之间发生交互。
作为一个简单的例子,想象一下我们有两家公司共享一台Asterisk服务器。如果我们把每家公司的自动接待都放在他们各自的上下文中,他们彼此间就完全分开了。这就允许我们独立地定义什么分机做什么,比如拨分机0的时候:从A公司的语音菜单拨分机0会找到A公司的接待员,而从B公司的语音菜单拨分机0会找到B公司的接待员。(当然,这也表示我们已经告诉了Asterisk拨0的意思就是把呼叫转到接待员。)
上下文通过把名字放到方括号中来定义。名字由字母(大写或小写)、数字、连字符(就是减号)和下划线构成。入局呼叫(incoming>
注:“插”这个概念对于掌握信道和拨号计划至关重要。一旦你理解了在信道中指定的上下文和拨号计划中相应的上下文的关系,就很容易调试呼叫流程的异常问题了。
上下文一个很重要的用途(也许是最重要的用途)就是提供安全控制。通过正确地使用上下文,你能够控制某些特性(比如打长途电话)只对部分用户开放。如果你设计拨号计划的时候不谨慎,就可能有人不正当地使用你的系统。构建Asterisk系统时一定要留意这一点;Internet上有很多机器人程序专门设计用于识别那些不安全的Asterisk系统,然后盗用它们。
注:https://wiki.asterisk.org/wiki/display/AST/Important+Security+Considerations总结了一些使你的Asterisk系统保持安全的步骤,阅读并理解这篇文章至关重要。如果你忽略这些安全警告,可能会导致任何人都可以用你的系统打长途电话而不用掏钱!如果你不认真地对待Asterisk的系统安全,可能会损失惨重。请确保在系统安全上有足够的投入,以防止盗用。
在通讯行业,分机这个词一般指的是一串数字,当拨出的时候,可以打通一个电话(或者访问某项系统资源,比如语音信箱,或呼叫队列)。在Asterisk中,分机的概念要强大很多,因为它定义了一个步骤序列(每个步骤包含一个应用程序),Asterisk会按照这些步骤来处理呼叫。
在每个上下文中,我们可以根据需要定义任意数量的分机。当某个分机被触发(通过入局呼叫,或者通过信道上拨出的数字)时,Asterisk会执行分机中定义的步骤。所以,当呼叫流经拨号计划时,是分机决定了系统做什么。分机当然可以用于指定传统意义上的电话号码(比如,分机153可以拨通John的SIP电话机),但在Asterisk中,分机的用途远不止于此。
分机的语法是,一个关键字exten,后面跟一个由等号和大于号构成的箭头,像这样:
exten =>
后面再跟分机的名字(或数字)。当使用传统电话系统时,我们倾向于认为分机就是用于打通另外一部电话的数字号码。在Asterisk中,完全不一样;例如,分机名可以是字母、数字的任意组合。在本章和下一章,数字分机和字母数字分机我们都会用到。
注:为分机指定名字看起来是一个革命性的概念,但是当你意识到很多VoIP运营商支持(甚至鼓励)用名字或者email地址拨号时,这种方式是很有意义的。这是使得Asterisk灵活和强大的特性之一。
分机中的每个步骤都是由三个组件构成的:
这三个组件用逗号隔开,像这样:
exten =>>在这个例子中,分机名是123,优先级是1,应用程序是Answer()。
优先级
每个分机能够有多个步骤,叫做优先级。优先级按顺序编号,从1开始,并执行一个指定的应用程序。看下面这个例子,分机123接听电话(优先级1),然后挂断(优先级2):
exten => 123,1,Answer() exten => 123,2,Hangup()很明显,这段代码没有做什么有意义的事情。这里要说明的问题是,在一个分机中,Asterisk按顺序执行优先级。这种风格的语法还会不时地出现,尽管新代码已经不再这么写了(你很快就会看到了):
exten => 123,1,Answer() exten => 123,2,do>不编号的优先级
在老版本的Asterisk中,给优先级编号引起了不少问题。想象一下,某个分机有15个优先级,后来需要在第2步插入什么东西:后面所有的优先级都需要重新编号。Asterisk不能处理漏掉的步骤,也不能处理编错了号的优先级,调试这种问题会很没有头绪,也很没意思。
从1.2开始,Asterisk解决了这个问题:它引入了优先级n的使用,n表示next。只要Asterisk碰到了优先级n,就把上一个优先级拿过来加1。这就使得修改拨号计划容易了,因为你不在需要为所有步骤编号了。例如,你的拨号计划看起来会像这样:
exten => 123,1,Answer() exten => 123,n,do>在内部,Asterisk碰到n时会计算优先级编号。需要注意的是,你必须指定优先级编号1。如果你不小心为第一个优先级指定了n而不是1,你会发现重新装载拨号计划后该分机不存在。
“same =>”操作符
在简化编码工作的持续努力下,一个新的结构出现了,使得分机的编写和维护更容易。只要分机不变,就不再需要每行都写分机名了,只需要写same =>,后面跟优先级和应用程序:
exten => 123,1,Answer() >缩进不是必须的,但它便于阅读。这种风格的拨号计划,使得它更容易在分机之间拷贝代码。我们自己很喜欢这种风格,也强烈建议它。
优先级标号
优先级标号允许你为分机中的优先级指定一个名字。这使得你可以避免用数字引用某个优先级(而且你也知道,优先级是可以不编号的)。能够引用优先级之所以重要是因为,你会经常把呼叫从拨号计划的一个部分转移到某个分机的某个优先级。后面我们会详细谈到这个问题。要为优先级指定文字标号,简单地把标号放到优先级后面的括号里就可以了,像这样:
exten => 123,n(label),application()后面我们会讲到如何根据拨号计划的逻辑,在不同的的优先级之间实现跳转。你会看到很多优先级标号,你也会在自己的拨号计划中用到它们。
一个常见的错误就是,写标号的时候在n和(之间插入一个逗号,像这样:
exten => 123,n,(label),application() ;<-- THIS IS NOT GOING TO WORK这个错误将导致该部分拨号计划无法工作,并且会有错误提示说“应用程序找不到”。
应用程序
应用程序是拨号计划的工作部件。每个应用程序在当前信道上执行一个特定的动作,比如播放一段声音,接收按键输入,在数据库中查找什么东西,拨打一个信道,挂断电话,诸如此类。在上面的例子中,你看到了两个简单的应用程序:Answer()和Hangup()。你很快就会学习他们是如何工作的。
一些应用程序,像Answer()和Hangup(),不需要更多的信息就能完成工作了。然而,大多数应用程序都需要额外的信息。这些额外的信息被称为参数,被传递给应用程序以影响它们如何完成工作。要给应用程序传递参数,把它们放到应用程序名后面的括号里,用逗号隔开。
注:有时候你也会看到用管道符(|)分隔参数,而不是逗号。从Asterisk 1.6.0开始,就不再支持用管道符作为分隔符了。
Answer(),Playback()和Hangup()应用程序
Answer()应用程序用于应答一个正在响铃的信道。它会完成信道的初始设置,以便接收来电。正如我们之前提到的,Answer()没有参数。Answer()不是必须的(在有些情况下甚至不用它更好),但它能保证在执行进一步的动作之前信道已经被连接了。
注:Progress()应用程序。有时候在应答一个呼叫之前向网络传回一些信息是很有用的。Progress()应用程序尝试向来电信道提供呼叫进度信息。有些运营商需要这个,所以有时候你可以通过插入一个Progress()来解决一些奇怪的信号问题。
Playback()应用程序用于在信道上播放一个事先录制的声音文件。用户输入被忽略了,这意味着你不能在自动接待中使用Playback(),除非你不想在其间接收用户输入。
注:Asterisk有很多专业录制的声音文件,位于缺省的声音目录下(一般是/var/lib/asterisk/sounds)。编译Asterisk的时候,你可以选择安装各种不同语言、不同格式的声音文件。我们将在很多例子中使用这些文件。例子中的有些文件来自附加声音包,所以请安装它(见“安装Asterisk”)。你也可以访问http://www.theivrvoice.com/,以同样的语音录制你自己的声音提示。本书的后面,我们还会谈到如何用电话和拨号计划建立和管理你自己的声音提示。
使用Playback(),需要指定一个文件名(不带扩展名)作为参数。例如,Playback(filename)将播放名为filename.wav的声音文件,假设它位于缺省的声音目录下。你也可使用文件的完整路径,像这样:
Playback(/home/john/sounds/filename)这个例子将播放/home/john/sounds/目录下的filename.wav文件。使用相对路径也可以,比如:
Playback(custom/filename)这个例子将播放缺省声音目录的子目录custom/下的filename.wav文件(可能是/var/lib/asterisk/sounds/custom/filename.wav)。如果指定的路径下存在多个同名但扩展名不同的文件,Asterisk将自己选择一个(根据转码的代价)。
Hangup()做得事情就和它的名字一样:它挂断当前的活动信道。你想结束当前呼叫的时候应该使用它,以防止在你没有意识到的情况下当前呼叫还被保持在拨号计划的某处。Hangup()应用程序不需要参数,但你可以给它传一个ISDN码,如果你希望的话。
随着本书的展开,我们将会向你介绍更多的Asterisk应用程序。