freeSWITCH从0开始搭建记录

11,558 阅读4分钟

准备

硬件准备

  • centos7.x服务器
  • 公网IP(非必须,有的话更好,测试不影响)
  • 域名(非必须,有的话更好,测试不影响)
  • 证书(非必须,有的话更好,测试不影响)

客户端

网页端

开始安装

yum install -y https://files.freeswitch.org/repo/yum/centos-release/freeswitch-release-repo-0-1.noarch.rpm epel-release
yum install -y freeswitch-config-vanilla freeswitch-lang-* freeswitch-sounds-*

安装速度跟你的网络有关,耐心等待,看到complete,就证明你安装完成了。

执行freeswitch -help就能看到相关命令提示

前提知识

  • 安装完毕后,系统全局中就会注册有freeswitch命令和fs_cli命令;
  • freeswitch是启动freeswitch服务的开始命令。
  • fs_cli是客户端命令,执行该命令就能连接到freeswitch服务器,就可以操作和查看freeswitch日志了。
  • 执行freeswitch之后,会自动连接上服务器并查看日志;
  • 当你更改了相关配置后,按快捷键F6或者在命令行按Enter键并输入reloadxml命令进行更新配置操作,下面会列出所有快捷键
  • 关闭freeswitch服务在命令行按Enter键并输入fsctl shutdown命令,如果重启可以再键入freeswitch即可
  • 重启freeswitch服务命令是fsctl restart,但是我老是不成功;
  • freeswitch服务启动后,客户端SIP协议端口在5060,网页端SIP协议端口在5066(使用SIP协议状态码,底层是WS协议),外线是5080端口

修改配置

修改默认密码

使用默认密码注册用户拨打电话,系统会做拨号延迟。

我的配置文件在/etc/freeswitch,但是网上很多资料说在/usr/local/freeswitch/conf,根据自己的情况吧

cd /etc/freeswitch
vim vars.xml

将里面的默认密码1234修改一下,比如123456,如

<X-PRE-PROCESS cmd="set" data="default_password=123456"/>

然后输入:wq回车,保存退出

取消默认密码拨号延迟

如果你不想修改密码,可以将延迟密码得配置修改掉

找到文件/etc/freeswitch/dialplan/default.xml

<condition field="${default_password}" expression="^1234$" break="never">
    <action application="log" data="CRIT WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING "/>
    <action application="log" data="CRIT Open $${conf_dir}/vars.xml and change the default_password."/>
    <action application="log" data="CRIT Once changed type 'reloadxml' at the console."/>
    <action application="log" data="CRIT WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING "/>
    <action application="sleep" data="10000"/>
</condition>

找到最后一行<action application="sleep" data="10000"/>,将10000改为3000,退出保存,F6刷新

配置IP地址

这一步主要是解决,拨号成功之不然可能打得通电话,但是没有声音。

cd sip_profiles

找到external.xmlinternal.xml文件,将value字段修改为公网的IP地址,没公网写内网的

<param name="ext-rtp-ip" value="[external_ip]"/>
<param name="ext-sip-ip" value="[external_ip]"/>

改后

<param name="ext-rtp-ip" value="192.168.235.94"/>
<param name="ext-sip-ip" value="192.168.235.94"/>

至此,处于同一网络环境得两台分机互播就可以通话了,但是如果两台分机处于两个不通网段则没有声音

比如,freeswitch在公网,一台手机通过路由器连接freeswitch,一台手机通过4G链接服务器,它们之间可以互相拨打电话,但是接通之后没有声音。这是因为通过路由器连接freeswitch得分机,经过路由器后,IP地址做了转换。

所以最好改为

改后

<param name="ext-rtp-ip" value="autonat:192.168.235.94"/>
<param name="ext-sip-ip" value="autonat:192.168.235.94"/>

此外,还是这两个文件(external.xmlinternal.xml),找到<param name="local-network-acl" value="localnet.auto"/> 修改为 <param name="local-network-acl" value="rfc1918.auto"/>

