FreeSWITCH Java ESL Client Demo

1简介

在开启Java ESL Client编程之前,请先阅读《FreeSWITCH权威指南》学习什么是FreeSWITCH Event Socket。

Java连接FreeSWITCH的ESL可以采用两种模式:inbound和outbound。

  1. Inbound模式:Java应用作为客户端主动连接到FreeSWITCH的内置TCP服务器上,默认监听8021端口。连接成功后,Java应用可以订阅FreeSWITCH的各种事件通知,并对特定事件进行处理,也可以向FreeSWITCH发送命令。例如,通过Java应用可以获取来电事件,并且可以控制呼叫流程,如发送异步API命令发起呼叫 。

  2. Outbound模式:Java应用作为服务器端,FreeSWITCH作为客户端连接到Java应用。这种模式下,需要在FreeSWITCH配置中指定Java应用的IP和端口。当有来电时,FreeSWITCH会连接到Java应用,Java应用可以根据业务需求对通话进行处理,如播放音乐或转接呼叫 。

选择哪种模式取决于具体的业务需求。如果需要监控所有来电情况或实现自助语音服务,inbound模式可能更合适。而如果需要对来电进行人工客服分配,outbound模式可能更简单 。另外,Java ESL客户端库支持这两种模式,并且提供了丰富的API来处理ESL连接和事件 。

在实际开发中,开发者可以根据需要选择适合的连接模式,并利用Java ESL客户端提供的接口来实现与FreeSWITCH的交互。

Java ESL客户端通过与FreeSWITCH的Event Socket Library(ESL)交互,能够实现以下操作:

  1. 连接管理:建立与FreeSWITCH的连接,并处理连接的生命周期,包括断开和重连。

  2. 事件订阅:订阅FreeSWITCH产生的各种事件,如呼叫开始、结束、通道变更等。

  3. 事件处理:接收并处理来自FreeSWITCH的事件通知,允许应用根据事件执行业务逻辑。

  4. 发送API命令:向FreeSWITCH发送同步或异步API命令来控制呼叫流程,例如发起呼叫、挂断、转接等。

  5. 执行异步命令:发送异步命令并接收结果,如查询呼叫状态、执行数据库操作等。

  6. 同步命令执行:发送同步命令并等待其执行结果,适用于需要即时反馈的操作。

  7. 设置呼叫变量:在呼叫过程中设置或修改呼叫变量,以控制FreeSWITCH的行为。

  8. 用户和设备管理:通过API命令管理SIP用户、分机、IVR流程等。

  9. 呼叫录音:控制呼叫录音的开始和停止。

  10. 呼叫监听和监视:监听特定呼叫的音频流或监视呼叫状态。

  11. 发送DTMF:向正在通话的通道发送DTMF信号。

  12. 应用执行:在呼叫中执行特定的FreeSWITCH应用,如播放音乐、发送短信等。

  13. 错误处理:处理与FreeSWITCH连接或命令执行过程中可能出现的错误。

  14. 日志记录:记录与FreeSWITCH交互的日志信息,方便问题排查。

  15. 自定义协议支持:实现自定义的ESL协议命令,以支持FreeSWITCH的高级特性。

  16. 多线程和并发处理:支持多线程环境下的并发调用和事件处理。

  17. 资源管理:管理与FreeSWITCH的连接资源,优化连接使用效率。

Java ESL客户端作为一个强大的工具,允许开发者通过编程方式与FreeSWITCH进行交互,实现复杂的呼叫处理和通信应用(fs_cli)。

2实现

2.1maven依赖

<dependency>
<groupId>org.freeswitch.esl.client</groupId>
<artifactId>org.freeswitch.esl.client</artifactId>
<version>0.9.2</version>
</dependency>

2.2代码

package org.example;

import org.freeswitch.esl.client.IEslEventListener;
import org.freeswitch.esl.client.inbound.Client;
import org.freeswitch.esl.client.transport.event.EslEvent;

/**
 * @author yrz
 * @create 2024/08/20
 */
public class ESLClient {
    private static final String HOST = "10.192.33.34";
    private static final int PORT = 8021;
    private static final String PASSWORD = "ClueCon";
    private static String job_UUID = null;

    public static void main(String[] args) {
        InBound();
    }

