如何使用AsterNET.ARI实现Asterisk内部号码间的简单呼叫?
AsterNET.ARI 实现内部号码401呼叫402失败问题排查与解决
问题场景
需通过AsterNET.ARI实现绑定软电话的内部号码401呼叫402,当前通过拨打1000触发Stasis应用,但调用Channels.Originate后呼叫未发起,且缺少官方示例参考。
当前使用的extensions.conf配置:
exten => 1000,1, NoOp() same => n,Stasis(originate-example) same => n,Hangup()
原C#代码存在参数和逻辑错误,导致呼叫失败。
问题原因分析
- 参数缺失:原
Originate调用未指定context参数,Asterisk无法确定在哪个拨号计划上下文查找目标号码402的路由 - 逻辑错误:直接指定
endpoint: "PJSIP/401"和extension: "402"不符合ARI呼叫逻辑——ARI的Originate是创建新通道呼叫指定终端,而非让指定终端主动发起呼叫 - 事件覆盖不全:仅订阅
StasisStartEvent,未处理通道接听、桥接创建等关键环节,无法完成完整呼叫流程
解决方案
1. 调整extensions.conf配置
补充内部号码路由,确保Asterisk能正确解析4XX号码:
[internal] ; 内部号码路由:匹配4XX号码,直接拨打对应PJSIP终端 exten => _4XX,1,Dial(PJSIP/${EXTEN}) ; 触发Stasis应用的入口号码 exten => 1000,1,NoOp() same => n,Stasis(originate-example) same => n,Hangup()
2. 修正C#代码
实现完整呼叫流程:先呼叫401,待其接听后呼叫402,最后通过桥接关联两个通道
using AsterNET.ARI; using AsterNET.ARI.Helpers; using AsterNET.ARI.Models; using System; var AppName = "originate-example"; var actionClient = new AriClient(new StasisEndpoint("127.0.0.1", 8088, "asterisk", "asterisk"), AppName); // 订阅关键事件 actionClient.OnStasisStartEvent += OnStasisStart; actionClient.OnChannelAnswerEvent += OnChannelAnswer; actionClient.OnStasisEndEvent += OnStasisEnd; actionClient.Connect(); Console.WriteLine("Stasis应用已启动,按回车退出"); Console.ReadLine(); actionClient.Disconnect(); // 处理Stasis启动事件 void OnStasisStart(IAriClient sender, StasisStartEvent e) { // 初始触发通道(拨打1000的通道):接听并发起对401的呼叫 if (e.Channel.Dialplan.Extension == "1000") { sender.Channels.Answer(e.Channel.Id); SyncHelper.Wait(sender.Channels.Play(e.Channel.Id, "sound:vm-dialout", "en", 0, 0, Guid.NewGuid().ToString()), sender); // 发起对401的呼叫,接听后进入Stasis应用 var originateResponse = SyncHelper.Wait(sender.Channels.Originate( endpoint: "PJSIP/401", callerId: "自动拨号", app: AppName, timeout: 30000 ), sender); Console.WriteLine($"已发起对401的呼叫,通道ID: {originateResponse.Id}"); } // 401的通道进入Stasis:播放提示音 else if (e.Channel.Name.Contains("401")) { SyncHelper.Wait(sender.Channels.Play(e.Channel.Id, "sound:calling", "en", 0, 0, Guid.NewGuid().ToString()), sender); } } // 处理通道接听事件 void OnChannelAnswer(IAriClient sender, ChannelAnswerEvent e) { // 401接听后,发起对402的呼叫并创建桥接 if (e.Channel.Name.Contains("401")) { var originate402Response = SyncHelper.Wait(sender.Channels.Originate( endpoint: "PJSIP/402", callerId: "401", app: AppName, timeout: 30000 ), sender); // 创建混音桥接 var bridge = SyncHelper.Wait(sender.Bridges.Create("mixing", Guid.NewGuid().ToString()), sender); // 将两个通道加入桥接 SyncHelper.Wait(sender.Bridges.AddChannel(bridge.Id, new[] { e.Channel.Id }), sender); SyncHelper.Wait(sender.Bridges.AddChannel(bridge.Id, new[] { originate402Response.Id }), sender); Console.WriteLine("桥接创建成功,401与402已建立通话"); } } // 处理Stasis结束事件:清理桥接资源 void OnStasisEnd(IAriClient sender, StasisEndEvent e) { var bridges = SyncHelper.Wait(sender.Bridges.List(), sender); foreach (var b in bridges) { if (b.Channels.Contains(e.Channel.Id)) { SyncHelper.Wait(sender.Bridges.Destroy(b.Id), sender); Console.WriteLine("桥接已销毁"); break; } } }
关键注意事项
- 确保401、402的PJSIP终端已正确配置并在线
- Asterisk需启用ARI模块,
ari.conf中已配置合法的访问用户与权限 - 调整
timeout参数为合理值(如30000毫秒),避免因超时导致呼叫失败
内容的提问来源于stack exchange,提问作者Александр Инженер
相关产品推荐
相关产品推荐

