基于Asterisk的VoIP开发指南——(2)Asterisk AGI程序编写指南

原文:基于Asterisk的VoIP开发指南——(2)Asterisk AGI程序编写指南

5. Asterisk AGI程序编写指南

5.1概述

很多时候,我们需要在拨号方案中做某些业务逻辑的判断或者外部数据库的查询,根据具体地需要,有几种做法:

1.使用Asterisk的通道变量、Goto函数、Gotoif函数等实现某些简单跳转,通过几个这样的函数的组合,实现简单的业务。

2.对终端接入用户的呼叫请求中的某些属性,进行简单的数据库增删改查,在Asterisk官方发布的asterisk-addons开发包中安装MYSQL模块,具体地方法在这不细述。使用类似下面的方式:

exten => _0[0-9].,1, MYSQL(Connect connid dhhost dbuser dbpass dbname)

exten => _0[0-9].,2., MYSQL(Query resultid ${connid} query-string)

exten => _0[0-9].,3,MYSQL(Fetch fetchid ${resultid} var1/ var2/ .../ varN)

exten => _0[0-9].,4,MYSQL(Disconnect ${connid})

3.如果碰到了结合了1、2的业务需求,这时候拨号方案配置文件中就会出现大量地与业务逻辑有关的复杂代码,造成技术人员阅读上不方便,并且代码也不好维护,如下面这段配置文件:

……

exten => 888,1,MYSQL(Connect connid localhost ipcontact passwd ipcontact)
exten => 888,n,GotoIf($["${connid}" = ""]?error,1)
exten => 888,n,MYSQL(Query resultid ${connid} SELECT/ `number`/ FROM/ `phones`/ WHERE/ `channel`=/‘${chan}/‘)
……

exten => 888,n,GotoIf($["${foundRow}" = "1"]?done) ; leave loop if no row found
exten => 888,n,NoOp(${number})
exten => 888,n,Goto(fetchrow) ; continue loop if row found
exten => 888,n(done),MYSQL(Clear ${resultid})
exten => 888,n,MYSQL(Disconnect ${connid})

……

上面综合了通道变量、Goto、Gotoif、MYSQL数据查询等操作,其实如果配置文件中只是几行这样的操作阅读起来没问题,但业务需求具有可变性,代码维护起来就比较麻烦。所以,我们就寻找另外一种方案,即在配置文件中不进行数据库查询等重量级的操作,而是将它交到一个外部应用程序或者脚本中,即封装到Asterisk的AGI后台中,可以实现各种复杂的业务需求。使用类似PHP、JAVA、C等编程语言内置的判断语句,而不是Asterisk封装的类似Goto、Gotoif等函数,会更加快地实现业务需求,代码也更好维护。下面先看看如何在拨号方案中使用AGI程序。

5.2 使用AGI脚本

执行AGI脚本时,Application应用就是‘agi‘,参数是脚本的文件名,同时脚本需要满足以下条件:

l        必须可执行,chomd 755

l        必须放置在指定目录,如标准目录:/var/lib/asterisk/agi-bin

l        必须指定完整的extension信息

比如说执行一个用PHP语言实现的AGI程序,有可能就是这样:

exten => 1,2,AGI(test.php, ${CALLERID(name)})

脚本执行时,可以从控制台上得到不同基本的详细信息,例如,文件不能被执行,或者找不到文件等等。

通过向Asterisk控制台信息输出,可以得到AGI脚本的执行信息,至少在开发初期中,控制台信息输出是个好办法。

通过agi VERBOSE命令,可以将信息发送到asterisk控制台上,并且而通过verbosity设置可以关闭开启这个功能。

如果直接,可以采用各种语言很方便的通过AGI接口编写实施脚本。脚本和Asterisk之间通过标准的输入输出进行交互。

5.3 AGI(Asterisk Gateway Interface)技术实现原理

l        传递参数到AGI脚本

在脚本名称后紧跟以英文半角状态下的逗号,分隔的字符串,就可以把需要的参数传入脚本:AGI(dial_agi.php,${EXTEN:11},${CALLERID(name)}),AGI脚本总是接收2种参数,1是脚本的完整路径,2是拨号方案中‘Exten‘传递的参数,其中第1种参数,如果AGI程序放在了Asterisk默认路径,可以省略,只写AGI程序名。第2种参数是AGI程序需要拨号方案中‘Exten‘传递进来的参数,比如说本文中${EXTEN:11},${CALLERID(name)},一个是被叫的电话号码,一个是主叫的账号ID。

