从Java应用程序与Asterisk服务器的交互

通过Asterisk管理界面(AMI)从Java应用程序与Asterisk服务器进行交互


如果您只是刚刚开始在这方面进行研究,那么与该服务器的交互在您看来可能会有些混乱,就像我曾经觉得的那样。

为了不以回答问题的方式在论坛上寻找必要的信息,我附上了一个小教程,内容涉及从Java与Asterisk服务器进行交互。

重要提示:我假设一旦您达到编写代码的阶段,那么您已经拥有可以使用的工作Asterisk服务器。

1)选择什么使工作更方便?

绝对-星号管理界面(AMI):此界面具有全方位的功能,可让您拨打电话,从服务器实时监听事件,接收呼叫状态并在必要时中断它。

2)要连接什么库?

这个:

<dependency> <groupId>org.asteriskjava</groupId> <artifactId>asterisk-java</artifactId> <version>2.0.4</version> </dependency> 

3)需要在服务器上查看哪些配置?

extensions.conf-描述diaplan的配置。 您将不断与他联系。 如果使用一种更易理解的语言,则它将包含服务器收到特定号码的呼叫时将执行的操作的脚本。 首先,在diaplan中搜索特定的上下文-将其写在方括号中,然后在要联系的该上下文的标签下搜索一个数字。

manager.conf-使用您的Asterisk服务器的用户名和密码进行配置

此配置的内容应大致如下:

 [user_name] secret = password read = all write = all deny=0.0.0.0/0.0.0.0 permit=0.0.0.0/255.255.255.0 

  • user_name-用户名
  • 秘密-密码
  • deny-该用户拒绝访问的IP地址
  • 允许-允许访问。 请务必在许可中指定要与之联系的IP,因为翠菊可以拒绝您的请求。

sip.conf-所有中继都在这里注册。 中继线是电话,我们将通过该电话呼叫客户。

4)从哪里开始编写代码?

有两个选项:您需要在Asterisk服务器上执行某些操作,或者侦听服务器上的事件。 我们的序列包括两者。

我们描述行动计划:

  1. 打开与服务器的连接;
  2. 我们描述了工作场景;
  3. 我们听事件;
  4. 关闭连接。

因此,将在创建DefaultAsteriskServer对象的过程中初始化连接:

 import org.asteriskjava.live.AsteriskServer; import org.asteriskjava.live.DefaultAsteriskServer; 

 AsteriskServer asteriskServer = new DefaultAsteriskServer(HOSTNAME, USERNAME, PASSWORD); asteriskServer.initialize(); 

