《FreeSWITCH权威指南》解读

读《FreeSWITCH权威指南》一书的一些笔记

一些基础的命令

1
2
# 查看 internal Profile 上面的注册用户
freeswitch> sofia status profile internal reg

一些缩写的解释

  1. PBX 系统 :

什么是FreeSWITCH?

  • 开源的,跨平台的,伸缩性极好的,免费的,多协议的 电话软交换平台
    1. 伸缩性:从一个简单的软电话客户端 到 运营商级的软交换设备,几乎无所不能。
    2. 多协议的:支持 SIP,H323,Skype,Google Talk 等多种通信协议,并且可以与各种开源的 PBX 系统(例如:sipXecs,Call Weaver,Bayonne,YATE,Asterisk等)通信,也可以与商用的交换系统(如华为,中兴的交换机或者是思科,Avaya的交换机等)互通。
    3. FreeSWITCH 是一个 B2BUA(Back to Back User Agent),它作为一个背靠背的用户代理帮助通信双方进行实时的语音视频通行

如何安装 & 运行

安装

  • TODO

运行

FreeSWITCH启动参数

  • 虽然参数众多,用到的也没有几个
    1. freeswitch -nc : 启动到后台
    2. freeswitch -nonat : freeswitch默认启用uPnP(或NAT-PMP)协议,尝试进行打孔

判断 freeswitch 是否运行

  1. 查看进程 : ps aux | grep freeswitch
  2. 查看端口 : netstat -an | grep 5060

呼叫

  • FreeSWITCH 最基础的功能

发起呼叫

  • 假设用户1003已经注册到freeswitch上
  • 使用 originate命令 发起一次呼叫,然后执行echo回声程序
    1
    freeswitch> originate user/1003 &echo

呼叫字符串

  • 上个例子中 user/1003 称为呼叫字符串(Dial String,Call URL)
  • user 是一种特殊的呼叫字符串,后续介绍其他的呼叫字符串。
  • 命令 sofia status profile internal reg 可以查看已经注册的 UA 的注册信息
    UA状态

一些概念

  • 先来看一个场景:Bob 与 Alice 通话,典型的呼叫流程有如下两种

    1. Bob 向 FreeSWITCH 发起呼叫,FreeSWITCH 接着启动一个 UA 呼叫 Alice,两者通话。
    2. FreeSWITCH 同时呼叫 Bob 与 Alice,两者接电话之后,FreeSWITCH 将 a-leg 与 b-leg 桥接(bridge)到一起,两者通话。
  • 上述第一种呼叫流程中,Bob 到 FreeSWITCH 的通话称为来话(注意:在这儿都是针对FreeSWITCH来说的),而 FreeSWITCH 作为一个 B2BUA 再去呼叫 Alice,就称为去话。在第二种呼叫流程中两路通话(两个leg)都是去话。

  • 无论是来/去话,对每一次呼叫,FreeSWITCH都会启动一个 Session(会话,包括SIP会话),用于控制整个呼叫,它会一直持续到通话结束,其中,每个Session都控制着一个Channel(通道,又称信道),Channel是一对 UA 间通信的实体,相当于 FreeSWITCH 的一条腿,每个 Channel 都会用一个 uuid 标识。通话时,FreeSWITCH的作用是将两个Channel(a-leg 和 b-leg,通常先创建的/占主动的叫 a-leg)桥接到一起,使得双方可以通话。

  • 这两路桥接的通话(两条腿)在逻辑上组成一个通话,称为一个 Call