由于我是在公司内网服务器测试的,所以一定要让网络管理员开放硬件防火墙的端口号,不然也是不通的,这个问题就让我郁闷很久

需要注意的时,内网分机连接服务器是会做两部鉴权的,第一步时账号密码是否正确,第二部时检查客户端的ip地址是否在自己的acl池中,放开ip地址访问权限,需要修改

cd autoload_configs
vim acl.conf.xml

在该标签中添加允许连接的安全域名

 <list name="domains" default="deny">
     <node type="allow" cidr="0.0.0.0/0"/>
 </list>

如果你不想放开所有的ip地址, 比如放开某一网段

<node type="allow" cidr="192.168.32.0/24"/>

放开某一固定的ip地址

<node type="allow" cidr="192.168.32.109/32"/>

里面的0,24和32是子网掩码转换为二进制后1的位数

至此,我们就扫清了所有的障碍,就可以通过客户端进行电话互联了

启动服务器

freeswitch #前台启动
freeswitch -nc #后台启动

freeswitch启动时会检测你的公网IP和内网IP并自动给你配置好。

系统默认会给你分配20个账号,分别是1000-1019;

客户端拨号

这个时候拿出客户端,比如Zoiper客户端配置

用户名:1000@118.190.209.12:5060
密码:123456
服务器:118.190.209.12:5060
其他都点skip

君语电话配置

SIP服务器: 118.190.209.12
用户名:1000
密码:123456
SIP端口号:5060
传输协议:TCP

网页端由于浏览器的安全限制会稍微麻烦一些,下面会专门讲解网页端的连接方法

当你都配置好了以后,发现还是链接不上,这是为什么呢,这是因为我们还少了两步,刚开始为了快速测试,可以先关闭系统的防火墙,再开启服务器的防火墙,比如去阿里云后台开放TCP、UDP的所有端口号

systemctl stop firewalld.service           #停止firewall
systemctl disable firewalld.service        #禁止firewall开机启动

如果生产环境,可以参考如下,将需要用的端口进行开放

FireWall PortsNetwork ProtocolApplication ProtocolDescription
1719UDPH.323 Gatekeeper RAS port
1720TCPH.323 Call Signaling
3478UDPSTUN service用于 NAT 穿透
3479UDPSTUN service用于 NAT 穿透
5002TCPMLP protocol server
5003UDPNeighborhood service
5060UDP & TCPSIP UAS用于 SIP 信令 (标准 SIP 端口,对于默认 ”Internal” 配置文件)
5070UDP & TCPSIP UAS用于 SIP 信令 (对于默认 “NAT” 配置文件)
5080UDP & TCPSIP UAS用于 SIP 信令 (对于默认 “External” 配置文件)
8021TCPESL用于 mod_event_socket
16384-32768UDPRTP/ RTCP multimedia streaming用于 SIP、交换以及其他协议的语音或视频的数据传输
5066TCPWebsocket用于 WebRTC
7443TCPWebsocket用于 WebRTC

接下来在试一下,就可以联通了,freeswitch提供了测试号码

号码说明
9664保持音乐
9196echo,回音测试
9195echo,回音测试,延迟5秒
9197milliwatte extension,铃音生成
9198TGML 铃音生成示例
5000示例IVR
4000听取语音信箱
33xx电话会议,48K(其中xx可为00-99,下同)
32xx电话会议,32K
31xx电话会议,16K
30xx电话会议,8K
2000-2002呼叫组
1000-1019默认分机号

自己测试的,freeswitch第一次启动,测试号码可以拨通,重启freeswitch后就拨不通了,但是不影响分机号码之间互播

网页端拨号

测试demo

先下载一个sip.js的demo sip.js + freeswitch 软电话(webRTC)demo

这个版本有点旧,但是亲测可以用

扫除浏览器安全限制

搭建https服务器

前端源文件下载完毕之后,接下来就可以启动一个服务器进行访问了,这个根据自己的使用习惯吧,只要能启动一个服务器即可。

