1,基本信息
1,项目背景
现在有一个指挥调度系统,需要实现
1.用户A点击拨号按钮,给用户B打电话,然后也有可能用户A同时呼叫B和C
2.呼叫一波人,发送语音通知 之前项目采用浙江电信的翼信系统,现在翼信服务停了,阿里,等平台没找到平替的方案,需要根据业务所在城市选择对应地区的号码,最好对方显示的是固话号码,如果客户在北京,来电就显示北京的固话号码。
2,需求梳理
1,需要支持一对一及一对多呼叫
2,运营商不会为了小规模业务专门立项(即无法依赖传统的运营商级“多方通话”业务) 最佳实现方案是采用基于 IP 的软交换技术(VoIP)结合应用服务器逻辑。
简单来说,就是利用软件(如 FreeSWITCH、Asterisk)在服务器端模拟“调度台”的行为,而不是依赖运营商的电话网络功能。
在指挥调度这种关键场景下,“稳定压倒一切”。虽然现在的 VoIP 技术已经很成熟,但在很多核心业务场景中,传统的电话线路(PSTN)确实代表着更高的可靠性和心理安全感。
既然运营商不会为你单独“立项”(即不会专门拉一根昂贵的数字中继线 E1/PRI 给你),你完全可以通过“模拟线路接入”的方式来实现。这在中小企业和专网通信中是非常成熟的方案。 以下是具体的实现路径,核心设备叫做语音网关(VoIP Gateway)。
核心思路:用“语音网关”做桥梁 你需要购买一台语音网关设备(国内常见的品牌如鼎信通达、朗视、迅时等,性价比高且稳定)。 你的系统(软交换/调度台) <---> 语音网关 <---> 运营商电话线(PSTN) <---> 用户 B/C 的电话 业务架构:
从运营商办理多条普通电话线----》语音网关----》FreeSWITCH---》系统指挥调度平台
电话线接入语音网关,网关网线连接交换机,FreeSWITCH通过网线和语音网关建立连接,指挥调度平台通过调用FreeSWITCH实现
3,方案选择
1,FXO网关 采购了索泰的IAD300-8o
2, 调度程序采用FreeSWITCH,操作系统选择Contos,安装方式采用docker安装方便移植,因软件下载异常复杂
3,
🚀 第一步:安装并启动 Docker
1、安装 Docker
在终端执行以下命令:
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce
选项参考:
╭─ 请选择软件源的网络地址(访问方式):
│
╰─ ● 公网 / ○ 内网
╭─ 请选择软件源网络协议:
│
╰─ ● HTTP / ○ HTTPS
╭─ 是否安装 EPEL 附加软件包?
│
╰─ ● 是 / ○ 否
2、启动 Docker 服务
安装完成后,启动 Docker 并设置开机自启:
sudo systemctl start docker
sudo systemctl enable docker
📦 第二步:一键部署 FreeSWITCH
现在,我们用一个命令来拉取并运行 FreeSWITCH 镜像。这个命令会创建一个名为 freeswitch 的容器,并让它使用主机的网络,这样后续的呼叫测试会方便很多。
docker run -d --net=host --name freeswitch safarov/freeswitch:1.10.12
-d: 后台运行容器。
--net=host: 让容器和宿主机共享网络,这是为了简化 SIP 协议的通信。
--name freeswitch: 给容器起个名字叫 freeswitch。
safarov/freeswitch:1.10.12: 指定要拉取的镜像和版本。
注意:第一次运行时可能会卡在下载镜像的阶段,请耐心等待。
*** 经过多次检验,有时无法下载,配置了国内镜像也不行 *** 需要找一台有代理的机器 给 CentOS 7 配置代理正是解决 Docker 拉取镜像失败的最有效手段之一。
但是,给 CentOS 配置代理有一个巨大的“坑”:你在终端里配置了代理(比如 export http_proxy=...),curl 或 wget 能上网了,但 docker pull 命令通常不会走这个代理,因为它是由后台服务(Daemon)运行的,不继承终端的环境变量。
需要专门配置 Docker 服务的代理。以下是保姆级教程:
🚀 第一步:配置 Docker 专用的系统级代理
这是让 docker pull 走代理的关键步骤。 1、创建配置目录 在终端执行:
sudo mkdir -p /etc/systemd/system/docker.service.d
2、创建代理配置文件 执行以下命令创建并编辑文件:
sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf
3、写入代理信息 按 i 键进入编辑模式,粘贴以下内容(请替换为你实际的代理地址和端口):
[Service]
Environment="HTTP_PROXY=http://192.168.1.100:7890"
Environment="HTTPS_PROXY=http://192.168.1.100:7890"
Environment="NO_PROXY=localhost,127.0.0.1,registry-1.docker.io"
注意:
192.168.1.100:7890 必须换成你局域网内能上网的那台代理服务器的 IP 和端口(比如 Clash/V2Ray 的端口)。
NO_PROXY 里的 registry-1.docker.io 是为了防止你配置了国内镜像源时,Docker 反而走了代理导致连不上国内源。如果你只用代理,可以去掉这一项。
4、保存并退出 按 Esc 键,输入 :wq 并回车。 5、重载配置并重启 Docker 这一步至关重要,否则配置不会生效
sudo systemctl daemon-reload
sudo systemctl restart docker
6、验证代理是否生效 执行以下命令,查看输出中是否包含你刚才配置的代理地址:
sudo systemctl show --property=Environment docker
如果看到了 HTTP_PROXY=...,说明配置成功!现在尝试 docker pull 应该就能动了。
🌐 第二步:配置系统全局代理(可选)
如果你希望 yum install、curl 等命令也走代理,可以单独配置系统环境变量。 1、编辑配置文件
sudo vi /etc/profile
2、在文件末尾添加
export http_proxy="http://192.168.1.100:7890"
export https_proxy="http://192.168.1.100:7890"
export no_proxy="localhost,127.0.0.1"
3、使其生效
source /etc/profile
💡 常见问题排查
我的代理是 SOCKS5 协议(如 V2Ray/Clash 默认),能用吗?
Docker 官方守护进程原生只支持 HTTP/HTTPS 代理。
解决方法:你需要在你的代理软件(如 Clash)设置中开启 “允许局域网连接” 和 “混合端口”(通常是 HTTP 端口,如 7890),然后在上面的配置中填写这个 HTTP 端口,而不是 SOCKS 端口。
配置了代理还是连不上?
检查你的代理软件是否开启了“规则模式”,确保 registry-1.docker.io 和 docker.io 被规则命中走了代理。
检查 CentOS 虚拟机的网络模式。如果是 NAT 模式,确保虚拟机能 ping 通你物理机的 IP(即代理服务器的 IP)。
配置好这个之后,再配合之前的国内镜像源配置,你的 Docker 应该就能“起飞”了。 安装成功示例如下:
[root@localhost ~]# docker run -d --net=host --name freeswitch safarov/freeswitch:1.10.12
Unable to find image 'safarov/freeswitch:1.10.12' locally
1.10.12: Pulling from safarov/freeswitch
f6da18772b74: Pull complete
04374166f4ca: Pull complete
Digest: sha256:b31c743f4c911a19687c61e3214968f2a24f93f9d3d667cc26284192e158ffc6
Status: Downloaded newer image for safarov/freeswitch:1.10.12
bed0bf17be66ddd21a702a5a937db9e1a5a7cda551960f7df7631726d60d19c2
启动 FreeSWITCH
docker restart freeswitch
⚙️ 第三步:进入 FreeSWITCH 命令行界面
FreeSWITCH 运行起来后,我们需要进入它的“大脑”——命令行界面(CLI)来进行操作和测试。
执行以下命令:
docker exec -it freeswitch fs_cli
如果一切顺利,你会看到类似下面的欢迎界面:
FreeSWITCH Version 1.10.12 (...)
Type "help" to get a list of commands
...
freeswitch@internal>
看到这个 freeswitch@internal> 提示符,就说明 FreeSWITCH 已经成功部署并运行了!
✅ 第四步:验证系统是否正常
我们来做一个最简单的测试,确保系统能正常工作。在 fs_cli 界面中,输入以下命令并按回车:
status
你会看到 FreeSWITCH 的运行状态信息,包括运行时间、内存使用等。 再输入:
sofia status
这个命令会显示 SIP 协议栈的状态,你会看到 internal 和 external 两个 profile 的状态是 RUNNING。 恭喜你!到目前为止,FreeSWITCH 的核心环境已经搭建完毕。
进入FreeSWITCH控制台
docker exec -it freeswitch fs_cli
以下是正确启动示例:
[root@localhost ~]# docker exec -it freeswitch fs_cli
Cannot read termcap database;
using dumb terminal settings.
.=======================================================.
| _____ ____ ____ _ ___ |
| | ___/ ___| / ___| | |_ _| |
| | |_ \___ \ | | | | | | |
| | _| ___) | | |___| |___ | | |
| |_| |____/ \____|_____|___| |
| |
.=======================================================.
| Anthony Minessale II, Ken Rice, |
| Michael Jerris, Travis Cross |
| FreeSWITCH (http://www.freeswitch.org) |
| Paypal Donations Appreciated: paypal@freeswitch.org |
| Brought to you by ClueCon http://www.cluecon.com/ |
.=======================================================.
.=======================================================================================================.
| _ _ ____ _ ____ |
| / \ _ __ _ __ _ _ __ _| | / ___| |_ _ ___ / ___|___ _ __ |
| / _ \ | '_ \| '_ \| | | |/ _` | | | | | | | | |/ _ \ | / _ \| '_ \ |
| / ___ \| | | | | | | |_| | (_| | | | |___| | |_| | __/ |__| (_) | | | | |
| /_/ \_\_| |_|_| |_|\__,_|\__,_|_| \____|_|\__,_|\___|\____\___/|_| |_| |
| |
| ____ _____ ____ ____ __ |
| | _ \_ _/ ___| / ___|___ _ __ / _| ___ _ __ ___ _ __ ___ ___ |
| | |_) || || | | | / _ \| '_ \| |_ / _ \ '__/ _ \ '_ \ / __/ _ \ |
| | _ < | || |___ | |__| (_) | | | | _| __/ | | __/ | | | (_| __/ |
| |_| \_\|_| \____| \____\___/|_| |_|_| \___|_| \___|_| |_|\___\___| |
| |
| ____ _ ____ |
| / ___| |_ _ ___ / ___|___ _ __ ___ ___ _ __ ___ |
| | | | | | | |/ _ \ | / _ \| '_ \ / __/ _ \| '_ ` _ \ |
| | |___| | |_| | __/ |__| (_) | | | | _ | (_| (_) | | | | | | |
| \____|_|\__,_|\___|\____\___/|_| |_| (_) \___\___/|_| |_| |_| |
| |
.=======================================================================================================.
Type /help <enter> to see a list of commands
[This app Best viewed at 160x60 or more..]
+OK log level [7]
2026-04-01 12:06:48.542547 99.93% [ERR] mod_signalwire.c:393 Curl Result 77, Error: error setting certificate file: /etc/ssl/certs/ca-certificates.crt
2026-04-01 12:06:48.542547 99.93% [INFO] mod_signalwire.c:1125 Next SignalWire adoption check in 3 minutes
freeswitch@localhost.localdomain>
现在看到的 freeswitch@localhost.localdomain> 是 FreeSWITCH 的核心控制台(CLI)。
关于你看到的那条红色(或高亮)的报错信息,我来为你解释一下,以及接下来该做什么。 ❓ 关于那条报错信息 你看到的这行信息:
[ERR] mod_signalwire.c:393 Curl Result 77, Error: error setting certificate file: /etc/ssl/certs/ca-certificates.crt
这是什么? 这是一个非致命错误(Non-fatal Error)。FreeSWITCH 启动时会尝试连接 SignalWire(SignalWire 是 FreeSWITCH 的主要赞助商之一,由创始人创办)的服务器进行“心跳”或“检查更新”。
为什么报错? 因为容器内部找不到 SSL 证书文件,或者网络无法连接到外部的 SignalWire 服务器(可能是证书问题,也可能是你的服务器无法访问国外网络)。
有影响吗? 完全没有影响。 这只是意味着它无法连接 SignalWire 的云服务,但这丝毫不影响你使用 FreeSWITCH 的核心功能(打电话、点击拨号)。你可以完全忽略这条信息。
🚀 接下来:验证你的电话系统是否正常工作
1. 查看当前运行的模块
在 freeswitch@localhost.localdomain> 后面输入以下命令并回车:
reloadxml
这会让系统重新加载配置,通常不会有报错。
2. 查看在线分机
输入以下命令查看是否有分机注册进来(虽然你现在还没接电话机,但系统里预设了几个测试分机):
sofia status profile internal reg
预期结果:你会看到系统里预设的几个分机号(比如 1000, 1001 等),状态可能是 NO(未注册),这很正常,因为我们还没用电话去连它。
3. 退出控制台
按 Ctrl + C 两次,或者输入 exit 退出控制台。
freeswitch@localhost.localdomain> status
UP 0 years, 0 days, 0 hours, 3 minutes, 46 seconds, 934 milliseconds, 812 microseconds
FreeSWITCH (Version 1.10.12 -release-10222002881-a88d069d6fgit a88d069 2024-08-02 21:02:27Z 64bit) is ready
0 session(s) since startup
0 session(s) - peak 0, last 5min 0
0 session(s) per Sec out of max 30, peak 0, last 5min 0
1000 session(s) max
min idle cpu 0.00/99.97
Current Stack Size/Max 240K/8192K
freeswitch@localhost.localdomain> sofia status
Name Type Data State
=================================================================================================
external-ipv6 profile sip:mod_sofia@[::1]:5080 RUNNING (0)
192.168.0.12 alias internal ALIASED
external profile sip:mod_sofia@60.207.76.141:5080 RUNNING (0)
external::example.com gateway sip:joeuser@example.com NOREG
internal-ipv6 profile sip:mod_sofia@[::1]:5060 RUNNING (0)
internal profile sip:mod_sofia@60.207.76.141:5060 RUNNING (0)
=================================================================================================
4 profiles 1 alias
freeswitch@localhost.localdomain> reloadxml
+OK [Success]
2026-04-01 12:09:16.762482 99.93% [INFO] switch_stun.c:915 External ip address detected using STUN: 60.207.76.141
2026-04-01 12:09:16.982600 99.93% [INFO] switch_stun.c:915 External ip address detected using STUN: 60.207.76.141
2026-04-01 12:09:17.002539 99.93% [INFO] mod_enum.c:884 ENUM Reloaded
2026-04-01 12:09:17.002539 99.93% [INFO] switch_time.c:1436 Timezone reloaded 597 definitions
freeswitch@localhost.localdomain> sofia status profile internal reg
Registrations:
=================================================================================================
Total items returned: 0
=================================================================================================
2026-04-1 12:09:48.843105 99.93% [ERR] mod_signalwire.c:393 Curl Result 77, Error: error setting certificate file: /etc/ssl/certs/ca-certificates.crt
2026-04-01 12:09:48.843105 99.93% [INFO] mod_signalwire.c:1125 Next SignalWire adoption check in 4 minutes
freeswitch@localhost.localdomain>
使用电话模拟软件验证服务
推荐工具:
Windows/Mac: MicroSIP (轻量级,推荐) 或 Linphone。
手机 (Android/iOS): CSipSimple (Android) 或 Linphone (iOS/Android)。
MicroSIP 下载地址 https://www.microsip.org/downloads
CSipSimple 下载地址 https://csipsimple.en.uptodown.com/android/download
linphone 下载地址 https://download.linphone.org/releases/android/
MiCroSIP设置
点击右上角,选择 Edit Account 或者 Add Account 在Account页面需要编辑的栏目如下: SIP Server : 192.168.0.12 (SIP 服务器IP) Username : 1000 (分机号) Domain : 192.168.0.12 (SIP 服务器IP) Password : kf-A6DUVDXCt (SIP 服务器配置的密码)
docker exec -it freeswitch /bin/sh #登录docker
grep "default_password" /etc/freeswitch/vars.xml查看密码
然后点击save即可,左右下角落显示Online即表示连接成功
CSipSimple设置
安装好软件,点击左下角的钥匙图标---》选择“新建账户”---》选择通用向导的“Basic”--->然后输入账号名称“1001”---》输入账号“1001”---输入服务器“192.168.0.12”---》密码“kf-A6DUVDXCt”
启动docker镜像
有时候是看不到
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
查看所有镜像
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a1daf11189a safarov/freeswitch:1.10.12 "/docker-entrypoint.…" 2 weeks ago Exited (0) 16 hours ago freeswitch
启动项目
docker restart freeswitch
进入docker
docker exec -it freeswitch /bin/bash
这命令正常会报错
正确命令
docker exec -it freeswitch /bin/sh
配置文件在/etc/freeswitch/下
一次查看密码示例
[root@localhost ~]# docker exec -it freeswitch /bin/sh
Error response from daemon: container 4a1daf11189ad50d0d00cce1d027833e9fd282d163226e29811462d9ad7cde78 is not running
[root@localhost ~]# docker exec -it freeswitch fs_cli
Error response from daemon: container 4a1daf11189ad50d0d00cce1d027833e9fd282d163226e29811462d9ad7cde78 is not running
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a1daf11189a safarov/freeswitch:1.10.12 "/docker-entrypoint.…" 2 weeks ago Exited (0) 16 hours ago freeswitch
[root@localhost ~]# docker restart freeswitch
freeswitch
[root@localhost ~]# docker exec -it freeswitch /bin/sh
BusyBox v1.35.0 (Debian 1:1.35.0-4+b4) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/ # grep "default_password" /etc/freeswitch/vars.xml
YOU SHOULD CHANGE THIS default_password value if you don't want to be subject to any
<X-PRE-PROCESS cmd="set" data="default_password=kf-A6DUVDXCt"
查看连接用户
sofia status profile internal reg
一次查看示例
2026-04-21 06:18:03.129513 99.93% [INFO] mod_signalwire.c:1125 Next SignalWire adoption check in 8 minutes
freeswitch@localhost.localdomain> sofia status profile internal reg
Registrations:
=================================================================================================
Call-ID: f64ce6b1-ac83-4b55-a6dc-671556395afb
User: 1002@192.168.0.12
Contact: "" <sip:1002@192.168.0.15:5060;line=mhwtnun>
Agent: IAD300-8o
Status: Registered(UDP)(unknown) EXP(2026-04-21 06:51:39) EXPSECS(1889)
Ping-Status: Reachable
Ping-Time: 0.00
Host: localhost.localdomain
IP: 192.168.0.15
Port: 5060
Auth-User: 1002
Auth-Realm: 192.168.0.12
MWI-Account: 1002@192.168.0.12
Call-ID: RnTsyD9HbFuqmJld-rVb7hRZMvfTOIRx
User: 1001@192.168.0.12
Contact: "" <sip:1001@192.168.0.9:5060;ob>
Agent: CSipSimple_HWNOH-31/r2470
Status: Registered(UDP)(unknown) EXP(2026-04-21 06:26:25) EXPSECS(375)
Ping-Status: Reachable
Ping-Time: 0.00
Host: localhost.localdomain
IP: 192.168.0.9
Port: 5060
Auth-User: 1001
Auth-Realm: 192.168.0.12
MWI-Account: 1001@192.168.0.12
Call-ID: a7b8692c44ca44b2a7ef61cb48f76e4a
User: 1000@192.168.0.12
Contact: "" <sip:1000@192.168.0.10:49855;ob>
Agent: MicroSIP/3.22.5
Status: Registered(UDP)(unknown) EXP(2026-04-21 06:21:18) EXPSECS(68)
Ping-Status: Reachable
Ping-Time: 0.00
Host: localhost.localdomain
IP: 192.168.0.10
Port: 49855
Auth-User: 1000
Auth-Realm: 192.168.0.12
MWI-Account: 1000@192.168.0.12
Total items returned: 3
=================================================================================================
freeswitch@localhost.localdomain>
索泰IAD300-8o配置
1,设备wan口插上网线,
设备到手后先使用内网Lan口登录,获取外网IP,使用其他方法也可以, 然后使用浏览器登录,这里是192.168.0.15 输入密码直接登录
2,配置VOIP
选择VOIP----》SIP服务器----》新建 名称:freeSWITCH 传输协议:UDP 注册模式:端口注册 域名/IP地址:192.168.0.12 From头域地址:192.168.0.12 其他默认点击保存
3,配置PSTN
选择PSTN----》模拟中继----》端口1“修改”-----》 线路号码:1002 VOIP服务器:选择上面新建的freeSWITCH 用户名:1002 认知名称:1002 密码:kf-A6DUVDXCt 点击保存
4,呼叫控制
选择呼叫控制----》呼出路由----》 名称:留空 描述:留空 优先级:默认 时间规则:任意 源地址:任意 主叫号码匹配:留空 被叫号码匹配:^1[3-9]\d{9}$ 目的地址:端口:1 (FXO)【上面编辑的端口】 保存
5,配置服务
选择“服务”----》自动配置 启用SIP即插即用 : 打钩 端口:WAN 服务器地址:192.168.0.12 服务器端口:5060 更新周期:1
测试单线呼叫
配置转发 请修改 FreeSWITCH 的拨号计划(Dialplan),必须显式地把手机号拼接到命令里。 在上一轮尝试中使用了 user/... 语法,这是用来打给“内部注册分机”的。我们要打给“外部网关”,必须使用 sofia/gateway/... 语法。 请将你的 Dialplan 修改为以下代码:
1. 修改 Dialplan (XML)
找到你刚才修改的那个 XML 文件(通常在 conf/dialplan/default/ 或 conf/dialplan/public/ 下),将
vi /etc/freeswitch//dialplan/default.xml
在这些内容下插入
<!-- http://wiki.freeswitch.org/wiki/Dialplan_XML -->
<include>
<context name="default">
<extension name="unloop">
<condition field="${unroll_loops}" expression="^true$"/>
<condition field="${sip_looped_call}" expression="^true$">
<action application="deflect" data="${destination_number}"/>
</condition>
</extension>
在上述内容结尾插入以下内容
<!-- 路由规则:拨打手机号走索泰网关 -->
<extension name="Call_Mobile_Via_SuoTai">
<condition field="destination_number" expression="^(1[3-9]\d{9})$">
<!-- 关键修改点 -->
<!-- 1. 不需要 set sip_outbound_destination_number -->
<!-- 2. 使用 user/1002 而不是 sofia/internal/1002 -->>
<!-- user/ 会自动处理注册状态和 Contact 地址 -->->>
<!-- <action application="bridge" data="sofia/gateway/1002/$1"/> -->
<action application="bridge" data="sofia/gateway/1002/$1"/>
</condition>
</extension>
2. 配置 Gateway (XML)
FreeSWITCH 需要知道 1002 是一个网关,而不是一个普通分机。你需要在 conf/sip_profiles/external/ 目录下(或者你的自定义目录)创建一个 XML 文件,例如 1002.xml:
touch /etc/freeswitch/sip_profiles/external/1002.xml
vi /etc/freeswitch/sip_profiles/external/1002.xml
echo "
<include>
<gateway name="1002">
<!-- 索泰网关的 IP 地址 -->
<param name="username" value="1002"/>
<param name="realm" value="192.168.0.15"/>
<!-- 索泰网关的密码 网关那边填的sip密码-->
<param name="password" value="kf-A6DUVDXCt"/>
<!-- 索泰网关的端口 (通常默认) -->
<param name="port" value="5060"/>
<!-- 注册过期时间 -->
<param name="register" value="false"/>
<!-- 如果网关需要鉴权用户名,通常和 username 一样 -->
<param name="auth-username" value="1002"/>
<!-- 通常不需要域,除非网关强制要求 -->
<!-- <param name="from-domain" value="192.168.0.15"/> -->
</gateway>
</include>
"> /etc/freeswitch/sip_profiles/external/1002.xml
在 FreeSWITCH CLI 中输入:
#重启服务重新加载配置文件
reloadxml
注意:register=false 是因为我们通常用网关是“中继”模式,FreeSWITCH 不需要向它注册,只需要能发 INVITE 给它即可。
重启服务
#sofia global shutdown restart
#sofia restart
freeswitch@localhost.localdomain> sofia global shutdown restart
-ERR Usage: siptrace <on|off>|capture <on|off>|watchdog <on|off>|debug <sla|presence|none
freeswitch@localhost.localdomain> sofia restart
Unknown Command [restart]
freeswitch@localhost.localdomain>
这两个命令确实都不对。sofia global 是用来设置全局参数(如抓包)的,而 sofia 模块本身并没有 restart 这个直接子命令。 我们需要针对具体的 Profile(配置集)进行操作。既然你的网关是在 external 配置集下,正确的命令是:
sofia profile external restart
或者,如果你只想重新加载配置而不中断现有通话(推荐):
sofia profile external rescan
3.等待重启完成后,查询网关状态:
sofia status gateway
解决通话32每秒固定挂断的问题
问题定位: 是 NAT(网络地址转换) 问题导致的 ACK 超时。 🔍 为什么会是 32 秒? SIP 通话建立的流程是: 1. INVITE(邀请) 2. 200 OK(对方接听) 3. ACK(确认收到)<--- 问题出在这里 现象解释: 电话能通,说明 INVITE 和 200 OK 都成功了。但是,FreeSWITCH 发送完 200 OK 后,一直在等对方的 ACK 确认包。 由于 IP 地址配置错误,ACK 包发到了错误的地方(或者被防火墙挡住了)。 FreeSWITCH 默认等待 ACK 的超时时间大约就是 32秒(实际上是 32000毫秒)。时间一到,它认为对方失联了,就会主动挂断电话。
🛠️ 解决方案 你需要修改 FreeSWITCH 的 vars.xml 文件,告诉它你的真实 IP 地址,而不是让它去猜(它猜错了)。 第一步:修改 vars.xml
1、打开文件:
vi /etc/freeswitch/vars.xml
2、找到 external_rtp_ip 和 external_sip_ip 这两行。
它们可能配置的是 stun:... 或者 auto-nat,这在局域网环境下通常会出错。
<X-PRE-PROCESS cmd="stun-set" data="external_rtp_ip=stun:stun.freeswitch.org"/>
<X-PRE-PROCESS cmd="stun-set" data="external_sip_ip=stun:stun.freeswitch.org"/>
注释掉上面2行
<!-- 修改后:宴接写死你的 IP -->
<X-PRE-PROCESS cmd="set" data="external_rtp_ip=192.168.0.12"/>
<X-PRE-PROCESS cmd="set" data="external_sip_ip=192.168.0.12"/>
3、强制修改 internal.xml
不要依赖变量,直接在配置文件中写死你的 FreeSWITCH 局域网 IP(192.168.0.12)
vi /etc/freeswitch/sip_profiles/internal.xml
找到:
<param name="sip-ip" value="$${local_ip_v4}
注释替换为:
<!--<param name="sip-ip" value="$${local_ip_v4}"/> -->
<param name="sip-ip" value="192.168.0.12"/>
<param name="rtp-ip" value="$${local_ip_v4}"/>
注释替换为:
<!-- <param name="rtp-ip" value="$${local_ip_v4}"/> --> <param name="rtp-ip" value="192.168.0.12"/>
<param name="ext-rtp-ip" value="$${external_rtp_ip}"/>
<param name="ext-sip-ip" value="$${external_sip_ip}"/>
注释替换为:
<!-- <param name="ext-rtp-ip" value="$${external_rtp_ip}"/>
<param name="ext-sip-ip" value="$${external_sip_ip}"/>
-->
<param name="ext-rtp-ip" value="192.168.0.12"/>
<param name="ext-sip-ip" value="192.168.0.12"/>
注意:如果你的文件里没有这些行,请手动添加进去。
4、彻底重启 FreeSWITCH
不要只用 reloadxml。如果是 Docker,必须重启容器:
# 在宿主机执行
docker restart freeswitch
这时可以使用一根外线测试了
设置IAD300-8o网关前4口通讯
专心把底层的“路”铺好。要实现“4个口都能打任意号码”,核心就是让 FreeSWITCH 把这 4 个 FXO 口识别为 4 个独立的“出口通道”。 这就好比你有 4 个拨号盘,每个都能独立拨号。我们需要分三步走:索泰网关设置账号 -> FreeSWITCH 配置网关 -> 验证通道。
🛠️ 第一步:索泰网关侧配置 (制造 4 个“拨号盘”)
我们需要让索泰网关把 4 个 FXO 口模拟成 4 个独立的 SIP 分机。 登录索泰网关 Web 界面,进入 线路配置 -> FXO 设置 (或类似名称):
1、FXO 1 设置:
SIP 账号:1001
SIP 密码:kf-A6DUVDXCt (自定义)
SIP 服务器:192.168.0.12 (FreeSWITCH IP)
注册:勾选 (让索泰主动告诉 FreeSWITCH 它在线)
端口:5060
*2、FXO 2 设置:
SIP 账号:1002
SIP 密码:kf-A6DUVDXCt
SIP 服务器:192.168.0.12
注册:勾选
端口:5060*
FXO 3 设置 -> 账号 1003
FXO 4 设置 -> 账号 1004
保存并应用。此时,索泰网关会向 FreeSWITCH 注册 4 个分机。
🛠️ 第二步:FreeSWITCH 侧配置 (连接 4 个“拨号盘”)
FreeSWITCH 需要知道这 4 个“拨号盘”的存在,并允许通过它们拨出电话。
1. 确保索泰能注册进来 (Internal Profile)
FreeSWITCH 的 internal 配置集需要允许索泰注册。
打开 /etc/freeswitch/sip_profiles/internal.xml,确保
<param name="auth-calls" value="true"/>
<param name="force-register-domain" value="$${domain}"/>
默认通常是开启的,检查即可。
2. 创建 4 个“出口网关” (External Profile)
虽然索泰是“注册进来”的,但为了让 FreeSWITCH 能主动使用某个 FXO 口拨出电话,我们需要在 external 配置集中定义这 4 个网关。这相当于告诉 FreeSWITCH:“如果你想用 1 号口打电话,就找 1001”。
在 /etc/freeswitch/sip_profiles/external/ 目录下创建 4 个文件: suotai_fxo1.xml
<include>
<gateway name="suotai_fxo1">
<!-- 指向索泰的 IP,而不是让索泰注册 -->
<param name="username" value="1001"/>
<param name="realm" value="192.168.0.15"/> <!-- 索泰网关 IP -->
<param name="password" value="123456"/>
<param name="port" value="5060"/>
<param name="register" value="false"/> <!-- 关键:我们不需要 FreeSWITCH 去注册,只需要它知道路由 -->
<param name="auth-username" value="1001"/>
<!-- 关键:强制使用索泰的 IP 发送请求,确保走 FXO 1 -->
<param name="proxy" value="192.168.0.15:5060"/>
</gateway>
</include>
suotai_fxo2.xml
<include>
<gateway name="suotai_fxo2"><!-- 和文件名无关这里的name是真正定义网关名称的地方 -->
<param name="username" value="1002"/>
<param name="realm" value="192.168.0.15"/>
<param name="password" value="123456"/>
<param name="port" value="5060"/>
<param name="register" value="false"/>
<param name="auth-username" value="1002"/>
<param name="proxy" value="192.168.0.15:5060"/>
</gateway>
</include>
同理创建 suotai_fxo3.xml (账号 1003) 和 suotai_fxo4.xml (账号 1004)。
注意:这里我们使用 proxy 参数强制指定索泰 IP,这样即使索泰是注册模式,FreeSWITCH 也能通过指定的 IP 和端口把呼叫送过去。
3. 配置拨号规则 (Dialplan)
为了让这 4 个网关能拨打任意号码,我们需要在 Dialplan 中添加通用规则。
vi /etc/freeswitch/dialplan/default.xml
打开 /etc/freeswitch/dialplan/default.xml,在
<extension name="Outbound_Any_Number">
<condition field="destination_number" expression="^(1[3-9]\d{9}|0\d{2,3}\d{7,8})$">
<!-- 这里不写死 bridge,因为我们会通过 originate 命令指定具体的 gateway -->
<!-- 这个 extension 主要是为了匹配号码格式 -->
<action application="answer"/>
<!-- 实际上,originate 命令会直接指定 gateway,所以这里可以留空或简单处理 -->
</condition>
</extension>
其实,使用 originate 命令时,可以直接指定 sofia/gateway/...,完全绕过 Dialplan 的正则匹配,只要 Gateway 配置正确即可。
🛠️ 第三步:验证配置
配置完成后,执行以下操作:
1、重启 FreeSWITCH:
fs_cli -x "shutdown restart"
启动镜像
docker restart freeswitch
2、检查网关状态:
fs_cli -x "sofia status gateway"
你应该能看到 suotai_fxo1, suotai_fxo2, suotai_fxo3, suotai_fxo4 都在列表中,状态是 ACTIVE 或 DOWN(只要不是 INVALID 就行)。 以下是配置示例
[root@localhost ~]# docker exec -it freeswitch /bin/sh
BusyBox v1.35.0 (Debian 1:1.35.0-4+b4) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/ # fs_cli -x "sofia status gateway"
Profile::Gateway-Name Data State Ping Time IB Calls(F/T) OB Calls(F/T)
=================================================================================================
external::example.com sip:joeuser@example.com NOREG 0.00 0/0 0/0
external::suotai_fxo1 sip:1001@192.168.0.15:5060 NOREG 0.00 0/0 0/0
external::1004 sip:1004@192.168.0.15:5060 NOREG 0.00 0/0 0/0
external::1003 sip:1003@192.168.0.15:5060 NOREG 0.00 0/0 0/0
external::suotai_fxo2 sip:1002@192.168.0.15:5060 NOREG 0.00 0/0 0/0
=================================================================================================
5 gateways: Inbound(Failed/Total): 0/0,Outbound(Failed/Total):0/0
5、测试拨号:
在 fs_cli 中分别测试 4 个口:
# 测试 FXO 1 拨打你的手机
originate sofia/gateway/suotai_fxo1/13800000000 &echo()
# 测试 FXO 2 拨打你的手机
originate sofia/gateway/suotai_fxo2/13800000000 &echo()
# 测试 FXO 3 拨打固话
originate sofia/gateway/suotai_fxo3/01012345678 &echo()
# 测试 FXO 4 拨打固话
originate sofia/gateway/suotai_fxo4/01012345678 &echo()
开启代码调用
1、FreeSWITCH修改配置文件,开启ACL非本地访问
修改:/etc/freeswitch/autoload_configs/event_socket.conf.xml
vi /etc/freeswitch/autoload_configs/event_socket.conf.xml
确认包含以下内容
<configuration name="event_socket.conf" description="Socket Client">
<settings>
<!--<param name="bind-ip" value="0.0.0.0"/>--> <!--经过测试配置此项不行 -->
<param name="nat-map" value="false"/>
<param name="listen-ip" value="0.0.0.0"/>
<param name="listen-port" value="8021"/>
<param name="password" value="ClueCon"/> <!-- 登录密码,以此密码为主 -->
<param name="apply-inbound-acl" value="lan"/><!--经过测试配置此项不行 -->
<!--<param name="apply-inbound-acl" value="localnet.auto"/>--><!-- 经过测试配置此项不行 -->
<!--<param name="apply-inbound-acl" value="loopback.auto"/>--><!--开启此项只能本地连接 -->
<!--<param name="stop-on-bind-error" value="true"/>-->
<!--<param name="apply-inbound-acl" value="lan"/>-->
<!--<param name="acl" value="false"/>--><!--经过测试配置此项不行 -->
</settings>
</configuration>
上述修改后需要同步修改 /etc/freeswitch//autoload_configs/acl.conf.xml
vi /etc/freeswitch//autoload_configs/acl.conf.xml
确认以下内容: 上面配置的是lan,所以需要修改lan
<list name="lan" default="allow">
<node type="deny" cidr="192.168.42.0/24"/>
<node type="allow" cidr="192.168.42.42/32"/>
<node type="allow" cidr="192.168.0.0/0"/><!-- 添加此项允许内网访问 -->
</list>
重启docker以让配置生效
docker restart freeswitch
2、引入java项目
在spring boot的pom.xml中添加以下内容
<dependency>
<groupId>org.freeswitch.esl.client</groupId>
<artifactId>org.freeswitch.esl.client</artifactId>
<version>0.9.2</version>
</dependency>
经过多次测试,其他的连接效果都不太好
注意:本文归作者所有,未经作者允许,不得转载