Asterisk Summary - 5

来源:互联网 发布:甘肃省网络研修社区 编辑:程序博客网 时间:2024/05/18 13:12

         看了前面小节的内容后,如果你现在正在做asterisk客户端的开发,我想你为了很好的实现第四节中的multiline也花了不少的时间吧。至少我是这样,不然也不会现在才写第五节内容。 

首先讲一讲我在实现multiline中花费很多时间做一项工作 同步。由于iaxc工作的话会启用一条线程(iaxc_start_processing_thread();),为了不让你的程序出现异常,我们必须要小心翼翼的处理callNo callState main thread iaxc thread的同步问题。实现同步当然就要利用锁机制啦,java中只需要假如synchronized C/C++就要利用互斥变量,信号量等来加锁。同时,如果有些异常还是无法避免的话,你可要小心的处理这些异常,不让你的程序轻则陷入混乱状态,重则崩溃(当然,你应该不希望看到这种情况的发生)。

接下来我要说一说这一节的主题—AMI(Asterisk Manager Interface)

 

What is AMI

          The Asterisk Manager Interface (AMI) allows a client program to connect to an Asterisk instance and issue commands or read events over a TCP/IP stream. Integrators will find this particularly useful when trying to track the state of a telephony client inside Asterisk, and directing that client based on custom (and possibly dynamic) rules.
      A simple "key: value" line-based protocol is utilized for communication between the connecting client and the Asterisk PBX. Lines are terminated using CR/LF. For the sake of discussion below, we will use the term "packet" to describe a set of "key: value" lines that are terminated by an extra CR/LF.

      简单来说就是你可以与asterisk server进行telnet通信,并且按照特定的格式向asterisk服务器发送指令来完成特定的任务。你是否想为你的super account实现一个监视的功能,好让这个supervi- sor可以实时的监视各个agent的状态,例如idledialingtalkingholdtalk-time。没错,这个时候AMI就派上用场啦!

Protocol Behavior

AMI的通信协议:

1. 你首先得建立一个与asterisk服务器的可靠授权连接。Eg: telnet server_ip 5038

2. 你向服务器发送的包应该是plain text,并且以特定的格式发送:第一行必须是 Action : ActionName

   同时,服务器向client发送回复的第一行必然是 Event : EventName Response : ResponseName

3. 每行的结束标志位CR/CL,在code层面也就是”/r/n”啦。从第二行开始,你可以描述这个Action的行为特性,具体有哪些行为特性可以参考: http://www.voip-info.org/wiki/view/Asterisk+manager+API http://www.voip-info.org/wiki/view/asterisk+manager+events

4. 要发送command 时只需要输入”/r/n/r/n”,telnet中就是连续两个回车换行符。