我这列一下自己常用的方案,

一种是使用vscode插件live Server,另一种是使用了nodejs的一个插件browser-sync插件

这两种都可以生成https服务器,后者会更方便一些,不用考虑生成证书的事情,一行命令即可

browser-sync是一款浏览器同步工具,前端开发时编写html页面比较方便,现在有webpack等构建工具,这个基本不用这个了,这里我们只用他的生成服务器功能,启动时会关闭同步功能

先安装nodejs

然后安装插件

npm install -g browser-sync

在前端项目的根目录,打开cmd,并输入

browser-sync start --server --https --no-ghost-mode

意思时开启https服务器并关闭代码同步功能(幽灵模式)

这样我们就得到了一个https服务器了

设置白名单

接下来解决与服务器连接时,websocket的安全限制问题

在chrome地址栏输入

chrome://flags/#unsafely-treat-insecure-origin-as-secure

在里面填入,你要访问的freeswitch地址,比如

ws://192.168.235.94:5066

再把disabled改为enable

浏览器会有一个弹框,要求你重启浏览器,点击relaunch

手机同上,需要下载chrome浏览器,移动端不好测试,可以引入插件eruda.js方便调试查看日志

生成证书及配置

对于要发布上线的话,还得用域名和证书 官网教程

配置sip网关拨打外部电话

在这里我是在两个不通的服务器分别搭建了一套freeswitch服务,当然,freeswitch不仅能和freeswitch联通,还可以与其他电话服务器联通;

配置网关

首先在sip_profiles/external目录下创建一个xml文件,名字随便起,如gw_a.xml,同目录下有一个example.xml,复制一下里面的配置,然后在gw_a.xml文件中粘贴并修改如下

  <gateway name="gw_a">
  <!--/// account username *required* ///-->
  <param name="username" value="1000"/>
  <!--/// auth realm: *optional* same as gateway name, if blank ///-->
  <param name="realm" value="118.190.209.12"/>
  <!--/// username to use in from: *optional* same as  username, if blank ///-->
  <!--<param name="from-user" value="cluecon"/>-->
  <!--/// domain to use in from: *optional* same as  realm, if blank ///-->
  <!--<param name="from-domain" value="asterlink.com"/>-->
  <!--/// account password *required* ///-->
  <param name="password" value="123456"/>
  <!--/// extension for inbound calls: *optional* same as username, if blank ///-->
  <!--<param name="extension" value="cluecon"/>-->
  <!--/// proxy host: *optional* same as realm, if blank ///-->
  <!--<param name="proxy" value="asterlink.com"/>-->
  <!--/// send register to this proxy: *optional* same as proxy, if blank ///-->
  <!--<param name="register-proxy" value="mysbc.com"/>-->
  <!--/// expire in seconds: *optional* 3600, if blank ///-->
  <!--<param name="expire-seconds" value="60"/>-->
  <!--/// do not register ///-->
  <param name="register" value="true"/>
  <!-- which transport to use for register -->
  <!--<param name="register-transport" value="udp"/>-->
  <!--How many seconds before a retry when a failure or timeout occurs -->
  <!--<param name="retry-seconds" value="30"/>-->
  <!--Use the callerid of an inbound call in the from field on outbound calls via this gateway -->
  <!--<param name="caller-id-in-from" value="false"/>-->
  <!--extra sip params to send in the contact-->
  <!--<param name="contact-params" value=""/>-->
  <!-- Put the extension in the contact -->
  <!--<param name="extension-in-contact" value="true"/>-->
  <!--send an options ping every x seconds, failure will unregister and/or mark it down-->
  <!--<param name="ping" value="25"/>-->
  <!--<param name="cid-type" value="rpid"/>-->
  <!--rfc5626 : Abilitazione rfc5626 ///-->
  <!--<param name="rfc-5626" value="true"/>-->
  <!--rfc5626 : extra sip params to send in the contact-->
  <!--<param name="reg-id" value="1"/>-->
  </gateway>

