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

无法从Zoiper连接到Google Cloud上的OpenSIPS服务器求助

问题:Zoiper无法连接Google Cloud上的OpenSIPS服务器

我在Google Cloud虚拟机实例上安装了OpenSIPS及其控制面板,可正常访问面板并创建alice、bob两个用户,但Zoiper客户端无法连接服务器,TCP和UDP连接均检测失败。以下是我的OpenSIPS配置文件:

#
# OpenSIPS residential configuration script
#     by OpenSIPS Solutions <team@opensips-solutions.com>
#
# This script was generated via "make menuconfig", from
#   the "Residential" scenario.
# You can enable / disable more features / functionalities by
#   re-generating the scenario with different options.#
#
# Please refer to the Core CookBook at:
#      https://opensips.org/Resources/DocsCookbooks
# for a explanation of possible statements, functions and parameters.
#


####### Global Parameters #########

/* uncomment the following lines to enable debugging */
debug_mode=yes

log_level=3
xlog_level=3
log_stderror=no
log_facility=LOG_LOCAL0

udp_workers=4

/* uncomment the next line to enable the auto temporary blacklisting of 
   not available destinations (default disabled) */
#disable_dns_blacklist=no

/* uncomment the next line to enable IPv6 lookup after IPv4 dns 
   lookup failures (default disabled) */
#dns_try_ipv6=yes


socket=udp:*
socket=udp:127.0.0.1:5060



####### Modules Section ########

#set module path
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"

#### SIGNALING module
loadmodule "signaling.so"

#### StateLess module
loadmodule "sl.so"

#### Transaction Module
loadmodule "tm.so"
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)

#### Record Route Module
loadmodule "rr.so"
/* do not append from tag to the RR (no need for this script) */
modparam("rr", "append_fromtag", 0)

#### MAX ForWarD module
loadmodule "maxfwd.so"

#### SIP MSG OPerationS module
loadmodule "sipmsgops.so"

#### FIFO Management Interface
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)

#### USeR LOCation module
loadmodule "usrloc.so"
modparam("usrloc", "nat_bflag", "NAT")
modparam("usrloc", "working_mode_preset", "single-instance-no-db")

#### REGISTRAR module
loadmodule "registrar.so"
modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)

#### ACCounting module
loadmodule "acc.so"
/* what special events should be accounted ? */
modparam("acc", "early_media", 0)
modparam("acc", "report_cancels", 0)
/* by default we do not adjust the direct of the sequential requests.
   if you enable this parameter, be sure to enable "append_fromtag"
   in "rr" module */
modparam("acc", "detect_direction", 0)

loadmodule "proto_udp.so"

####### Routing Logic ########

# main request routing logic

