1. 在CentOS7.2中部署freeswitch环境
vmware虚拟机使用网络共享的话没办法让freeswitch监听5060端口,换成桥接之后就恢复正常了
1.2 通过signalwire源下载并安装rpm包
1
2
3
4
5
6
7
8
9
|
# 配置初始参数
echo "signalwire" > /etc/yum/vars/signalwireusername
echo "TOKEN" > /etc/yum/vars/signalwiretoken # 这里需要替换Token字段为刚生成的token
# 安装所需的源
yum install -y https://$(< /etc/yum/vars/signalwireusername):$(< /etc/yum/vars/signalwiretoken)@freeswitch.signalwire.com/repo/yum/centos-release/freeswitch-release-repo-0-1.noarch.rpm epel-release
# 安装freeswitch
yum install -y freeswitch-config-vanilla freeswitch-lang-* freeswitch-sounds-*
|
1.3 关闭防火墙
反正是内网测试,安全问题并不是第一位的,为了避免不必要的端口问题就关闭防火墙:
1
2
|
systemctl disable firewalld
systemctl stop firewalld
|
1.4 启动并检查freeswitch的运行状态
启动freeswitch:
1
|
freeswitch -nc -nonat # 后台运行且不使用NAT穿透
|
如果报错没有这个命令的话,使用下面命令安装:
检查freeswitch是否监听了对应端口:
1
2
3
4
5
6
7
8
9
10
11
12
|
netstat -npl | grep freeswitch
tcp 0 0 192.168.77.183:5060 0.0.0.0:* LISTEN 2871/freeswitch
tcp 0 0 192.168.77.183:5066 0.0.0.0:* LISTEN 2871/freeswitch
tcp 0 0 192.168.77.183:7443 0.0.0.0:* LISTEN 2871/freeswitch
tcp 0 0 192.168.77.183:5080 0.0.0.0:* LISTEN 2871/freeswitch
tcp6 0 0 2409:894a:2d:998:2:5060 :::* LISTEN 2871/freeswitch
tcp6 0 0 :::8021 :::* LISTEN 2871/freeswitch
tcp6 0 0 2409:894a:2d:998:2:5080 :::* LISTEN 2871/freeswitch
udp 0 0 192.168.77.183:5060 0.0.0.0:* 2871/freeswitch
udp 0 0 192.168.77.183:5080 0.0.0.0:* 2871/freeswitch
udp6 0 0 2409:894a:2d:998:2:5060 :::* 2871/freeswitch
udp6 0 0 2409:894a:2d:998:2:5080 :::* 2871/freeswitch
|
可以看到freeswitch正常启动且能够监听几个常用端口
1.5 systemd相关问题
通过rpm包安装的freeswitch会自动注册systemd服务,可以使用systemctl
命令控制freeswitch的启停。
现在直接用systemctl status freeswitch
会出现这两个报错:
1
2
3
4
5
6
7
8
9
|
● freeswitch.service - FreeSWITCH
Loaded: loaded (/usr/lib/systemd/system/freeswitch.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2023-08-10 17:49:14 CST; 2min 57s ago
Main PID: 962 (freeswitch)
CGroup: /system.slice/freeswitch.service
└─962 /usr/bin/freeswitch -nc -nf
Aug 10 17:49:14 localhost.localdomain systemd[1]: Started FreeSWITCH.
Aug 10 17:49:14 localhost.localdomain freeswitch[962]: ERROR: Failed to set SCHED_FIFO scheduler (Operation not permitted)
Aug 10 17:49:14 localhost.localdomain freeswitch[962]: ERROR: Could not set nice level
|
权限问题,通过修改/usr/lib/systemd/system/freeswitch.service
可以修复问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[Unit]
Description=FreeSWITCH
After=syslog.target network.target
After=postgresql.service postgresql-9.3.service postgresql-9.4.service mysqld.service httpd.service
[Service]
EnvironmentFile=-/etc/sysconfig/freeswitch
Environment="USER=freeswitch"
# RuntimeDirectory is not yet supported in CentOS 7. A workaround is to use /etc/tmpfiles.d/freeswitch.conf
#RuntimeDirectory=/run/freeswitch
#RuntimeDirectoryMode=0750
WorkingDirectory=/run/freeswitch
ExecStart=/usr/bin/freeswitch -nc -nf -u ${USER} $FREESWITCH_PARAMS
ExecReload=/usr/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
|
然后重新加载一下systemctl:
1
|
systemctl daemon-reload
|
现在再重新测试就恢复正常了:
1
2
3
4
5
6
7
8
|
● freeswitch.service - FreeSWITCH
Loaded: loaded (/usr/lib/systemd/system/freeswitch.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2023-08-10 18:00:29 CST; 15s ago
Main PID: 948 (freeswitch)
CGroup: /system.slice/freeswitch.service
└─948 /usr/bin/freeswitch -nc -nf -u freeswitch
Aug 10 18:00:29 localhost.localdomain systemd[1]: Started FreeSWITCH.
|
最后记得禁用一下开机自启,方便之后调试:
1
|
systemctl disable freeswitch
|
1.6 语音测试&其中的坑
-
手机和iPad下载 linphone
-
两个账户为1000(手机)和1010(iPad),密码均为1234,地址为192.168.77.183
测试了两个项目:
- 1000(手机)拨打9198,能听到Tetris的BGM,且能正常通话
- 1000(手机)拨打1010(iPad),语音拨打有三十秒主动断连的问题:
1.7 填坑
https://blog.csdn.net/FlyLikeButterfly/article/details/100581609?ydreferer=aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8%3D
这个更像是原因说明,我的实验场景是在一个小内网中的,在默认情况下通话建立后,设备一直再向公网地址发送ack,相当于虚空喊话,自然得不到另一台设备的应答,于是经过设置的默认30秒超时时间之后就自动断连了。
修改方法如下,也可以直接将这两行注释掉。
1
2
3
4
5
|
vim /etc/freeswitch/sip_profiles/internal.xml
# 修改这两行
<param name="ext-rtp-ip" value="$${local_ip_v4}"/>
<param name="ext-sip-ip" value="$${local_ip_v4}"/>
|
然后重新检测freeswitch运行状态:
1
|
kill -9 $(pidof freeswitch) && freeswitch -nc -nonat
|
经测试,修复了三十秒自动挂断的问题。
1.8 其他
经过测试,现在是可以正常进行视频和音频通话的,但是有两个比较头疼的问题:
- 拨号等待时间过久;
- 通话延迟比较高,音频通话可能还好一点,视频通话延迟很高,有些影响视频通话的效果。
1.9 单节点双freeswitch桥接
1.9.1复制所有所需文件
由于我这个是直接使用yum从官方源中安装的程序包,其实并不像书里那样文件全都位于/usr/local/freeswitch
中:
1
2
|
[root@localhost ~]# whereis freeswitch
freeswitch: /usr/bin/freeswitch /usr/lib64/freeswitch /etc/freeswitch /usr/share/freeswitch
|
四个项目分别是:
- 二进制可执行文件
- 运行库
- 配置文件
- 文档、字体、脚本、声音等素材文件
使用下面的命令复制一份:
1
2
3
4
5
6
7
8
|
cp /usr/bin/freeswitch /usr/bin/freeswitch2
ln -sf /usr/lib64/freeswitch /usr/lib64/freeswitch2
mkdir /etc/freeswitch2
cp -r /etc/freeswitch/* /etc/freeswitch2
ln -sf /usr/share/freeswitch /usr/share/freeswitch2
|
除此之外还需要知道日志路径、数据库路径等信息
在fs_cli
中可以使用eval $${variable}
查询
1
2
3
4
|
freeswitch@localhost.localdomain> eval $${db_dir}
/var/lib/freeswitch/db
freeswitch@localhost.localdomain> eval $${log_dir}
/var/log/freeswitch
|
1.9.2 修改freeswitch2的信息
按照书中的说明修改配置文件:
/etc/freeswitch2/autoload_configs/event_socket.conf.xml
1
|
<param name="listen-port" value="38021"/>
|
/etc/freeswitch2/vars.xml
1
2
3
4
|
<X-PRE-PROCESS cmd="set" data="internal_sip_port=35060"/>
<X-PRE-PROCESS cmd="set" data="internal_tls_port=35061"/>
<X-PRE-PROCESS cmd="set" data="external_sip_port=35080"/>
<X-PRE-PROCESS cmd="set" data="external_tls_port=35081"/>
|
这个是书上没有说的,但是如果不配置的话我这边会出现一些报错信息
/etc/freeswitch2/sip_profiles/internal.xml
1
2
|
<param name="ws-binding" value=":35066"/>
<param name="wss-binding" value=":37443"/>
|
1.9.3 运行并检测状态
所以freeswitch2的启动命令就是:
1
|
freeswitch2 -conf /etc/freeswitch2 -log /var/log/freeswitch2 -db /var/lib/freeswitch2/db -nc -nonat
|
检查运行状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[root@localhost ~]# netstat -npl | grep freeswitch
tcp 0 0 192.168.109.218:7443 0.0.0.0:* LISTEN 2807/freeswitch
tcp 0 0 192.168.109.218:37443 0.0.0.0:* LISTEN 2812/freeswitch2
tcp 0 0 192.168.109.218:5060 0.0.0.0:* LISTEN 2807/freeswitch
tcp 0 0 192.168.109.218:35060 0.0.0.0:* LISTEN 2812/freeswitch2
tcp 0 0 192.168.109.218:5080 0.0.0.0:* LISTEN 2807/freeswitch
tcp 0 0 192.168.109.218:35080 0.0.0.0:* LISTEN 2812/freeswitch2
tcp 0 0 192.168.109.218:5066 0.0.0.0:* LISTEN 2807/freeswitch
tcp 0 0 192.168.109.218:35066 0.0.0.0:* LISTEN 2812/freeswitch2
tcp6 0 0 2409:894a:22:228c::5060 :::* LISTEN 2807/freeswitch
tcp6 0 0 2409:894a:22:228c:35060 :::* LISTEN 2812/freeswitch2
tcp6 0 0 2409:894a:22:228c::5080 :::* LISTEN 2807/freeswitch
tcp6 0 0 2409:894a:22:228c:35080 :::* LISTEN 2812/freeswitch2
tcp6 0 0 :::8021 :::* LISTEN 2807/freeswitch
tcp6 0 0 :::38021 :::* LISTEN 2812/freeswitch2
udp 0 0 192.168.109.218:5060 0.0.0.0:* 2807/freeswitch
udp 0 0 192.168.109.218:35060 0.0.0.0:* 2812/freeswitch2
udp 0 0 192.168.109.218:5080 0.0.0.0:* 2807/freeswitch
udp 0 0 192.168.109.218:35080 0.0.0.0:* 2812/freeswitch2
udp6 0 0 2409:894a:22:228c::5060 :::* 2807/freeswitch
udp6 0 0 2409:894a:22:228c:35060 :::* 2812/freeswitch2
udp6 0 0 2409:894a:22:228c::5080 :::* 2807/freeswitch
udp6 0 0 2409:894a:22:228c:35080 :::* 2812/freeswitch2
|
1.9.4 两个FSM的桥接
在两个freeswitch的配置路径/etc/freeswitch/
中找到./dialplan/default.xml
,添加如下内容:
1
2
3
4
5
6
|
# vim /etc/freeswitch/dialplan/default.xml
<extension name="local_bridge">
<condition field="destination_number" expression="^0(.*)$">
<action application="bridge" data="sofia/external/sip:$1@$${local_ip_v4}:35060"/>
</condition>
</extension>
|
1
2
3
4
5
6
|
# vim /etc/freeswitch2/dialplan/default.xml
<extension name="local_bridge">
<condition field="destination_number" expression="^0(.*)$">
<action application="bridge" data="sofia/external/sip:$1@$${local_ip_v4}:5060"/>
</condition>
</extension>
|
分别把符合正则表达式的号码桥接到sofia上,sofia再将这个号码转发到对应地址。
2 相关概念
2.1 Profile
类似一个分流策略,根据不同的规则(可能是IP、可能是载荷类型,总之有多种分类方式)将入站流量进行分流。
FreeSwitch默认提供了internal
和external
两个Profile,按照官方的说法,internal的适用范围是本地网络中使用FreeSwitch注册的设备,而external适用于其他网络中其他sip中继服务的设备。
对一个channel来说,存在new-init-routing-execute-hangup-reporting-destory这几个流程。
https://blog.51cto.com/u_14349334/3490017
一个profile会启动一个UserAgent
2.2 DialPlan
一个DialPlan可能会由多个Context构成,一个Context下又有多个Extension,这些项目构成了一个树状结构,不同Context下的Extension是相互隔离的。
一个Extension中定义了“条件”和“动作”,也就是和这么两个字段。中较为常见的是词条是"destination_number",但也可以使用别的变量作为词条。
这玩意还不能嵌套,只能分情况将多个中的进行逻辑与,同时可以通过:
- on-true
- on-false
- always
- never
四种break参数控制判断流向。
除了break参数外,满足的项目会到中进行下一步操作,同时可以使用让不满足的项目执行特定操作。
条件判断完全结束之后才会进行动作,因此在一些条件判断-赋值的场景下会出现错误,通过内联执行的方法能够解决这些问题,但是内联执行本身是一种改变程序自然执行状态的操作,因此会有一些限制。
正则表达式中使用了“()”,因此匹配结果会放入变量$1中。
常见的dialPlan有以下几项:
- set
- echo
- info
- answer
- bridge
- playback
- sleep
- ring_ready
- pre_answer
- read
- play_and_get_digits
2.2.1 一个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<extension name="Local_Extension">
//匹配1000-1019,保存到$1中
<condition field="destination_number" expression="^(10[01][0-9])$">
// 将号码保存到dialed_extension中
<action application="export" data="dialed_extension=$1"/>
<!-- bind_meta_app can have these args <key> [a|b|ab] [a|b|o|s] <app> -->
// 绑定DTMF
<action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
<action application="bind_meta_app" data="2 b s record_session::$${recordings_dir}/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
<action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
<action application="bind_meta_app" data="4 b s execute_extension::att_xfer XML features"/>
// 设置回铃音
<action application="set" data="ringback=${us-ring}"/>
// 设置转接时的回铃音
<action application="set" data="transfer_ringback=$${hold_music}"/>
//设置超时时间
<action application="set" data="call_timeout=30"/>
<!-- <action application="set" data="sip_exclude_contact=${network_addr}"/> -->
<action application="set" data="hangup_after_bridge=true"/>
<!--<action application="set" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> -->
<action application="set" data="continue_on_fail=true"/>
// 设置哈希,绑定在腿上
<action application="hash" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
<action application="hash" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
<action application="set" data="called_party_callgroup=${user_data(${dialed_extension}@${domain_name} var callgroup)}"/>
<action application="hash" data="insert/${domain_name}-last_dial_ext/${called_party_callgroup}/${uuid}"/>
<action application="hash" data="insert/${domain_name}-last_dial_ext/global/${uuid}"/>
<!--<action application="export" data="nolocal:rtp_secure_media=${user_data(${dialed_extension}@${domain_name} var rtp_secure_media)}"/>-->
<action application="hash" data="insert/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
<action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
<action application="answer"/>
<action application="sleep" data="1000"/>
<action application="bridge" data="loopback/app=voicemail:default ${domain_name} ${dialed_extension}"/>
</condition>
</extension>
|
满足条件中设置的正则表达式的时候就会进入动作层,在默认配置里一般是执行,这些application大部分在mode_dptools
中定义:
https://developer.signalwire.com/freeswitch/FreeSWITCH-Explained/Modules/mod_dptools_1970333/
单腿、多腿呼叫:一个用户(User Agent)和FreeSwitch之间的连接被称为一个“leg”或“channel”,因此单腿呼叫常见场景就是ivr,即用户和FreeSwitch通话。双腿呼叫也就是两个用户通过FreeSwitch建立连接。