这里面的username是第二台freeswitch中已经注册的用户名,password就是该用户的登录密码,realm是其服务器地址,可以是IP或者IP:Port,如果还需要其方面的要求,可以自行放开注释部分进行配置;

保存好以后,重启服务,freeswitch就会以1000用户的身份连接上第二台freeswitch服务器当中了

修改拨号规则

但是我们第一台freeswitch服务器中的用户如何给第二台freeswitch中的用户拨打电话呢?

这时候我们就需要修改拨号规则,通过在拨号中添加前缀,根据前缀将该拨号转发给我们刚刚建立的网关,然后让网关代理我们去拨打其他电话服务中的用户.举个例子,我们要给1001打电话,我们在1001前面加上0或9(数字随意),就是91001,拨打时发现以9开头,然后去掉9,把1001转发到gw_1.xml网关,网关拨打1001,接通后建立本机号码与外机号的连接与消息转发。

下面我们来配置这个拨号规则

打开/dialplan/public目录,(《freeswitch权威指南》说建在default目录下,但是我没有成功,但是在public目录下是可以的)创建call_out.xml,内容如下

<include>
  <extension name="callout">
    <condition field="destination_number" expression="^9(\d+)$">
      <action application="bridge" data="sofia/gateway/gw_a/$1"/>
    </condition>
  </extension>
</include>

其中^9(\d+)$是正则表达式,就是匹配到9以后,把9后面的数字\d+用$1代替传递到下面网关中,然后用bridge程序去拨打号码

重启服务器生效

拨打电话流程

先理解SIP Profile的几个概念

  • 一个Profile就是一个UA;一个UA就是一个“IP地址: 端口”;

  • fs的/sip_profiles目录下主要有三个东西(不考虑ipv6):external.xml、internal.xml、 /external目录。其中external.xml和internal.xml就是两个Profile;而/external里的xml文件是我们自定义的外部网关,这些外部网关都会被fs装入external.xml中;

  • internal.xml默认运行在5060端口,external.xml默认运行在5080端口。注意不要因为它们的名称而把它们理解为内部和外部。 60端口和80端口的区别就是前者会对sip消息鉴权而后者不需要(各自xml中的auth-calls标签定义)。

本地用户互拨流程

本地user/1000拨打本地user/1001:

  1. 因为user/1000注册在5060端口,所以向fs的5060端口发送INVITE请求;
  2. INVITE请求到达internal这个Profile所配置的UA(internal.xml);
  3. 此UA会对此INVITE请求进行鉴权(因为auth-calls=ture);
  4. 先检查ACL(acl.conf.xml),然后进行Digest鉴权(directory/default/1000.xml中的用户名和密码);
  5. 若鉴权通过则开始寻找路由,在1000.xml中的user_context标签即是路由;
  6. 这里以user_context的值是default为例,则进入diaplan/default.xml中寻找路由;
  7. diaplan/default.xml会找到1001这个用户,并执行bridge user/1001;
  8. bridge user/1001这个呼叫字符串会再次查找directory用户目录并找到directory/default/1001.xml;
  9. 因为1001是被叫,所以fs会进一步找到它实际注册位置,内部用户的实际注册位置在conf/directory/default.xml中dial-string标签配置。在这个标签中可以看到调用了sofia_contact这个API,这个API会查找数据库并找到user/1001的contact地址并返回真正的呼叫字符串。

本地用户拨打外部号码流程

本地user/1000拨打外部号码40012345:

  1. 因为user/1000注册在5060端口,所以向fs的5060端口发送INVITE请求;
  2. INVITE请求到达internal这个Profile所配置的UA(internal.xml);
  3. 此UA会对此INVITE请求进行鉴权(因为auth-calls=ture);
  4. 先检查ACL(acl.conf.xml),然后进行Digest鉴权(directory/default/1000.xml中的用户名和密码);
  5. 若鉴权通过后会找到该用户的配置文件(即1000.xml),在1000.xml中的user_context标签中配置了路由,所以fs会根据此配置进行路由查找:以默认配置为例:,此时进入diaplan/default.xml中寻找路由;
  6. 对于外部号码,default.xml中一般会将请求送到外部网关,例如:bridge sofia/gateway/gw1/40012345这样;
  7. 其中gw1是我们配置的一个网关。本文开头解释过,网关最终都会被装入external.xml,而external这个Profile运行在5080端口。因此,该INVITE请求最终会通过本机的5080端口发往gw1网关(在gw1对应的xml中配好了目的地的ip和端口)。

