1.准备工作 && 编译
首先,需要安装一些最基本的依赖库。以Ubuntu为例:
sudo apt-get install uuid-dev libjansson-dev libxml2-dev unixodbc-dev libmyodbc
然后我们从Asterisk的官网 获取其最新的代码
wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-14-current.tar.gz
然后解压缩
tar zxvf asterisk-14-current.tar.gz
进入代码目录,进行环境配置、编译和安装
./configure --prefix=$(pwd)/../build/ --with-pjproject-bundled
make && make install && make sample
到这里Asterisk就已经安装好了,但是要经过配置才可以使用。
2.听见HelloWorld
我们在这里先配置一个最简单的测试环境,又一台手机进行拨号,系统会自动接通这一路通话,稍等5秒后播放一段HelloWorld的语言,然后自动挂断。
首先进入Asterisk安装目录下的etc子目录,修改以下文件的内容:
extensions.conf
[hello]
exten = 100,1,Answer()
same = n,Wait(5)
same = n,Playback(hello-world)
same = n,Hangup()
pjsip.conf
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
[6001]
type=endpoint
context=hello
disallow=all
allow=ulaw
auth=6001
aors=6001
[6001]
type=auth
auth_type=userpass
password=testpwd
username=6001
[6001]
type=aor
max_contacts=1
这里简单的解释一下这两个文件的含义,extensions.conf描述了Asterisk在接到呼叫以后根据被叫号码所决定的行为,这在Asterisk中被称为Dailplan。这份Dailplan内容很简单的,只包含了一个被称为hello的Dailplan,当接到被叫号码为100的呼叫后自动接听,等待5秒,然后播放一个名叫hello-world的语言文件,然后挂断。
pjsip.conf是对应PJSIP插件的配置文件。Asterisk的插件分为很多类型,PJSIP插件属于channel类型,用来最终实现和终端的交互。PJSIP是当前Asterisk主要支持的SIP协议Channel插件。这份pjsip.conf包含了四个块。第一块,transport-udp,这一部分描述了pjsip使用udp与外部通信,并制定了监听消息的地址。后面三个块都叫做6001,分别从终端属性(Endpoint),登录信息(Auth)和授权信息(Aor)三个角度描述一个设备。Endpoint主要表述终端支持的协议,以及适用哪一个Auth和Aor信息。Auth描述了系统如何验证用户。Aor限制了用户最多可以在系统中同时登录几次。
到这里,对Asterisk就告一段落,执行下面的命令启动Asterisk
./asterisk -cvvvvv
下面我们需要对终端进行配置。
当前市面上有各种各样的SIP终端,包括各个平台上的软件和各种硬件,在这里我们建议使用Windows或者Android上的Linphone进行测试,本文中所有的配置都使用Android上的Linphone进行过测试。Linphone的官网在这里 。
打开Android上的Linphone,如果你是第一次打开Linphone的话,选择Use SIP Account,根据上面的配置输入用户名、密码和服务器地址,并选择Transport为UDP,如下图所示,然后点击Login。
如果看到下图一样的界面,注意界面左上角显示为绿色的已注册(Registered),就说明你已经成功的登录了Asterisk服务器,同时在服务器的命令行上也会有显示。
这时,可以尝试使用拨号盘给100打电话,看看能不能听到Hello World的声音。
一般情况下,会出现的情况是电话可以正确的接通和挂断,但是听不到声音。出现这一现象的原因是,我们的终端处于NAT内网中,而我们使用的又是UDP传输,在没有进行特殊配置的情况下,语音会被NAT所阻挡,所以会听不到声音。
NAT的组网方式多种多样,同样的配置不可能通吃所有情况,通常我们会在pjsip.conf的Endpoint块中增加如下两行,可以解决大多数由于NAT引起的语音不通。
rtp_symmetric=yes
direct_media=no
前一行强制Asterisk与终端交互时,在语音上行和下行时使用同样的UDP端口。后一行则不允许终端间以P2P的形式进行通信,强制通过Asterisk中转。由于我们的服务器通常配置在拥有公网IP的云主机上,这样的配置就可以解决大多数由于NAT引起的问题。
现在再试一试,是不是可以听到Hello World的声音了?
3.第一次接触
作为一个网络电话系统,最重要的莫过于实现两个终端间的通话,在这一部分我们就来改进我们的配置,实现这一功能。
修改pjsip.conf,增加以下内容:
[3001]
type=endpoint
rtp_symmetric=yes
context=sipcall
message_context=sipsms
disallow=all
allow=opus,h264
auth=3001
aors=3001
[3001]
type=auth
auth_type=userpass
password=pwd3001
username=3001
[3001]
type=aor
max_contacts=1
[3002]
type=endpoint
rtp_symmetric=yes
context=sipcall
message_context=sipsms
disallow=all
allow=opus,h264
auth=3002
aors=3002
[3002]
type=auth
auth_type=userpass
password=pwd3002
username=3002
[3002]
type=aor
max_contacts=1
修改extensions.conf,增加以下内容:
[sipcall]
exten = _3XXX,1,Answer()
same = n,Dial(PJSIP/${EXTEN})
same = n,Hangup()
[sipsms]
exten = _3XXX,1,NoOp(SMS receiving dialplan invoked)
same = n,NoOp(To ${MESSAGE(to)})
same = n,NoOp(From ${MESSAGE(from)})
same = n,NoOp(Body ${MESSAGE(body)})
same = n,Set(ACTUALTO=${CUT(MESSAGE(to),@,1)})
same = n,MessageSend(${ACTUALTO},${MESSAGE(from)})
same = n,NoOp(Send status is ${MESSAGE_SEND_STATUS})
same = n,Hangup()
分别解释一下新增的两部分配置文件。对pjsip.conf的修改很简单,允许两个新的终端接入,同时为其的呼叫和短信都指定了相应的Dailplan。extensions.conf中则详细的描述了sipcall和sipsms两个Dailplan。其中类似_3XXX的写法是Asterisk特有的类似于正则表达式的匹配语法,用来匹配数字号码。以$开头的则是Dailplan中的参数。Dailplan中的NoOp函数没有任何实际的作用,通常用来辅助Debug。
修改好两个配置文件之后,重新启动Asterisk,使用新的号码登录设备,就可以测试一下电话和短信功能了。
4.变得更专业
到这里,我们已经基本实现了网络电话系统最基础的功能。但是我们会发现一个问题,每当我们需要增加一个新的用户的时候,都需要修改配置文件,然后重启Asterisk。这样的操作不但流程复杂,易于出错,还使得Asterisk需要频繁重启,不能连续的提供服务。这对于一个通信系统来说可是致命的弱点。
好在,作为一个商业级的SIP服务器,Asterisk提供了Realtime功能来解决这一问题。Realtime功能可以将pjsip.conf、extensions.conf中的配置信息存入数据库,并在合适的时机从数据库中查询。使得终端的增减等简单的操作可以不需要重启Asterisk,只需要修改数据库中的数据就可以完场。下面,我们就将当前pjsip.conf中记录的终端信息迁移到数据库中。
Asterisk使用UnixODBC来实现对多种数据库的支持,在这里我们选择MySQL来存储Asterisk所需要的信息。首先我们需要配置UnixODBC。
在最开始,我们已经在系统中安装了UnixODBC和其MySQL支持。然后需要修改其配置文件。
首先是/etc/odbcinst.ini,内容如下:
[MySQL]
Description = ODBC for MySQL
Driver = /usr/lib/x86_64-linux-gnu/odbc/libmyodbc.so
Setup = /usr/lib/x86_64-linux-gnu/odbc/libodbcmyS.so
UsageCount = 2
然后是/etc/odbc.ini,内容如下:
[asterisk]
Driver = MySQL
Description = MySQL for Asterisk
Server = 【服务器地址】
Port = 【服务器端口】
Database = 【数据库名】
UserName = 【用户名】
Password = 【密码】
Socket = /var/run/mysqld/mysqld.sock
接下来是修改Asterisk的配置,首先是res_odbc.conf,增加或修改以下内容:
[asterisk]
enabled => yes
dsn => asterisk
username => 【用户名】
password => 【密码】
pre-connect => yes
然后是modules.conf,增加或修改以下内容:
preload => res_odbc.so
preload => res_config_odbc.so
接下来是extconfig.conf,增加或修改以下内容:
[settings]
ps_endpoints => odbc,asterisk
ps_auths => odbc,asterisk
ps_aors => odbc,asterisk
ps_domain_aliases => odbc,asterisk
ps_endpoint_id_ips => odbc,asterisk
ps_contacts => odbc,asterisk
紧接着是sorcery.conf,增加或修改以下内容:
[res_pjsip] ; Realtime PJSIP configuration wizard
endpoint=realtime,ps_endpoints
auth=realtime,ps_auths
aor=realtime,ps_aors
domain_alias=realtime,ps_domain_aliases
contact=realtime,ps_contacts
[res_pjsip_endpoint_identifier_ip]
identify=realtime,ps_endpoint_id_ips
最后是pjsip.conf,删除除了transport-udp块以外的其他内容。
接下来我们就需要对数据库进行配置了,Asterisk已经给我们提供了数据库中表的的结构模版,放在源代码目录下的contrib\realtime\中,根据不同的数据库类型存放在不同的文件夹中,每个文件夹中又包含了cdr、config和voicemail三个文件,在这里我们只需要导入config文件就可以。
接下来我们就可以向数据库中插入用户的数据,需要操作的有ps_aors、ps_auths、ps_endpoints,一个可以参考的操作如下面6条SQL语句表示。
insert into `ps_aors` (id,max_contacts) values ('3001','1')
insert into `ps_aors` (id,max_contacts) values ('3002','1')
insert into `ps_endpoints` (id,transport,aors,auth,context,disallow,allow,direct_media,rtp_symmetric,message_context) values ('3001', 'transport-udp', '3001', '3001', 'sip', 'all', 'opus,h264', 'no','yes')
insert into `ps_endpoints` (id,transport,aors,auth,context,disallow,allow,direct_media,rtp_symmetric,message_context) values ('3002', 'transport-udp', '3002', '3002', 'sip', 'all', 'opus,h264', 'no','yes')
insert into `ps_auths` (id,auth_type,password,username) values ('3001', 'userpass','pwd3001','3001')
insert into `ps_auths` (id,auth_type,password,username) values ('3002', 'userpass','pwd3002','3002')
完成以上所有的修改以后,我们就可以重启Asterisk。首先,查看一下数据库中的Endpoints是否已经被Asterisk所识别。在Asterisk CLI中使用下面的命令列出当前系统中的所有Endpoints。
pjsip show endpoints
通常情况下,我们应该可以看到数据库中所添加的终端,如下图所示。然后我们就可以使用终端进行登录、测试。
为了体现Realtime配置与文件配置的区别,我们在数据库中增加一个终端,在填写玩所有需要的数据后,无需Reload Asterisk,我们就可以直接使用终端登录Asterisk。
5.还可以怎么玩
以上介绍了Asterisk最基础的几个应用,Asterisk作为一个成功的商业开源通信服务器,其功能多种多样,既可以实现简单的电话软交换,也可以实现如电话会议、语言留言、呼叫中心等多种多样的商用电话功能。甚至有小规模的电信运营商将其作为交换中心的基础。因此,使用其来构建网络电话系统,是十分恰当的选择。