Opening a Manager Session and Authenticating as a User

        通常,asterisk server都会开放5038端口来给manager通过TCP/IP建立连接和登陆。你可以通过配置/etc/asterisk/manager.conf 来改变这个端口和设定允许连接的IP范围,以及设置各个用户的asterisk权限。这些权限有(节选自/etc/asterisk/manager.conf

; Read authorization permits you to receive asynchronous events, in general.

; Write authorization permits you to send commands and get back responses.  The

; following classes exist:

;

; system    - General information about the system and ability to run system

;             management commands, such as Shutdown, Restart, and Reload.

; call      - Information about channels and ability to set information in a

;             running channel.

; log       - Logging information.  Read-only.

; verbose   - Verbose information.  Read-only.

; agent     - Information about queues and agents and ability to add queue

;             members to a queue.

; user      - Permission to send and receive UserEvent.

; config    - Ability to read and write configuration files.

; command   - Permission to run CLI commands.  Write-only.

; dtmf      - Receive DTMF events.  Read-only.

; reporting - Ability to get information about the system.

; cdr       - Output of cdr_manager, if loaded.  Read-only.

; dialplan  - Receive NewExten and VarSet events.  Read-only.

; originate - Permission to originate new calls.  Write-only.

        为了增加一个manager用户,你只需要在manager.conf中增加一个section eg

[super]

secret= super

read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr

write = system,call,agent,user,config,command,reporting,originate

        打开windowscmd,然后输入 telnet  asterisk_ip 5038 然后登陆super用户,你将见到:

 

登陆成功后,你将收到很多asterisk发来的Event(如果有的话),为了只发送action command而不去接受event 你可以在Secret: super 后面再加入一行 events: off。登陆后若想登出,只需要键入Action logoff就可以啦!

 

 

Action Packets

To send Asterisk an action, follow this simple format:

   Action: <action type><CRLF>
   <Key 1>: <Value 1><CRLF>
   <Key 2>: <Value 2><CRLF>
   ...
   Variable: <Variable 1>=<Value 1><CRLF>
   Variable: <Variable 2>=<Value 2><CRLF>
   ...
   <CRLF>

 

Programing On AMI

        暂时我编写的AMI程序是用Java写的,如果你一定要问我为什么不是CC++呢?那我的回答就是,用Java的话已经有现成的AMI开发包啦,这个开发包已经把ActionEvent都封装好了。为了节省开发时间所以就选择了Java。(详情请参考: http://asterisk-java.org/development/ http://asterisk-java.org/development/apidocs/index.html )若想要这些开发包或源代码的话,可以联系我。

          为了熟悉AMIActionEvent,我这里给了一个Java AMI的例子,程序中主要查看了BridgeEvent/ UnlinkEvent/StatusEvent, 同时可以发送BridgeAction/StatusAction/RedirectAction。这些都是为之后要实现transfer call3 way conference的功能打下基础。

/* * To change this template, choose Tools | Templates * and open the template in the editor. */package testast;import org.asteriskjava.manager.AuthenticationFailedException;import org.asteriskjava.manager.ManagerConnection;import org.asteriskjava.manager.ManagerConnectionFactory;import java.io.IOException;import java.util.Scanner;import org.asteriskjava.manager.ManagerEventListener;import org.asteriskjava.manager.TimeoutException;import org.asteriskjava.manager.action.BridgeAction;import org.asteriskjava.manager.action.RedirectAction;import org.asteriskjava.manager.action.StatusAction;import org.asteriskjava.manager.event.BridgeEvent;import org.asteriskjava.manager.event.JitterBufStatsEvent;import org.asteriskjava.manager.event.ManagerEvent;import org.asteriskjava.manager.event.RtcpReceivedEvent;import org.asteriskjava.manager.event.RtcpSentEvent;import org.asteriskjava.manager.event.RtpReceiverStatEvent;import org.asteriskjava.manager.event.RtpSenderStatEvent;import org.asteriskjava.manager.event.StatusEvent;import org.asteriskjava.manager.event.UnlinkEvent;/** * * @author Dolphin Cheung (E-Mail: dolphin98629@163.com) */public class Main {    /**     * @param args the command line arguments     */    public static void main(String[] args) {        // TODO code application logic here        boolean quit = false;        Scanner sc = new Scanner(System.in);        System.out.println("Press Quit to quit ");        MyAMI ami = new MyAMI();        do{            String input = sc.next();            if(input.equals("Quit"))                quit = true;         if(input.equals("Transfer"))         {            if(ami.transfer())            {               ami.clearBridgeChan(0);               ami.clearBridgeChan(1);            }         }         if(input.equals("ChanState"))         {            System.out.println("input chan :");            input = sc.next();            ami.sentStatusAction(input);         }            if(input.equals("Redirect"))         {            System.out.println("input chan1 :");            String ch1 = sc.next();                System.out.println("input chan2 :");            String ch2 = sc.next();            if(ch2.equals("null"))               ami.redirectCall(ch1,"");            else               ami.redirectCall(ch1,ch2);         }        }while(!quit);        ami.removeEventListener();      ami.logOff();    }}class MyAMI {    public ManagerConnection managerConn;    public MyAstEvents astEvents;   String bridgeChan[][] = new String[2][2];   public MyAMI()   {      managerLogin();      astEvents = new MyAstEvents();        managerConn.addEventListener(astEvents);   }    public void removeEventListener()    {        managerConn.removeEventListener(astEvents);    }   public void logOff()   {      managerConn.logoff();   }   public void managerLogin()   {      ManagerConnectionFactory factory  = new ManagerConnectionFactory ("xxx.xxx.xxx.xxx", "super", "super");      managerConn = factory.createManagerConnection();      try {                managerConn.login();            } catch (IllegalStateException ex) {                ex.printStackTrace();            } catch (IOException ex) {                ex.printStackTrace();            } catch (AuthenticationFailedException ex) {                ex.printStackTrace();            } catch (org.asteriskjava.manager.TimeoutException ex) {                ex.printStackTrace();        }   }   public void dispalyBridgeChan()   {      for(int i=0;i<2;i++)         System.out.println(i+" : "+bridgeChan[i][0]+" <--> "+bridgeChan[i][1]);   }   public void setBridgeChan(int idx,String iax2Chan, String sipChan)   {      bridgeChan[idx][0] = iax2Chan;      bridgeChan[idx][1] = sipChan;   }   public void clearBridgeChan(int idx)   {      bridgeChan[idx][0] = null;      bridgeChan[idx][1] = null;   }   public String getIax2Chan(int idx)   {      return bridgeChan[idx][0];   }   public String getSipChan(int idx)   {      return bridgeChan[idx][1];   }   public int getFreeBridgeIdx()   {      for(int i=0;i<2;i++)      {         if(bridgeChan[i][0] == null && bridgeChan[i][1] == null)            return i;      }      return -1;   }   public boolean transfer()   {      String chan1 = null, chan2=null;      if(bridgeChan[0][0] != null && bridgeChan[0][1] != null)         chan1 = bridgeChan[0][1];  //first sip chan      if(bridgeChan[1][0] != null && bridgeChan[1][1] != null)         chan2 = bridgeChan[1][1];  //second sip chan      if(chan1 == null || chan2 == null )      {         System.out.println("Can not bridge : only one call!");         return false;      }      BridgeAction bAct = new BridgeAction (chan1, chan2);        System.out.println("Bridge " + chan1 + " + " + chan2 );        try {            managerConn.sendAction(bAct);            return true;        } catch (IOException ex) {            ex.printStackTrace();        } catch (TimeoutException ex) {            ex.printStackTrace();        } catch (IllegalArgumentException ex) {            ex.printStackTrace();        } catch (IllegalStateException ex) {            ex.printStackTrace();        }        return false;   }   public void sentStatusAction(String chan)   {      if(chan == null)         return;      StatusAction stAct = new StatusAction();        System.out.println("sent ChanStatus action - " + chan );        stAct.setChannel(chan);        try {            managerConn.sendAction(stAct);        } catch (IOException ex) {            ex.printStackTrace();        } catch (TimeoutException ex) {            ex.printStackTrace();        } catch (IllegalArgumentException ex) {            ex.printStackTrace();        } catch (IllegalStateException ex) {            ex.printStackTrace();        }   }   public boolean redirectCall(String chan1, String chan2){        RedirectAction reAct;        //this.managerLogin();        System.out.println("redirectCall - [" + chan1 + "], [" + chan2 +"]");        if (chan2.length() > 0)            reAct = new RedirectAction(chan1, chan2, "from_iax", "*88111001000000", 1);        else            reAct = new RedirectAction(chan1, "from_iax", "*88111001000000", 1);        try {            managerConn.sendAction(reAct);            //this.managerConn.logoff();            return true;        } catch (IOException ex) {            ex.printStackTrace();        } catch (TimeoutException ex) {            ex.printStackTrace();        } catch (IllegalArgumentException ex) {            ex.printStackTrace();        } catch (IllegalStateException ex) {            ex.printStackTrace();        }        //this.managerConn.logoff();        return false;    }   class MyAstEvents implements ManagerEventListener {      public void onManagerEvent(ManagerEvent event) {         Object classObj = event.getClass();         if(classObj.equals(RtpSenderStatEvent.class) || classObj.equals(RtpReceiverStatEvent.class))            return;         if(classObj.equals(RtcpReceivedEvent.class) || classObj.equals(RtcpSentEvent.class))            return;         if(classObj.equals(JitterBufStatsEvent.class))            return;         if(classObj.equals(BridgeEvent.class)){            System.out.println("/n BridgeEvent detail - " + event.toString());            BridgeEvent evt = (BridgeEvent)event;            if(evt.getBridgeState().equals(BridgeEvent.BRIDGE_STATE_LINK)               /*&& evt.getBridgeType().equals(BridgeEvent.BRIDGE_TYPE_CORE )*/)            {               int idx =  getFreeBridgeIdx();               if(idx != -1)               {                  String ch1 = evt.getChannel1();                  String ch2 = evt.getChannel2();                  if(ch1.startsWith("IAX2/")){                     setBridgeChan(idx,ch1,ch2);                  }else if(ch1.startsWith("SIP/")){                     setBridgeChan(idx,ch2,ch1);                  }               }            }            dispalyBridgeChan();         }else if(classObj.equals(UnlinkEvent.class)){            System.out.println("/n UnlinkEvent detail - " + event.toString());            UnlinkEvent evt = (UnlinkEvent)event;            String iaxCh = null;            String sipCh = null;            System.out.println("Unlinke event: chan1: "+evt.getChannel1()+" chan2: "+evt.getChannel2());            if(evt.getChannel1().startsWith("IAX2/") && evt.getChannel2().startsWith("SIP/"))            {               iaxCh = evt.getChannel1();               sipCh = evt.getChannel2();            }else if(evt.getChannel1().startsWith("SIP/") && evt.getChannel2().startsWith("IAX/"))            {               iaxCh = evt.getChannel2();               sipCh = evt.getChannel1();            }            if(iaxCh == null || sipCh == null )               return;            for(int i=0;i<2;i++)            {               if(iaxCh.equals(bridgeChan[i][0])                  && sipCh.equals(bridgeChan[i][1]))               {                  clearBridgeChan(i);               }            }            dispalyBridgeChan();         }else if(classObj.equals(StatusEvent.class)){            System.out.println("/n StatusEvent detail - " + event.toString());         }         else            System.out.println("/n Event detail - " + event.toString());      }   }}
原创粉丝点击