基于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();                        

}      

/**

主叫号码特殊显示

*/

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的输入输出信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win7关闭休眠_【科普】笔记本电脑,待机/睡眠/休眠有啥区别?(ACPI SPM S0-S5)...

本文聊聊笔记本电脑话题下&#xff0c;经常有人问到的内容——关机 睡眠 休眠 这三个选项有什么区别&#xff1f;电脑可以不关机只合上盖子吗&#xff1f;花了俩小时&#xff0c;求个赞&#xff0c;希望有专栏能收这篇内容~先说答案&#xff1a;快速唤醒&#xff1a;开盖/操作鼠…

k8s dashboard_【大强哥-k8s从入门到放弃02】Kubernetes1.17部署Dashboard2.0

号外号外&#xff0c;后面所有提升视频都会更新到知乎和B站上去&#xff0c;不会直接发群里了&#xff0c;哈哈&#xff0c;能看懂这句话的我都认识&#xff0c;大家可以先关注一下&#xff0c;我知乎上的所有文档也会录成视频更多视频详见 杨哥天云&#xff1a;https://space.…

Andoid 采用ListView三种显示数据列表

Android系统中列表形式的显示方式应该是我们最熟悉不过的界面了&#xff0c;例如通讯录、通话记录、信息列表等等&#xff0c;例如下面的形式&#xff1a; 我们在开发项目需要用到这种形式显示信息时除了调用系统给我们提供的ListView控件以外我们还可以自定义该控件&#xff0…

url采集器_Linux「第三节」-centos7.5部署数据采集器Telegraf

Telegraf采集器采集数据后&#xff0c;会存储到influxdb数据库&#xff0c;然后grafana从influxdb读取数据&#xff0c;在grafana面板上展示&#xff0c;整体的流程就是这样。下面就演示下Telegraf、influxdb、grafana相互间的配置与衔接下载Telegrafwget https://dl.influxdat…

西门子主程序调用子程序_S7200Smart 子程序局部变量使用教程

全新的 S7-200 SMART 带来两种不同类型的 CPU 模块&#xff0c;标准型和经济型&#xff0c;全方位满足不同行业、不同客户、不同设备的各种需求。标准型作为可扩展 CPU 模块&#xff0c;可满足对 I/O 规模有较大需求&#xff0c;逻辑控制较为复杂的应用 &#xff1b;而经济型 C…

电脑怎么结束进程_深刻了解windows系统的任务管理器,电脑高手的成长之路

伙伴们我们每天都在用电脑&#xff0c;电脑和手机已经离不开了&#xff0c;可以说手机和电脑就是我们的左膀右臂。我们用电脑的时候&#xff0c;如果电脑卡了或者反应慢了&#xff0c;我们都会想到吧任务管理器调出来看看&#xff0c;可以说调出任务管理器就像医生看病一样&…

手机连接投影机的步骤_家用投影仪如何安装 家用投影仪安装方法【步骤详解】...

家用投影仪怎么安装 根据安装方式的不同&#xff0c;投影仪可以分为桌式正投、吊顶正投、桌式背投、吊顶背投几种。方便随时随地使用的便携式投影仪一般是桌式正投&#xff0c;这种投影方法受环境光影响较大 但如果装修时就设计成吊顶正投方式&#xff0c;不仅在性价比方面更加…

win7锁屏时间怎么设置_电脑锁屏时间怎么设置

以WIN10系统为例演示。1/3打开“控制面板”&#xff1b;点击“电源选项”2/3点击“更改计划设置”&#xff1b;设置锁屏时间3/3点击“保存修改”即可

Ajax原理

引用&#xff1a;http://www.cnblogs.com/xugang/archive/2007/10/16/925993.html 看了“坐断东南 笑煞之”的那篇 .net组件开发系列(二)之武林系列 太极拳 开发ajax控件 总有一种意犹味尽的感觉。没有想到 金色海洋&#xff08;jyk&#xff09;竟“晕了”&#xff0c;我感觉写…

cdh界面 hue 配置hbase_海量数据存储技术之HBase:使用HBase Shell操纵HBase

Apache HBase Shell是JRuby的IRB&#xff0c;并在此基础上加入了HBase特有的命令。JRuby是用Java实现的Ruby解释器。通过JRuby&#xff0c;你可以在JVM上直接运行Ruby程序&#xff0c;调用Java的类库。IRB即interactive ruby&#xff0c;交互式Ruby。运行HBase Shell&#xff1…

安卓 图像清晰度识别_智能车牌识别系统的常见故障和解决方法