route{

    if (!mf_process_maxfwd_header(10)) {
        send_reply(483,"Too Many Hops");
        exit;
    }

    if (has_totag()) {

        # handle hop-by-hop ACK (no routing required)
        if ( is_method("ACK") && t_check_trans() ) {
            t_relay();
            exit;
        }

        # sequential request within a dialog should
        # take the path determined by record-routing
        if ( !loose_route() ) {
            # we do record-routing for all our traffic, so we should not
            # receive any sequential requests without Route hdr.
            send_reply(404,"Not here");
            exit;
        }

        if (is_method("BYE")) {
            # do accounting even if the transaction fails
            do_accounting("log","failed");
        }

        # route it out to whatever destination was set by loose_route()
        # in $du (destination URI).
        route(relay);
        exit;
    }

    # CANCEL processing
    if (is_method("CANCEL")) {
        if (t_check_trans())
            t_relay();
        exit;
    }

    # absorb retransmissions, but do not create transaction
    t_check_trans();

    if ( !(is_method("REGISTER")) ) {
        
        if (is_myself("$fd")) {
                    
        } else {
            # if caller is not local, then called number must be local
            
            if (!is_myself("$rd")) {
                send_reply(403,"Relay Forbidden");
                exit;
            }
        }

    }

    # preloaded route checking
    if (loose_route()) {
        xlog("L_ERR",
            "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
        if (!is_method("ACK"))
            send_reply(403,"Preload Route denied");
        exit;
    }

    # record routing
    if (!is_method("REGISTER|MESSAGE"))
        record_route();

    # account only INVITEs
    if (is_method("INVITE")) {
        
        do_accounting("log");
    }

    
    if (!is_myself("$rd")) {
        append_hf("P-hint: outbound
"); 
        
        route(relay);
    }

    # requests for my domain
    
    if (is_method("PUBLISH|SUBSCRIBE")) {
        send_reply(503, "Service Unavailable");
        exit;
    }

    if (is_method("REGISTER")) {
                
        # store the registration and generate a SIP reply
        if (!save("location"))
            xlog("failed to register AoR $tu
");

        exit;
    }

    if ($rU==NULL) {
        # request with no Username in RURI
        send_reply(484,"Address Incomplete");
        exit;
    }

    

    
      
    # do lookup with method filtering
    if (!lookup("location","m")) {
        
        
        t_reply(404, "Not Found");
        exit;
    } 

    

    # when routing via usrloc, log the missed calls also
    do_accounting("log","missed");
    route(relay);
}


route[relay] {
    # for INVITEs enable some additional helper routes
    if (is_method("INVITE")) {
        
        
        t_on_branch("per_branch_ops");
        t_on_reply("handle_nat");
        t_on_failure("missed_call");
    }

    

    if (!t_relay()) {
        send_reply(500,"Internal Error");
    }
    exit;
}



branch_route[per_branch_ops] {
    xlog("new branch at $ru
");
}


onreply_route[handle_nat] {
    
    xlog("incoming reply
");
}


failure_route[missed_call] {
    if (t_was_cancelled()) {
        exit;
    }

    # uncomment the following lines if you want to block client 
    # redirect based on 3xx replies.
    ##if (t_check_status("3[0-9][0-9]")) {
    ##t_reply(404,"Not found");
    ##  exit;
    ##}

    
}

排查与修复步骤

1. 修正OpenSIPS监听配置

当前配置监听了127.0.0.1:5060(仅本地可访问),且未明确开启TCP监听,导致外部客户端无法连接。修改全局参数中的socket配置:

# 替换原有socket行
socket=udp:0.0.0.0:5060
socket=tcp:0.0.0.0:5060

同时在模块加载部分添加TCP协议模块:

loadmodule "proto_tcp.so"

2. 配置Google Cloud防火墙规则

在Google Cloud控制台VPC网络-防火墙中添加规则:

  • 目标:选择你的虚拟机实例或实例组
  • 来源IP范围:测试阶段可设为0.0.0.0/0,生产环境建议限制特定IP段
  • 协议与端口:勾选udp:5060和tcp:5060

3. 检查虚拟机本地防火墙

验证GCE实例的本地防火墙(如ufw)是否放行5060端口:

sudo ufw allow 5060/udp
sudo ufw allow 5060/tcp
sudo ufw reload

4. 完善OpenSIPS配置以支持公网访问

  • 添加公网IP/域名到alias,确保is_myself能识别本地服务:
# 全局参数添加
alias="你的公网IP"
# 若有域名,添加:alias="你的SIP域名"
  • 加载NAT穿透模块,解决公网客户端NAT问题:
# 模块部分添加
loadmodule "nathelper.so"
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nat_only", 1)
  • 在注册逻辑中添加NAT检测:
    在is_method("REGISTER")代码块内,save("location")前添加:
if (nat_uac_test("19")) {
    setflag("NAT");
}

5. 验证连通性

本地用nc工具测试网络连通:

  • UDP测试:
nc -u 你的公网IP 5060

输入任意字符,若能收到OpenSIPS的错误响应(如400 Bad Request),说明UDP端口连通正常。

  • TCP测试:
nc 你的公网IP 5060

若能建立连接,说明TCP端口正常。

最后检查Zoiper配置:SIP服务器填公网IP,端口5060,传输协议选UDP或TCP,账号密码与OpenSIPS控制面板创建的一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:07:01