APP 与 API

  • 前面的例子中, originate 是 FreeSWITCH 内部的一个命令(command),用于控制 FreeSWITCH 发起一个呼叫。FreeSWITCH 的命令不仅可以在控制台上使用,也可以在各种 嵌入式脚本,Event Socket(fs_cli就是使用了ESL库) 或者 HTTP RPC 上使用,所有的命令都遵循一个抽象的接口,因而这些命令又称 API Commands。

  • echo 则是一个常用的应用程序(Application,APP),它的作用是控制一个 Channel 的一端。一个 Channel 有两端(呼叫方,被呼叫方)。

  • 使用echo时。电话接通后相当于呼叫方在和 echo通话。

  • 后面我们会说到,他们实际上组成了 FreeSWITCH 的一条腿(leg),这种童话称为“单腿模式(one-legged connection)”。

  • 其他的一些的 app 的使用

    1
    2
    3
    4
    freeswitch> originate user/1003 &park
    freeswitch> originate user/1003 &hold
    freeswitch> originate user/1003 &playback(/root/welcome.wav)
    freeswitch> originate user/1003 &record(/tmp/voice_of_1003.wav)
  • 当我们初始化一个呼叫时候,在1003接电话之后对端必须有一个人与之对话(否则,一个Channel只有一端)

    • park 可以将该电话“挂起”,相当于 Channel 特殊的一端。park 的用户体验并不好,因为1003不知道等多久才有人接电话,由于听不到任何声音,用户会奇怪到底有没有接通。
    • hold 相对比较友好,它在等待的同时会播放音乐。
    • playback app 可以直接播放一个特定的声音文件
    • record app 可以直接录音
  • 上面的例子其实都是建立一个Channel,相当于 FreeSWITCH 作为一个 UA 和 1003 进行通话。它是个一条腿(只有a-leg)的通话。大多数情况之下, FreeSWITCH 都是作为一个B2BUA来桥接两个UA进行通话的。在A接听电话以后,bridge程序可以再启动一个UA呼叫 B,如下:

    1
    freeswitch> originate user/a &bridge(/user/b)
  • 这样就可以让a与b进行通话了。我们也可以使用另外一种方式建立他们之间的通话:

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    freeswitch> originate user/a  &park
    freeswitch> originate user/b &park
    freeswitch> show channels
    freeswitch> uuid_bridge <a_uuid> <b_uuid>
    ```
    - 在这,分别呼叫 a & b,并把他们暂时park到一个地方。通过 `show channels` 方法得到 channel UUID,然后通过 `uuid_bridge` 命令将两个 Channel 桥接起来。

    - 上面我们简单介绍了
    - 两条命令(API):originate & uuid_bridge
    - 几个程序(APP):echo & park & bridge
    - uuid_bridge API 和 bridge APP 有些类似。一个是先呼叫后桥接,另外一个是先桥接后呼叫,到底有啥区别?

    - **简单来说:一个APP是一个程序(application),它作为一个Channel一端与另一端的UA进行通信,相当于它工作在Channel内部;而一个API则是独立于一个Channel之外的,它只能通过找到Channel的UUID来控制一个Channel(如果需要),相当于一个第三者。**这就是他们的本质区别。

    - 通常控制台输入的命令都是 API,而在 dialplan 中执行的程序都是 APP(dialplan中也可以执行一些特殊的API)。大部分共用的API都是在mod_commands模块中加载的,而APP则是在mod_dptools中,因而 App 又称为拨号计划工具(DialPlan Tools)。某些模块(如:mod_sofia)有自己持有的API和APP。

    ---

    # FreeSWITCH 内部架构
    - FreeSWITCH 由一个稳定的核心(Core)及一些外围模块组成。这些外围的模块根据其功能&用途分成:EndPoint,Codec,Dialplan,Application等不同的类别,如图所示:
    ![FreeSWITCH架构图](/images/posts/rtc-freeswitch/freeswitch-architecture.png)

    - 其中,数据库的代码是在核心中实现的,但是上图中将其列出来是因为从逻辑上它是独立的。

    - FreeSWITCH 内部使用线程模型来处理并发请求,每个连接都是在单独的线程中进行处理,不同线程之间通过 Mutex 互斥访问共享资源,并通过消息和异步事件等方式进行通信。

    - FreeSWITCH的核心非常短小精悍,绝大部分应用层的功能都是在外围模块中实现。外围模块是可以动态加载(以及卸载)的,在实际应用中可以只加载用到的模块。

    - 外围模块通过核心提供的 Public API 与核心进行通信,而核心则通过回调(或称钩子)机制执行外围模块中的代码。

    - TODO :再看看书
    - 具体的可以去看书了

    ---

    # 拨号计划
    - Dialplan,主要作用就是对电话进行**路由**(类似路由表),决定 & 影响通话流程。

    - XML Dialplan 由一系列的 XML 配置文件组成,可以是静态配置的,也可以使用动态配置方式从其他服务器或者脚本中动态获取。Dialplan 又特定的结构, FreeSWITCH 通过解析相关机构,可以对 Dialplan 进行路由的呼叫,决定执行何种动作或者流程。

    ## 配置文件结构
    - 默认配置在 conf/dialplan 目录中。
    - 看一个 Dialplan 的完整结构:
    ```xml
    <extension name="Echo Test">
    <!-- destination_number :被呼叫的号码 -->
    <condition field="destination_number" expression="^echo|1234$">
    <!-- action 两个参数的含义:app + app 运行的参数 -->
    <action application="answer" data=""/>
    <action application="echo" data=""/>
    </condition>
    </extension>
  • extension 类似于路由表中的表项,每个 extension 都有一个 name 属性,可以为任意合法字符串,对呼叫流程没有任何影响,去一个有含义的名字,有助于在Log中找到。

  • extension 中可以对一些 condition(测试条件) 进行判断,如果满足测试条件所指定的表达式,则执行对于的 action

  • 拿上面的例子看,condition(测试条件)destination_number ,表示被呼叫的号码, FreeSWITCH 会将呼入电话的号码与后面的正则表达式 ^echo|1234$ 比较,如果满足条件,则执行后面的 action。

  • 第一个 action 为 answer,他是一个 app,用于对来话进行应答(由于是sip呼叫,底层返回200 ok),然后执行 echo。在这个例子中,我们呼叫1234创建了一个单腿的呼叫,与其说我们在和 FreeSWITCH 通话,还不如说在和一个 APP 通话。而 Dialplan 只是帮助我们找到这些个 APP。

  • 系统默认提供的配置文件中又几个 context : default,features,public,skinny-patterns。他们分别在不同的XML配置文件中。

    1. default 是默认的 dialplan,一般来说注册用户都可以通过它来打电话,如拨打其他分机或者外部电话。
    2. public 一般用于接受外来呼叫,因为外部进来的呼叫是不可信的,所以要严格控制。

通道变量

  • TODO
  • 除了通道变量之外还有一些其他的东西,后续补齐

SIP 模块

  • Session Initiation Protocol : 控制发起、修改和终结交互式多媒体会话的信令协议。

  • 它是一个对等的协议,类似于P2P。它和 HTTP 不一样,不是CS架构。也不像传统电话那样必须要有一个中心的交换机,它可以在不需要服务器的情况下进行通信。即点对点的通信。

  • 一个 Profile 就类似一个 SIP UA