本地用户接听外部来电流程

外部送来的sip消息可能送到5080端口,也可能送到5060端口。

  • 如果送到5080端口:
  1. 外部的INVITE请求到达fs的5080端口,即external这个Profile,external不会对来话进行鉴权,直接进行路由;
  2. 在external.xml的context标签中配置了路由,以默认配置为例:,那么就会去diaplan/public.xml中查找路由,路由中设定了接下来的操作,是转到一个本地用户,还是继续bridge到其他地方;
  3. 如果是路由将请求转向一个本地用户(例如user/1000),那么将会找到这个用户的配置文件1000.xml,并根据1000.xml中user_context标签的配置继续进行路由(参考上面内部用户互拨流程);
  • 如果送到5060端口:
  1. 外部的INVITE请求到达fs的5060端口,即internal这个Profile,internal会对来话进行鉴权;
  2. 先ACL鉴权,再Digest鉴权。如果INVITE请求发送者的ip在autoload_configs/acl.conf.xml中配置过,则ACL鉴权通过,直接根据external.xml中context标签的配置进行路由;如果未通过ACL鉴权则进行Digest鉴权,如果通过Digest鉴权的认证就会找到一个内部用户(例如user/1000),进而根据其用户目录(即1000.xml)中user_context标签的配置进行路由。

Event Socket

freeswitch提供了一种不登录后台就可以对freeswitch进行管理的方式

开启event_socket,开放8021端口

配置autoload_configs/acl.conf.xml文件:

<list name="domains" default="deny">
  <!-- domain= is special it scans the domain from the directory to build the ACL -->
  <node type="allow" domain="$${domain}"/>
  <!-- use cidr= if you wish to allow ip ranges to this domains acl. -->
  <!-- <node type="allow" cidr="192.168.0.0/24"/> -->
  <!-- <node type="allow" cidr="192.168.168.0/24"/> -->
  <node type="allow" cidr="0.0.0.0/0"/>
</list>

配置autoload_configs/event_socket.conf.xml文件:

<configuration name="event_socket.conf" description="Socket Client">
  <settings>
    <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="domains"/>
    <!--<param name="apply-inbound-acl" value="loopback.auto"/>-->
    <!--<param name="stop-on-bind-error" value="true"/>-->
  </settings>
</configuration>

执行命令

reload mod_event_socket //更新配置

查看端口运行情况

netstat -apn | grep 8021

尝试telnet连接

telnet 192.168.235.96 8021

1659420058715.png

输入 auto ClueCue 然后按两下回车

输入

api version
api status
api sofia status
api uptime

快捷键

快捷键命令
F1help
F2status
F3show channels
F4show calls
F5sofia status
F6reloadxml
F7console loglevel 7
F8console loglevel 0
F9sofia status profile internal
F10sofia profile internal siptrace on
F11sofia profile internal siptrace off
F12version

