FreeSWITCH + O口交换机(FXO)搭建语音电话服务VOIP

轩辕暗神 17天前 ⋅ 51 阅读

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>

经过多次测试,其他的连接效果都不太好


全部评论: 0

    我有话说: