Asterisk Summary - 5

         看了前面小节的内容后,如果你现在正在做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());
      }
   }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值