l        通过标准的输出,发送命令到Asterisk

我们可以在AGI脚本程序中向Asterisk发送各种命令从而调用Asterisk的某些应用程序,如Dial、Goto、Monitor等,也可以直接发送命令获得或者设置某些Asterisk通道变量的值。对于基于PHP的AGI脚本程序,可以按照如下步骤:

1.打开PHP输出文件描述符:

$stdout = fopen(‘php://stdout‘, ‘w‘);

2.向Asterisk发送命令:

fputs($stdout," SET CONTEXT media_gw1/n"); ");
    fflush($stdout);

上述步骤是对于SET 或者 GET Asterisk的某些通道变量时的使用方法,如果需要调用Asterisk内置的应用,如执行跳转到某个context下的某个priority的Goto应用函数,可以调用EXEC命令后面紧跟Asterisk,如:fputs($stdout," EXEC  Goto  media_gw1|s|2/n"); ");命令必须以换行符结束,AGI命令返回文本字符串,如下格式:200 Result=<number>,有时会在number数字后附加一些信息。如果向Asterisk发送了无效的命令,信息如下:510 Invalid or unknown command。对应上面的命令,如下所示:

AGI Rx << SET CONTEXT media_gw1

AGI Tx >> 200 result=0

l        通过标准的输入,从Asterisk接收信息

当AGI脚本执行时,Asterisk会向脚本发送各种的信息,可以在做其他事情之前通过标准输入获取这些信息,每项数据都是一行,发送完毕Asterisk会发送一个空行,表示结束,如:

AGI  Tx >> agi_request: dial_agi.php

AGI  Tx >> agi_channel: SIP/25946-0821ea88

AGI  Tx >> agi_language: en

AGI  Tx >> agi_type: SIP

AGI  Tx >> agi_uniqueid: 1209093478.477

AGI  Tx >> agi_callerid: 0000123456

AGI  Tx >> agi_calleridname: beigaolin

AGI  Tx >> agi_dnid: 998866015810370728

AGI  Tx >> agi_context: default

AGI  Tx >> agi_extension: 998866015810370728

AGI  Tx >> agi_priority: 1

根据项目需求,如果需要这些数据,就先保存起来,否则不用处理它。保存步骤按如下过程。

1.打开PHP输出文件描述符:

$in = fopen("php://stdin","r");

2.分析从Asterisk传到AGI的头信息,如需要在AGI程序中获取终端用户的ID,那么从“agi_calleridname: beigaolin”这个头信息可以获取,我们通过分析每一行这样以:分隔的字符串,取到需要后续处理的字符串

while (!feof($stdin)) {
  $temp = fgets($stdin);
  $temp = str_replace("/n","",$temp);
  $s = explode(":",$temp);
  $agivar[$s[0]] = trim($s[1]);
  if (($temp == "") || ($temp == "/n")) {
     break;
     }
  }

5.4 使用开源PHP AGI类函数PHPAGI

像上一小节那样先是获取输入流,分析从输入头字符串中获取对应某个输入变量的值,或者获取输出流然后发送各种标准命令执行某些Asterisk内置应用,如果在AGI程序中实现很复杂的业务逻辑,这样的流程会显得有点累赘,所以需要提取某些常用的操作,我们使用的时候不用关心这些操作,直接以调用类似Asterisk内置应用那样的方式。PHPAGI就是这样的一个开源PHP类函数。它封装了对应Asterisk内置应用的常用函数调用接口,比如说从PHP向Asterisk发送Dial命令的操作,可以直接调用PHP AGI类函数中的exec_dial。使用PHP AGI能够很容易的操作Asterisk AGI常用接口。使用这个类函数也很简单:

l        下载准备phpagi 函数文件:

cd /var/lib/asterisk/agi-bin/(也有可能在用户自定义的路径中)

wget http://nchc.dl.sourceforge.net/sourceforge/phpagi/phpagi-2.14.tgz

tar zxvf phpagi-2.14.tgz

l        在代码中使用:

include ("phpagi.php");//包含文件

include ("phpagi-asmanager.php");

$agi = new AGI;//引用PHPAGI类函数

5.5 使用AGI实现主叫号码透传功能

在这里以一个例子来说明AGI程序在VoIP开发中的作用以及开发思路。

假设说有个普通电话为02412345678,手机号为15810370728,而网络电话虚拟号码是0000123456,如果想让拨打出去的电话号码在被叫方(手机或者带有来电显示功能的座机)的来电显示为02412345678或者15810370728,那么他们回复电话的时候就可以直接打到这个普通电话上,方便与主叫的业务联系。这个需求就叫主叫号码透传,能不能进行主叫号码的透传,取决于VoIP落地网关运营商,语音网关可以设置IP侧送过来的主叫号码是否透传。在保证号码规范的前提下,透传什么样的主叫号码,则取决于IP-PBX系统,即Asterisk的设计了。

l        设计思路

1.增加一个针对终端用户账户ID的绑定管理系统,如图用户在第二项中输入自己的账户ID,然后再输入想要作为来显示的主叫号码完成绑定操作,后台php程序向数据库中插入一条新记录(X-Lite ID对应电话号码或者手机号码)。

图5-2 AGI后台管理系统页面

2.使用绑定了主叫号码的X-Lite呼叫某个被叫(手机或者座机)

Asterisk的后台PHP AGI程序的详细设计主叫号码透传流程设计如图4-4所示。

图5-3 Asterisk 主叫号码透传的后台PHP AGI流程图

l        代码实现

以下代码片断展示的是PHP AGI中部分代码,并且作了简化。

#!/usr/local/php.5.2.5/bin/php –q

<?php

include_once("phpagi.php");//开源PHP类函数

......

//判断当前这个id是否做了主叫号码来电显示的绑定操作

$query_string = "select * from  xliteid where xliteid = ‘{$caller_name}‘";

$query_result = mysql_query($query_string, $db_connection);

//如果当前这个id做了绑定操作,调用PHPAGI类函数,设置Asterisk主叫号码

if($query_result && mysql_num_rows($query_result) > 0)

{

caller_phone_display_agi ();

}

//没有做绑定,设置一个随机的号码

else

{

caller_name = $argv[2];

$rand_num1 = rand(0,9);

$rand_num2 = rand(0,9);

$rand_num3 = rand(0,9);

$caller_phone= "024{$rand_num1}{$rand_num2}650{$rand_num3}{$rand_num4}";

land_media_gw1($caller_phone);

exit();

}

/**

*@caller_phone_display_agi  主叫号码特殊显示

*/

function caller_phone_display_agi()

{

global $db_connection, $callee_phone, $caller_name;

$query_string = "select caller_phone from caller_phone_display _xliteid where skype_id = ‘{$caller_name}‘";

$query_result = mysql_query($query_string, $db_connection);

if($query_result && mysql_num_rows($query_result) > 0)

{

$row = mysql_fetch_array($query_result);

$caller_phone = $row[0];

$callerid_cli = "/"{$caller_name}/"<{$caller_phone}>";

land_media_gw1($callerid_cli);

exit();

}

}

/**

*@ land_media_gw1  VoIP语音网关media_gw1

*/

function  land_media_gw1($callerid_num)

{

global $agi, $callee_phone_withpre;

$agi->set_context("media_gw1");

$agi->set_extension($callee_phone_withpre);

$agi->set_priority(1);

//调用phpagi封装的set_callerid方法,向Asterisk传递设置主叫号码的指令

$agi->set_callerid($callerid_num);

}

对X-Lite账户gaolinb作了主叫号码绑定,使用X-Lite软终端呼叫普通的手机,在Asterisk中设置了agi debug,从Asterisk后台我们可以清晰地看到:

1.AGI Tx >> *CLI>上面部分,全是从Asterisk输入到当前AGI的环境变量信息,它包含了当前这个呼叫的详细信息,如Channel的类型,是SIP还是H.323,calleridname,即终端用户是gaolinb等重要信息。

2.AGI Tx >> *CLI>下面部分,全是在上面调用PHPAGI类函数后将命令传给了AGI程序执行,对于主叫号码来电显示的命令是:

SET CALLERID ‘gaolinb’<15810370728>,Asterisk将15810370728传到能够支持主叫号码透传的VoIP运营商,从而被叫用户在接听电话前能够显示一个有意义的电话号码。

图5-4 Asterisk服务器上AGI的输入输出信息

时间: 2024-10-13 11:18:40

基于Asterisk的VoIP开发指南——(2)Asterisk AGI程序编写指南的相关文章

基于Asterisk的VoIP开发指南——(1)实现基本呼叫功能

原文:基于Asterisk的VoIP开发指南--(1)实现基本呼叫功能 说明: 1.本文档探讨基于Asterisk如何实现VoIP的一些基本功能,包括基本呼叫功能的方案选取.主叫号码透传.如何编写Asterisk AGI程序.Radius认证计费模块等. 2.本文档VoIP软终端使用X-Lite,其它终端均可以接入测试. 3.文章内容仅供参考,转载请注明出处.   1 VoIP系统相关协议和标准 由于I P电话技术标准的开发涉及多个领域,因此,VOIP系统要想实现这些IP电话之间的通信,则必须提

基于Asterisk的VoIP开发指南——Asterisk 模块编写指南(1)

原文:基于Asterisk的VoIP开发指南--Asterisk 模块编写指南(1) 1 开源项目概述 Asterisk是一个开源的软件包,通常运行在Linux操作系统平台上.Asterisk可以用三种协议来实现VoIP,同时可以与目前电话使用的标准硬件进行交互通信,Asterisk在实现VoIP时,不需要任何附加硬件,本文所采用的也是这种使用方式.但是,如果企业没有与VoIP语音网关运营商建立合作关系,想要自己构建这样的一个平台,那么要和数字电话设备与模拟电话设备进行交互通信,Asterisk

基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(目录)

本系统文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客户端应用程序的设计与开发. 基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(1:概述)

基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(1:概述)

本系统文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客户端应用程序的设计与开发. 目录: http://www.cnblogs.com/sheng_chao/p/6084144.html 前言: 可能是接触计算机比较早,从96年左右386开始,到 Trubo C,以及后来的Foxpro.VB.Delphi,一直以来似乎都有一种客户端程序情节,喜欢写客户

基于Node.js+MySQL开发的开源微信小程序B2C商城(页面高仿网易严选)

高仿网易严选的微信小程序商城(微信小程序客户端) 界面高仿网易严选商城(主要是2016年wap版) 测试数据采集自网易严选商城 功能和数据库参考ecshop 服务端api基于Node.js+ThinkJS+MySQL 计划添加基于Vue.js的后台管理系统.PC版.Wap版 GitHub: https://github.com/tumobi/nideshop-mini-program 项目截图 首页 专题 分类 商品列表 商品详情 购物车 订单中心 功能列表 首页 分类首页.分类商品.新品首发.

关于前端开发的20篇文档与指南-转

关于前端开发的20篇文档与指南 前端开发开发者CSSJavaScriptHTMLWeb 摘要:前端开发者中相信很多人会有两种感受:真的不知所措,这个行业到底有多少东西需要去学习:渴望更多,并迫不及待的为接下来的学习寻求一些思想方向.本文整理一些有用的信息,希望可以帮助相关领域的前端开发人员. 相信在2015年很多这个行业的人都会有这样的两种感受: 真的不知所措,这个行业到底有多少东西需要去学习: 渴望更多,并迫不及待的为接下来的学习寻求一些思想方向. 第一个来自于我们的个人感受,而第二个则是纯粹

写给 Android 开发的小程序布局指南,Flex 布局!

一.序 Hi,大家好,我是承香墨影! 最近在做小程序,验证一些方向,开发效率确实很快,就是各种微信的审核有点费劲,但是总归是有办法解决的. 想要开发一款小程序,其实和我们正常写一款 App 类似,你需要有精美的前端布局,并且同时还需要处理和前端 UI 组件的交互以及它们背后的逻辑. 最近会分享一些关于小程序的内容(不保证,想到哪里写到哪里),今天先说说接触小程序第一步:布局. 如果有前端经验的话,小程序是非常容易上手的,而对于新手,第一步当然是阅读小程序的官方文档,不过之后你马上就要面临布局的问

基于TINY4412的Andorid开发-------简单的LED灯控制【转】

本文转载自:http://www.cnblogs.com/pengdonglin137/p/3857724.html 基于TINY4412的Andorid开发-------简单的LED灯控制 阅读目录(Content) 一.编写驱动程序 二.编写代码测试驱动程序 三.编写HAL代码 四.编写Framework代码 五.编写JNI代码 六.编写App 参考资料: <Andriod系统源代码情景分析> <嵌入式Linux系统开发完全手册_基于4412_上册> 作者:彭东林 邮箱:[em

开发环境——基于RHEL 6.8开发环境的搭建

开发环境--基于RHEL 6.8开发环境的搭建 本文讲述了基于RHEL 6.8操作系统搭建开发环境,主要安装了GCC 4.9.3.Qt 5.7.Apache 2.4.25.MySQL 5.6.35.本文同时适用于在Linux主机无互联网环境下搭建开发环境,只需要将所需安装包下载后拷贝到Linux主机即可. 一.RHEL6.8安装 1.RHEL 6.8下载 下载RHEL 6.8安装包:rhel-server-6.8-x86_64-dvd.iso 2.RHEL 6.8安装 安装过程中选择Develo