You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SipSorcery注册与呼叫端口不一致致403问题及端口配置咨询

问题描述

我在Windows电脑上用SipSorcery开发小型WPF应用,基于.NET 6和最新版SipSorcery包。用户代理注册成功无报错,但发起呼叫时收到403 Forbidden响应。发现注册和Invite请求使用不同端口,怀疑是问题根源,尝试为SipTransport设置同一端口但未成功,SipSorcery似乎每次都会分配不同端口。

注册请求截图

注册请求截图

Invite请求截图

Invite请求截图

呼叫代码

public async Task MakeCall()
{
    // ...
    var sipTransport = new SIPTransport();
    sipTransport.PreferIPv6NameResolution = preferIPv6;
    //sipTransport.AddSIPChannel(new List<SIPChannel> { new SIPTCPChannel(IPAddress.Any, 47063) });
    sipTransport.CreateChannel(SIPProtocolsEnum.udp,AddressFamily.Unknown,47063);

    sipTransport.EnableTraceLogs();

    SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI);

    var audioSession = new WindowsAudioEndPoint(new AudioEncoder());
    audioSession.RestrictFormats(x => x.Codec == AudioCodecsEnum.PCMA || x.Codec == AudioCodecsEnum.PCMU);
    var rtpSession = new VoIPMediaSession(audioSession.ToMediaEndPoints());

    var offerSDP = rtpSession.CreateOffer(preferIPv6 ? IPAddress.IPv6Any : IPAddress.Any);

    var uac = new SIPClientUserAgent(sipTransport);

   // Codes for trying and failing ... etc 
   
    SIPCallDescriptor callDescriptor = new SIPCallDescriptor(
                   username: DEFAULT_USERNAME,
                   password: DEFAULT_PASSWORD,
                   uri: callUri.ToString(),
                   from: $"sip:{DEFAULT_USERNAME}@{DEFAULT_SERVER}",
                   to: DEFAULT_DESTINATION_SIP_URI,
                   routeSet: null,
                   customHeaders: new List<string> {
                       "Allow:INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY,INFO,PUBLISH,MESSAGE"
                        ,"User-Agent:SIP.js/0.14.6"
                   },
                   authUsername: null,
                   callDirection: SIPCallDirection.Out,
                   contentType: SDP.SDP_MIME_CONTENTTYPE,
                   content: offerSDP.ToString(),
                   mangleIPAddress: null);

    uac.Call(callDescriptor, null);

  // ...

}

注册代码

public void RegisterSipUser()
{
   
    SIPTransport.PreferIPv6NameResolution = true;
    SIPTransport.EnableTraceLogs();

    Log = AddFileLogger(LogEventLevel.Verbose);
    SIPURI sIPURI = new SIPURI(DEFAULT_USERNAME, HOST, null, SIPSchemesEnum.sip, SIPProtocolsEnum.udp);
    SIPEndPoint sIPEndPoint = new SIPEndPoint(sIPURI);

    _userAgent = new SIPRegistrationUserAgent(SIPTransport, sIPEndPoint, sIPURI, DEFAULT_USERNAME, DEFAULT_PASSWORD, "xxx.net", HOST, sIPURI, DEFAULT_EXPIRY,null);

    _userAgent.UserAgent = "SIP.js/0.14.6";
    // Event handlers for the different stages of the registration.
    _userAgent.RegistrationFailed += (uri, err) => Log.LogError($"{uri}: {err}");
    _userAgent.RegistrationTemporaryFailure += (uri, msg) => Log.LogError($"{uri}: {msg}");
    _userAgent.RegistrationRemoved += (uri) => Log.LogWarning($"{uri} registration failed.");
    _userAgent.RegistrationSuccessful += (uri) => Log.LogInformation($"{uri} registration succeeded.");

    _userAgent.Start();

}

疑问

  1. 注册与呼叫使用不同端口是否会引发问题?
  2. 如何为这两个操作设置同一端口?

解答

1. 注册与呼叫使用不同端口是否会引发问题?

是的,这大概率是403 Forbidden的诱因。很多SIP服务器会把"注册请求源端口与后续呼叫请求源端口一致"作为安全校验条件——端口不一致时,服务器会判定呼叫请求并非来自已注册的合法客户端,直接拒绝请求返回403。

2. 如何为这两个操作设置同一端口?

你的代码核心问题是注册和呼叫各自创建了独立的SIPTransport实例,这必然导致系统分配不同端口。解决方法是复用全局唯一的SIPTransport实例:

步骤1:创建全局共享的SIPTransport

在应用初始化阶段创建单例SIPTransport并指定固定端口:

// 全局单例,应用启动时初始化一次
private static SIPTransport _globalSipTransport;

public void InitializeSipTransport(bool preferIPv6)
{
    _globalSipTransport = new SIPTransport();
    _globalSipTransport.PreferIPv6NameResolution = preferIPv6;
    // 明确指定UDP端口47063,确保注册和呼叫共用该端口
    _globalSipTransport.CreateChannel(SIPProtocolsEnum.udp, AddressFamily.Unknown, 47063);
    _globalSipTransport.EnableTraceLogs();
}

步骤2:注册和呼叫复用该实例

  • 注册代码修改:
public void RegisterSipUser()
{
    // 使用全局实例,不再操作静态属性
    _globalSipTransport.EnableTraceLogs();

    Log = AddFileLogger(LogEventLevel.Verbose);
    SIPURI sIPURI = new SIPURI(DEFAULT_USERNAME, HOST, null, SIPSchemesEnum.sip, SIPProtocolsEnum.udp);
    SIPEndPoint sIPEndPoint = new SIPEndPoint(sIPURI);

    // 传入全局SIPTransport实例
    _userAgent = new SIPRegistrationUserAgent(_globalSipTransport, sIPEndPoint, sIPURI, DEFAULT_USERNAME, DEFAULT_PASSWORD, "xxx.net", HOST, sIPURI, DEFAULT_EXPIRY,null);

    _userAgent.UserAgent = "SIP.js/0.14.6";
    // 事件处理代码保持不变
    _userAgent.Start();
}
  • 呼叫代码修改:
public async Task MakeCall()
{
    // 直接复用全局实例,无需重新创建
    var sipTransport = _globalSipTransport;

    SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI);

    var audioSession = new WindowsAudioEndPoint(new AudioEncoder());
    audioSession.RestrictFormats(x => x.Codec == AudioCodecsEnum.PCMA || x.Codec == AudioCodecsEnum.PCMU);
    var rtpSession = new VoIPMediaSession(audioSession.ToMediaEndPoints());

    var offerSDP = rtpSession.CreateOffer(preferIPv6 ? IPAddress.IPv6Any : IPAddress.Any);

    var uac = new SIPClientUserAgent(sipTransport);

    // 呼叫描述符代码保持不变
    uac.Call(callDescriptor, null);
}

额外注意事项

  • 确保全局SIPTransport只初始化一次,避免重复创建导致端口冲突。
  • 如果设置固定端口后仍有问题,检查防火墙是否允许该端口的UDP流量,以及SIP服务器是否支持自定义端口的注册/呼叫请求。

内容的提问来源于stack exchange,提问作者nzrytmn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 07:09:12