车牌号是车辆的唯一身份标志&#xff0c;它的特殊性与重要性从而决定了车牌识别系统在智慧城市交通管理系统中成为不可或缺的重要组成部分。下面智能车牌识别系统厂家畅荧智能科技将详细为您介绍一些智能车牌识别系统常见的故障问题和对应的解决方法。智能车牌识别系统的常见故…

post postman 传递数组对象_如何使用postman做接口测试

一&#xff0c;接口测试是什么什么是接口测试&#xff0c;不说广泛的概念&#xff0c;直接电脑打开浏览器&#xff0c;进入开发者工具(F12)随意进入一些网站可以看到下面左侧栏出现了一系列的数据&#xff1a;上面条数据都是一个单独的接口&#xff0c;点击单个接口&#xff0c…

形位公差符号大全_玩转CAD快捷键(大全),一篇文章就够了

CAD快捷键命令大全符号键&#xff08;CTRL开头&#xff09;CTRL1 PROPCLOSEOROPEN 对象特性管理器CTRL2或4 ADCENTER 设计中心CTRL3 CTOOLPALETTES 工具选项板CTRL8或QC QuickCalc 快速计算器控制键CTRLA AI_SELALL 全部选择CTRLC或CO/CP COPYCLIP或COpy 复制CTRLD或F6 COORDIN…

k近邻算法_K近邻(knn)算法是如何完成分类的?

摘要&#xff1a;K近邻算法是机器学习中的一个非常基础的算法。本文通过自生成数据&#xff0c;通过绘图的方式演示KNN算法的思路&#xff0c;让你不看数学公式就看了解什么是KNN算法。关键词&#xff1a;KNN算法1 生成一个二分类的数据集本文很多内容参考文献[1]。先生成一个两…

登录不上_《盗贼之海》登录不上?还在傻傻等待,快来让我教教你

大家都对这款海盗题材的游戏《盗贼之海》有兴趣了吧&#xff0c;相信大家都入手了它&#xff0c;但是也有很多朋友在入手后遇见了很多问题&#xff0c;别怕&#xff0c;今天干货来了&#xff0c;手把手教你。保证你流畅游戏&#xff0c;快人一步。我看见很多朋友都在问&#xf…

苹果X可以升级5G吗_苹果x可以用5g网络吗

随着5G网络的逐渐商用&#xff0c;越来越多5G原型机和量产机都即将推出&#xff0c;那么苹果iPhone X能够支持5G网络吗&#xff1f;下面就为您带来苹果iPhone x可以用5G网络吗的相关介绍&#xff0c;希望以上的介绍能够帮助到您。苹果x可以用5g网络吗1、iPhone X并不支持5G网络…

提交显示成功但是没有看到文件_如何向RTThread提交一个BSP?

RT-Thread今天的快速发展和所取得成绩&#xff0c;离不开所有开发者的持续贡献和社区小伙伴的竭力支持。一、前言今年6月&#xff0c;我在一款智能混合型的FPGA芯片上&#xff0c;完成了RT-Thread的移植&#xff0c;并向RT-Thread提交了一个完整的BSP&#xff0c;后续又根据审查…

WinForm中使用WPF的控件

在WinForm中可以使用WPF中的控件&#xff0c;或者由WPF创建的自定义控件&#xff1b; 步骤1&#xff1a;创建WinForm工程&#xff1b; 步骤2&#xff1a;在WinForm工程的解决方案资源管理器中&#xff0c;在刚刚创建的WinForm解决方案中新建或者添加现有的WPF用户控件工程&…

docker php composer 使用_「PHP编程」如何使用Docker制作自己的LNMP/LAMP镜像

LNMP和LAMP是PHP常用的两种运行环境&#xff0c;L代表Linux&#xff0c;N代表Nginx&#xff0c;A代表Apache&#xff0c;M代表Mysql&#xff0c;P代表PHP。在文章《「PHP编程」安装开发环境太烦&#xff1f;告诉你几个简单方法&#xff0c;分分钟搞定》中我们介绍了&#xff0c…

中天数相减获得差_Power BI 了解DAX中LASTDATE和MAX之间的区别

许多刚开始使用DAX的小伙伴在使用LASTDATE搜索某个时间段内的最后日期。或者他们使用NEXTDAY检索给定日期之后的日期。尽管这些函数可以实现它们所承诺的功能&#xff0c;但它们并不打算在简单的表达式中使用。相反&#xff0c;它们是设计用于时间智能计算的表函数。错误地使用…