常用命令

  1. freeswitch 启动

  2. freeswitch -nc 启动并后台运行

  3. freeswitch -stop 停止

  4. fs_cli -H 127.0.0.1 -P 8021 -p password 进入客户端

  5. /exit /bye /quit Ctrl+D 退出客户端

  6. sofia status profile local 查询网关状态

  7. sofia profile local restart 重启网关

  8. sofia status profile internal reg 查询注册用户数

  9. sofia status profile internal 查看当前注册的用户数量

  10. sofia profile external register gw 重新注册网关

  11. sofia global siptrace on 开启sip消息显示

  12. sofia global siptrace off 关闭sip消息显示

  13. originate user/1003 &echo 使用fs_cli进行呼叫,其中&echo会把听到的声音返回给发出者

  14. bgapi originate {absolute_codec_string=PCMU}user/1009 018162330576 xml default 音频编码部分设备不支持可以使用这个

  15. reloadxml 重新加载xml文件

  16. find / -name external -type d 查找文件夹

  17. echo a b c | xargs -n 1 cp -v ./gwl.xml 复制文件到多个文件夹

  18. ps aux | grep freeswitch 查看进程是否存在

  19. netstat -anp | grep freeswitch 查看freeswitch的端口情况

  20. netstat -apn | grep 8021 查看具体某个端口

  21. rpm -ql freeswitch 查看某应用的安装路径信息

freeswitch 命令

# freeswitch -help
Usage: freeswitch [OPTIONS]

These are the optional arguments you can pass to freeswitch:
	-nf                    -- no forking
	-reincarnate           -- restart the switch on an uncontrolled exit
	-reincarnate-reexec    -- run execv on a restart (helpful for upgrades)
	-u [user]              -- specify user to switch to
	-g [group]             -- specify group to switch to
	-core                  -- dump cores
	-help                  -- this message
	-version               -- print the version and exit
	-rp                    -- enable high(realtime) priority settings
	-lp                    -- enable low priority settings
	-np                    -- enable normal priority settings
	-vg                    -- run under valgrind
	-nosql                 -- disable internal sql scoreboard
	-heavy-timer           -- Heavy Timer, possibly more accurate but at a cost
	-nonat                 -- disable auto nat detection
	-nonatmap              -- disable auto nat port mapping
	-nocal                 -- disable clock calibration
	-nort                  -- disable clock clock_realtime
	-stop                  -- stop freeswitch
	-nc                    -- do not output to a console and background
	-ncwait                -- do not output to a console and background but wait until the system is ready before exiting (implies -nc)
	-c                     -- output to a console and stay in the foreground

	Options to control locations of files:
	-base [basedir]         -- alternate prefix directory
	-cfgname [filename]     -- alternate filename for FreeSWITCH main configuration file
	-conf [confdir]         -- alternate directory for FreeSWITCH configuration files
	-log [logdir]           -- alternate directory for logfiles
	-run [rundir]           -- alternate directory for runtime files
	-db [dbdir]             -- alternate directory for the internal database
	-mod [moddir]           -- alternate directory for modules
	-htdocs [htdocsdir]     -- alternate directory for htdocs
	-scripts [scriptsdir]   -- alternate directory for scripts
	-temp [directory]       -- alternate directory for temporary files
	-grammar [directory]    -- alternate directory for grammar files
	-certs [directory]      -- alternate directory for certificates
	-recordings [directory] -- alternate directory for recordings
	-storage [directory]    -- alternate directory for voicemail storage
	-cache [directory]      -- alternate directory for cache files
	-sounds [directory]     -- alternate directory for sound files

sofia

> sofia

USAGE:
--------------------------------------------------------------------------------
sofia global siptrace <on|off>
sofia        capture  <on|off>
             watchdog <on|off>

sofia profile <name> [start | stop | restart | rescan] [wait]
                     flush_inbound_reg [<call_id> | <[user]@domain>] [reboot]
                     check_sync [<call_id> | <[user]@domain>]
                     [register | unregister] [<gateway name> | all]
                     killgw <gateway name>
                     [stun-auto-disable | stun-enabled] [true | false]]
                     siptrace <on|off>
                     capture  <on|off>
                     watchdog <on|off>

sofia <status|xmlstatus> profile <name> [reg [<contact str>]] | [pres <pres str>] | [user <user@domain>]
sofia <status|xmlstatus> gateway <name>

sofia loglevel <all|default|tport|iptsec|nea|nta|nth_client|nth_server|nua|soa|sresolv|stun> [0-9]
sofia tracelevel <console|alert|crit|err|warning|notice|info|debug>

sofia help
--------------------------------------------------------------------------------

参考资料