打开连接后,我们需要致电用户。 我们将其称为行动方案。 该场景的描述将在一个单独的类中:

 /** *    */ public class ScenarioCall extends OriginateAction { private final Logger log = LoggerFactory.getLogger(ScenarioCall.class); private String TRUNK; private final String PHONE_FOR_RINGING; private final String EXTEN_FOR_APP; private final String CONTEXT_FOR_APP; public ScenarioCall(String trunk, String phoneForRinging, String extension, String context) { this.TRUNK = trunk; this.PHONE_FOR_RINGING = phoneForRinging; this.EXTEN_FOR_APP = extension; this.CONTEXT_FOR_APP = context; this.init(); } /** *         OriginateAction */ private void init() { //  String callId = ValidValues.getValidCallId(this.PHONE_FOR_RINGING); //    String channelAsterisk = ValidValues.getValidChannel(this.TRUNK, this.PHONE_FOR_RINGING); this.setContext(CONTEXT_FOR_APP); this.setExten(EXTEN_FOR_APP); this.setPriority(1); this.setAsync(true); this.setCallerId(callId); this.setChannel(channelAsterisk); log.info("Create Scenario Call: phone '{}',chanel '{}',context '{}',extension '{}'", callId, channelAsterisk, CONTEXT_FOR_APP, EXTEN_FOR_APP); } } 

在这种情况下,我们需要了解什么? 首先,创建中继连接。 中继线是您用来呼叫订户的号码。 此后,将在中继线和订户之间创建一个连接,然后在订户和您需要的其他人之间建立连接。

就是按照这个顺序。

 this.setContext(CONTEXT_FOR_APP) 
传输的值:在上下文中,我们将在其中查找要与订户关联的电话号码(来自extensions.conf)。

 this.setExten(EXTEN_FOR_APP) 
传输的值:联系订户后(从extensions.conf中)将执行的脚本。

 this.setCallerId(callId) 
传输的值:我们的订户号码

 this.setChannel(channelAsterisk) 
发送值:已建立的通信通道,通常如下所示:trunk_name / phone_user。

在哪里寻找trunk_name? 星号服务器上有一个sip.conf配置-所有干线都在那里注册。

建立通话:

 if (asteriskServer .getManagerConnection().getState().equals(ManagerConnectionState.CONNECTED) || asteriskServer .getManagerConnection().getState().equals(ManagerConnectionState.CONNECTING) || asteriskServer .getManagerConnection().getState().equals(ManagerConnectionState.INITIAL)) { try { ScenarioCall scenarioCall = new ScenarioCall(trank, phone, extension, context); CallBack callBackForScenarioCall = new CallBack(); asteriskServer.originateAsync(scenarioCall, callBackForScenarioCall); } catch (ManagerCommunicationException e) { //   , StateConnection    RECONNECTING,     } } 

我们创建了一个呼叫,但是如何动态跟踪呢?

为此,要完成两件事:在OriginAsync方法中传递CallBack类的实例。
并且侦听器挂在服务器上,它将合并发生在我们身上的所有内容。

需要一个侦听器,因为当用户已经通话时,CallBack类不会通知您呼叫结束,也不会通知您用户仍然可以转移到其他地方。

 /** *        asteriskConnection.originateAsync  *  CallBack -     ,     *  originateAsync. CallBack      , *       ,    onNoAnswer ,  *     onBusy,     ,  onFailure,  . *        . ,      *      (      ,    ) */ public class CallBack implements OriginateCallback { /** *     PRERING,       * OriginateCallback -    */ private ChannelState resultCall = ChannelState.PRERING; /** *    ,   .         */ @Override public void onDialing(AsteriskChannel asteriskChannel) { //   ,  resultCall, //   asteriskChannel    null, //    resultCall   //   } /** *   .      *      6 - setStatus */ @Override public void onSuccess(AsteriskChannel asteriskChannel) { //   , asteriskChannel   null, // asteriskChannel.getState()      ChannelState.UP //   } /** *      ,    *      7 - setStatus () */ @Override public void onNoAnswer(AsteriskChannel asteriskChannel) { //   , //   asteriskChannel    null, //    resultCall   //   } /** *   *      7 - setStatus () */ @Override public void onBusy(AsteriskChannel asteriskChannel) { //   , //   asteriskChannel    null, //    resultCall   //   } /** *      */ @Override public void onFailure(LiveException e) { //     , //     , // onFailure      } } 

如何将听众挂在星号上?

为此,请创建一个实现类AsteriskServerListener,PropertyChangeListener。
对于创建的连接,通过AsteriskConnection类的实例,我们实现:

  this.asteriskConnection.addAsteriskServerListener(this.callBackEventListener); 

this.callBackEventListener-我们的侦听器类的实例,其源于:

 ** *    Asterisk *  PropertyChangeListener   ,     . *  AsteriskServerListener   ,     AsteriskConnection. */ public class CallBackEventListener implements AsteriskServerListener, PropertyChangeListener { public void onNewAsteriskChannel(AsteriskChannel channel) { channel.addPropertyChangeListener(this); } public void onNewMeetMeUser(MeetMeUser user) { user.addPropertyChangeListener(this); } public void onNewQueueEntry(AsteriskQueueEntry user) { user.addPropertyChangeListener(this); } public void onNewAgent(AsteriskAgent asteriskAgent) { asteriskAgent.addPropertyChangeListener(this); } /** *    .   {@link PropertyChangeEvent} *    , *        , *        CallBack * * @param propertyChangeEvent      */ public void propertyChange(PropertyChangeEvent propertyChangeEvent) { findEventEndCall(propertyChangeEvent); } private void findEventEndCall(PropertyChangeEvent event) { if (event.getSource() instanceof AsteriskChannel) { AsteriskChannel callBackChannel = (AsteriskChannel) event.getSource(); String callId = getStringWithOnlyDigits(callBackChannel.getCallerId().toString()); callId = ValidValues.getValidCallId(callId); if (callBackChannel.getState().toString().equals("HUNGUP") && event.getOldValue().toString().contains("RINGING")) { //      callBackChannel.removePropertyChangeListener(this); //     } else if (callBackChannel.getState().toString().equals("HUNGUP") && event.getOldValue().toString().contains("UP")) { //     callBackChannel.removePropertyChangeListener(this); //     } else if (callBackChannel.getState().toString().equals("HUNGUP")) { //      callBackChannel.removePropertyChangeListener(this); //     } } } private String getStringWithOnlyDigits(String strForParse) { String result = ""; if (strForParse != null && !strForParse.isEmpty()) { CharMatcher ASCII_DIGITS = CharMatcher.anyOf("<>").precomputed(); result = ASCII_DIGITS.removeFrom(strForParse.replaceAll("[^0-9?!]", "")); } return result; } } 

我建议您在一开始时就承诺,关于propertyChange的内容并查看PropertyChangeEvent,这将是服务器上发生的所有事情的堆。 它根本不过滤信息。 因此,得出的结论是:尽可能少地挂起听众。 并非针对每个调用,因为据我发现,即使在OriginateCallback类中也可以完成。 这没用。 查看出现哪些PropertyChangeEvent对象,查看那里的字段类型以及所需的字段。 下一步-欢迎来到信息处理领域。

关于数据验证的一些知识。

在OriginateAction.setChannel中-传递trunk_name / phone_user
phone_user-如果是俄语,则国际数字为加号,则应以8开头。

在OriginateAction.setCallerId中-传输客户端的电话号码,
然后在CallBackEventListener中,它将出现在callBackChannel.getCallerId()中。

将像这样:

 String callId = getStringWithOnlyDigits(callBackChannel.getCallerId().toString()); 

最后,不要忘记:

 asteriskServer.shutdown(); 

如果需要中断任何呼叫,则在CallBackEventListener类中
在现有的通讯渠道上,我们执行:

 callBackChannel.hangup(); 

原来如此简单的教程。 乍一看,当然很简单,但是请相信我,查找信息,推迟使用所有方法并停止工作需要大量时间和精力。

祝您使用Asterisk服务器好运!

进一步阅读:

1) Asterisk-Java教程

2) 星号管理界面(AMI)

Source: https://habr.com/ru/post/zh-CN469491/


All Articles