    private static void InBound() {
        final Client client = new Client();
        try {
            client.connect(HOST, PORT, PASSWORD, 20);
            System.out.println("连接成功");
        } catch (Exception e) {
            System.out.println("连接失败");
        }

        client.addEventListener(new IEslEventListener() {
            @Override
            public void eventReceived(EslEvent event) {
                if (event.getEventName().equals("CHANNEL_ANSWER")) {
                    System.out.println("通道应答");
                } else if (event.getEventName().equals("HEARTBEAT")) {
                    System.out.println("收到心跳 --> " + event.getEventBodyLines());
                    job_UUID = client.sendAsyncApiCommand("status", null);
                    System.out.println("Job_UUID --> " + job_UUID);
                } else if (event.getEventName().equals("CHANNEL_DESTROY")) {
                    System.out.println("通道销毁");
                } else if (event.getEventName().equals("CHANNEL_HANGUP_COMPLETE")) {
                    //挂断
                    System.out.println("通道挂断完成");
                } else if (event.getEventName().equals("CHANNEL_CREATE")) {
                    System.out.println("通道创建");
                }
            }
            @Override
            public void backgroundJobResultReceived(EslEvent event) {
                String uuid = event.getEventHeaders().get("Job-UUID");
                if (job_UUID.equals(uuid)) {
                    for (String s : event.getEventBodyLines()) {
                        System.out.println(s);
                    }
                }
            }
        });
        client.setEventSubscriptions("plain", "all");
    }
}
/*
播打电话
String originate = client.sendAsyncApiCommand("originate", CallEnum.CALL_BRIDGE);
类型
//相当于1001给1002打电话   {origination_caller_id_number=1001}可以设置1001方显示名字(默认是0000000)
public static final String CALL_BRIDGE = "{origination_caller_id_number=1002}user/1001 &bridge(user/1002)";
// 给1001打电话,channel启动echo app(类似汤姆猫)   并修改名字和号码
public static final String CALL_CHANNEL_CHANGE_NAME = "{origination_caller_id_name='Zhang San',origination_caller_id_number=1234}user/1001 &echo";
//等价于 user/1001 &echo
public static final String CALL_DIALPLAN_XML = "user/1001 echo inline";
// 给1001通话,channel启动playback app(播放录音)
public static final String CALL_PLAYBACK = "{origination_caller_id_name='Zhang San',origination_caller_id_number=1234}user/1001 &playback(/播放文件路径)";
// 顺振 第一个呼叫失败,呼叫第二个,以此类推
public static final String CALL_PARAMAGNETIC_RESONANCE = "user/1001|user/1002 &echo";
// 同振 同时呼叫   谁先接听谁通话,另一个挂断
public static final String CALL_WITH_VIBRATION = "user/1001,user/1002 &echo";
// 服务器先给1001打,1001接听后进入dialplan,找到1002这个exten,最后执行&echo
public static final String CALL_EXTEN = "user/1001 1002";
// 在1001接听后进入public dialplan查找路由
public static final String CALL_DIALPLAN_CONTEXT = "user/1001 1002 XML public";
// 在呼叫时,在SDP里面向对方提供G729 PCMU编码,但在执行是会缺少PCMU编码   需要转义字符\或者^^:   {absolute_codec_string=G729\,PCMU}user/1001 &echo 或者下面的
public static final String CALL_ESCAPE_CODEC = "{absolute_codec_string=G729^^:PCMU}user/1001 &echo";
*/

3问题处理

3.1mod_event_socket.c:2674 IP ::ffff Rejected by acl “loopback.auto”

event socket默认监听地址为127.0.0.1。

在event_socket.conf.xml配置文件中,取消该行注释:

<param name="apply-inbound-acl" value="loopback.auto"/>

在acl.conf.xml中增加:

<list name="loopback.auto" default="allow">  
	<node type="allow" cidr="0.0.0.0"/>  
</list>
/***********************************************/
<list name="loopback.auto" default="allow">  
    <node type="allow" cidr="0.0.0.0/0"/>  
</list>

reloadxml

reloadacl

4参考文档

https://developer.signalwire.com/freeswitch/FreeSWITCH-Explained/Client-and-Developer-Interfaces/Java-ESL/Java-ESL-Client_7144076/

https://github.com/esl-client/esl-client

https://sunxiaodou.com/2017/08/02/java-esl-freeswitch/

https://blog.csdn.net/qq_40170041/article/details/127015890

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倔强的初学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值