使用Ozeki库在C#中实现SIP呼叫触发状态异常问题排查
Ozeki库C#对接Asterisk SIP呼叫异常排查与优化方案
问题描述
使用Ozeki库在C#中实现SIP呼叫,对接Asterisk电话系统,已成功完成SIP账号注册(输出Registration succeeded - Online!),但呼叫状态进入Setup后直接变为Error,程序输出如下:
Registration succeeded - Online!
Call state: Setup.
Call state: Error.
错误排查
- 端口配置错误:代码中
domainPort设置为5038,这是Asterisk的AMI(管理接口)端口,而非SIP服务默认端口(默认是5060),这是导致呼叫失败的核心原因。 - 错误信息未捕获:当前代码未处理
CallState.Error状态的具体异常信息,无法定位具体错误原因。 - 拨号号码格式问题:拨号号码
00994122000020可能不符合Asterisk的拨号规则,比如需要添加出局前缀(如9)或号码格式不被路由规则匹配。
代码修改方案
核心修改点
- 修正SIP端口为Asterisk默认的5060
- 添加Error状态的异常信息捕获
- 可选:调整拨号号码格式适配Asterisk路由规则
修改后的完整代码
class Program { static ISoftPhone softphone; static IPhoneLine phoneLine; static IPhoneCall call; static Microphone microphone; static Speaker speaker; static MediaConnector connector; static PhoneCallAudioSender mediaSender; static PhoneCallAudioReceiver mediaReceiver; public static void Main(string[] args) { // 创建软电话对象,指定RTP端口范围 softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000); // SIP账号注册信息 var registrationRequired = true; var userName = "111"; var displayName = "111"; var authenticationId = "111"; var registerPassword = "pin111"; var domainHost = "localhost"; var domainPort = 5060; // 修正为Asterisk SIP默认端口 var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort); RegisterAccount(account); microphone = Microphone.GetDefaultDevice(); speaker = Speaker.GetDefaultDevice(); mediaSender = new PhoneCallAudioSender(); mediaReceiver = new PhoneCallAudioReceiver(); connector = new MediaConnector(); Console.WriteLine("\nDone"); Console.ReadLine(); } static void RegisterAccount(SIPAccount account) { try { phoneLine = softphone.CreatePhoneLine(account); phoneLine.RegistrationStateChanged += line_RegStateChanged; softphone.RegisterPhoneLine(phoneLine); } catch (Exception ex) { Console.WriteLine("SIP注册错误: " + ex.Message); } } static void line_RegStateChanged(object sender, RegistrationStateChangedArgs e) { if (e.State == RegState.NotRegistered || e.State == RegState.Error) Console.WriteLine("注册失败!"); if (e.State == RegState.RegistrationSucceeded) { Console.WriteLine("注册成功 - 在线!"); CreateCall(); } } private static void CreateCall() { // 根据Asterisk拨号规则调整号码,比如添加出局前缀9 var numberToDial = "900994122000020"; call = softphone.CreateCallObject(phoneLine, numberToDial); call.CallStateChanged += call_CallStateChanged; call.Start(); } private static void SetupDevices() { connector.Connect(microphone, mediaSender); connector.Connect(mediaReceiver, speaker); mediaSender.AttachToCall(call); mediaReceiver.AttachToCall(call); microphone.Start(); speaker.Start(); } static void call_CallStateChanged(object sender, CallStateChangedArgs e) { Console.WriteLine("呼叫状态: {0}.", e.State); // 捕获Error状态的具体异常信息 if (e.State == CallState.Error) { Console.WriteLine("呼叫错误详情: " + e.Error.Message); } if (e.State == CallState.Answered) SetupDevices(); } }
额外验证步骤
- 确认Asterisk中已正确配置SIP账号
111,允许呼出权限 - 检查Asterisk的拨号规则(extensions.conf)是否包含目标号码的路由
- 确保防火墙允许5060(SIP)及5000-10000(RTP)端口的UDP流量
适用于Asterisk的SIP呼叫库推荐
- SIP.NET:轻量级开源库,原生支持SIP协议,与Asterisk兼容性出色,适合基础到中级VoIP开发
- PJSIP.NET:基于成熟的PJSIP库封装,功能全面,支持复杂呼叫场景(如视频呼叫、加密通话)
- Twilio Voice SDK:适合云呼叫集成场景,无需维护本地Asterisk,快速实现呼叫功能
内容的提问来源于stack exchange,提问作者Farid Huseynli
相关产品推荐
相关产品推荐

