Использование Event Socket Library в Freeswitch.
Различные практики.
Общие сведения.
Модуль mod_event_socket представляет собой интерфейс управления FreeSWITCH
По умолчанию прослушивается локальный интерфейс 127.0.0.1, порт 8021.
Пароль для доступа по умолчанию "ClueCon".
Модуль работает в двух режимах inbound и outbound.
В inbound режиме мы подключаемся к Freeswitch с помощью скриптов и выдаем команды / получаем события. В outbound режиме мы должны иметь скрипт-демон, к которому будет подключаться Freeswitch при обработке вызовов.
Включаем возможность работы с сокетами.
# nano /usr/local/freeswitch/conf/autoload_configs/modules.conf.xml
Следующая строчка должна быть раскомментирована:
<load module="mod_event_socket"/>
Затем открываем файл непосредственного конфигурации модуля mod_event_socket:
# nano /usr/local/freeswitch/conf/autoload_configs/event_socket.conf.xml
Конфиг для приема всех соединений с любого интерфейса на порт 8021 с паролем ClueCon выглядит так:
<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="loopback.auto"/>-->
<!--<param name="stop-on-bind-error" value="true"/>-->
</settings>
</configuration>
Рестартуем систему:
# systemctl restart freeswitch.service
Убеждаемся, что порт 8021 слушается системой:
# netstat -ltupn | grep freesw
tcp 0 0 0.0.0.0:8021 0.0.0.0:* LISTEN 9543/freeswitch
tcp 0 0 10.200.16.214:5060 0.0.0.0:* LISTEN 9543/freeswitch
udp 0 0 10.200.16.214:5060 0.0.0.0:* 9543/freeswitch
Не забываем в Iptables прописать дополнительные ip адреса для доступа к сокетам Freeswitch по сети.
Проверяем работу сокетов в режиме inbound с использованием скриптов на PHP.
Для проверки, что сокеты работают, создадим скрипт и запустим его:
# nano /var/www/html/callyshek.php
Содержание файла:
<?php
date_default_timezone_set('Etc/GMT-3');
$password = "ClueCon";
$port = "8021";
$host = "127.0.0.1";
function event_socket_create($host, $port, $password) {
$fp = fsockopen($host, $port, $errno, $errdesc)
or die("Connection to $host failed");
socket_set_blocking($fp,false);
if ($fp) {
while (!feof($fp)) {
$buffer = fgets($fp, 1024);
usleep(100); //allow time for reponse
if (trim($buffer) == "Content-Type: auth/request") {
fputs($fp, "auth $password\n\n");
break;
}
}
return $fp;
}
else {
return false;
}
}
function event_socket_request($fp, $cmd) {
if ($fp) {
fputs($fp, $cmd."\n\n");
usleep(100); //allow time for reponse
$response = "";
$i = 0;
$contentlength = 0;
while (!feof($fp)) {
$buffer = fgets($fp, 4096);
if ($contentlength > 0) {
$response .= $buffer;
}
if ($contentlength == 0) { //if contentlenght is already don't process again
if (strlen(trim($buffer)) > 0) { //run only if buffer has content
$temparray = split(":", trim($buffer));
if ($temparray[0] == "Content-Length") {
$contentlength = trim($temparray[1]);
}
}
}
usleep(100); //allow time for reponse
//optional because of script timeout //don't let while loop become endless
if ($i > 10000) { break; }
if ($contentlength > 0) { //is contentlength set
//stop reading if all content has been read.
if (strlen($response) >= $contentlength) {
break;
}
}
$i++;
}
return $response;
}
else {
echo "no handle";
}
}
$fp = event_socket_create($host, $port, $password);
$cmd = "api help";
//$cmd = "show channels";
$response = event_socket_request($fp, $cmd);
echo $response;
fclose($fp);
?>
Запускаем файл так:
# php -f /var/www/html/callyshek.php
На выход должны получить справочную информацию описания api Freeswitch
Valid Commands:
...,,Shutdown,mod_commands
acl,<ip> <list_name>,Compare an ip to an acl list,mod_commands
alias,[add|stickyadd] <alias> <command> | del [<alias>|*],Alias,mod_commands
banner,,Return the system banner,mod_commands
bg_system,<command>,Execute a system command in the background,mod_commands
bgapi,<command>[ <arg>],Execute an api command in a thread,mod_commands
break,<uuid> [all],uuid_break,mod_commands
cdr_csv,parameters,cdr_csv controls,mod_cdr_csv
chat,<proto>|<from>|<to>|<message>|[<content-type>],chat,mod_dptools
coalesce,[^^<delim>]<value1>,<value2>,...,Return first nonempty parameter,mod_commands
complete,add <word>|del [<word>|*],Complete,mod_commands
cond,<expr> ? <true val> : <false val>,Evaluate a conditional,mod_commands
… <………> …
xml_locate,[root | <section> <tag> <tag_attr_name> <tag_attr_val>],Find some xml,mod_commands
xml_wrap,<command> <args>,Wrap another api command in xml,mod_commands
Библиотека Event Socket Library (ESL)
Библиотека Event Socket Library (ESL) предназначена для использования взаимодействия с Freeswitch в различных языках программирования.
Сборка базовой библиотеки Event Socket Library (ESL):
# yum install libxml2-devel pcre-devel bzip2-devel curl-devel gmp-devel aspell-devel libtermcap-devel gdbm-devel db4-devel
# cd /usr/local/src/freeswitch/libs/esl/
# make
Затем для включения модулей для конкретных языков программирования выполняем:
Для языка php:
# yum install php-devel
# make phpmod-install
ПРИМЕЧАНИЕ:
Если во время установки возникнет следующая ошибка
esl_wrap.cpp:730:18: фатальная ошибка: zend.h: Нет такого файла или каталога
#include "zend.h"
^
компиляция прервана.
make[1]: *** [esl_wrap.o] Ошибка 1
make[1]: Выход из каталога `/usr/local/src/freeswitch/libs/esl/php'
make: *** [phpmod] Ошибка 2
Необходимо выполнить следующее:
# cd /usr/local/src/freeswitch
# ./configure
Затем повторяем:
# cd /usr/local/src/freeswitch/libs/esl/
# make phpmod-install
Теперь сборка происходит без ошибок.
Теперь после инсталляции ESL для PHP
Конфигурация библиотеки здесь - /etc/php.d/esl.ini
Модуль ESL прописался в /usr/lib64/php/modules и /usr/share/pear
Файл библиотеки здесь - /usr/share/pear/ESL.php
Его можно использовать в разработке программ ESL.php
Для языка python:
# yum install python-devel
# make pymod-install
Модуль PYTHON прописался в /usr/lib64/python2.7/site-packages/ESL.py
В заключении рестартуем apache:
# systemctl restart httpd.service
Проверяем работу сокетов в режиме inbound с использованием скриптов на PHP и библиотеки ESL.
Для проверки работы библиотеки ESL в языке php создаем файл /var/www/html/callyshek2.php
# touch /var/www/html/callyshek2.php
Его содержимое будет такое:
<?php
require_once('ESL.php');
if ($argc > 1) {
array_shift($argv);
$command = sprintf('%s', implode(' ', $argv));
printf("Command to run is: %s\n", $command);
$sock = new ESLconnection('127.0.0.1', '8021', 'ClueCon');
$res = $sock->api($command);
printf("%s\n", $res->getBody());
} else {
printf("ERROR: You Need To Pass A Command\nUsage:\n\t%s <command>", $argv[0]);
}?>
Запуск для проверки:
# php -f /var/www/html/callyshek2.php status
На выходе должны получить что-то такое:
Command to run is: status
UP 0 years, 0 days, 2 hours, 13 minutes, 46 seconds, 695 milliseconds, 497 microseconds
FreeSWITCH (Version 1.6.9 git d574870 2016-06-13 18:10:44Z 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.80
Current Stack Size/Max 240K/8192K
Проверка работы Freeswitch в режиме outbound.
В качестве сокет-сервера для проверки будет выступать утилита netcat.
Для того что бы передать управление внешнему серверу и использовать режим outbound прописываем в плане набора:
<extension name="call_in">
<condition field="destination_number" expression="^(8459938103)$|^(9938103)$">
<action application="socket" data="10.200.16.214:8022 async full"/>
</condition>
</extension>
Порт 8021, указанный в конфигурации мы использовать не можем, так как этот порт уже занят самим Freeswitch и не может выступать в качестве порта сокет-сервера. Используем здесь порт 8022.
Затем проводим рестарт:
# systemctl restart freeswitch
Запускаем сервер сокет на порту 8022 и ждем:
# nc -k -v -l 10.200.16.214 8022
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on 10.200.16.214:8022
При вызове на номер 8459938103 Freeswitch передаст управление сокет-серверу:
Ncat: Connection from 10.200.16.214.
Ncat: Connection from 10.200.16.214:39806.
Вводим в консоли команду connect и два раза ENTER:
# connect\n\n
Получаем ответ от сервера с данными о поступившем вызове:
Event-Name: CHANNEL_DATA
Core-UUID: 77af8caf-af88-42ab-81ed-b7287794de01
FreeSWITCH-Hostname: callpeg.svttk.ru
FreeSWITCH-Switchname: callpeg.svttk.ru
FreeSWITCH-IPv4: 10.200.16.214
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2016-07-13%2017%3A11%3A07
Event-Date-GMT: Wed,%2013%20Jul%202016%2014%3A11%3A07%20GMT
Event-Date-Timestamp: 1468419067842618
Event-Calling-File: mod_event_socket.c
Event-Calling-Function: parse_command
Event-Calling-Line-Number: 1996
….<….>….
variable_endpoint_disposition: DELAYED%20NEGOTIATION
variable_DP_MATCH: ARRAY%3A%3A8469938103%7C%3A8469938103
variable_call_uuid: ae37202f-c4ab-41dd-b109-8fde03c56223
variable_current_application_data: 10.200.16.214%3A8022%20async%20full
variable_current_application: socket
variable_socket_host: 10.200.16.214
Content-Type: command/reply
Reply-Text: %2BOK%0A
Socket-Mode: async
Control: full
Значит все работает.
Проверка работы Freeswitch в режиме outbound с использованием сокет-сервера на PYTHON.
Создадим файл servcall.py на основе примера, имеющегося в дистрибутиве ESL:
/usr/local/src/freeswitch/libs/esl/python/server.py
Содержимое файла:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import SocketServer
from ESL import *
class ESLRequestHandler(SocketServer.BaseRequestHandler ):
def setup(self):
print self.client_address, 'connected!'
fd = self.request.fileno()
print fd
con = ESLconnection(fd)
print "Connected: ", con.connected()
if con.connected():
info = con.getInfo()
uuid = info.getHeader("unique-id")
print uuid
con.execute("answer", "", uuid)
con.execute("playback", "ttk/fon.wav", uuid);
#server host is a tuple ('host', port)
server = SocketServer.ThreadingTCPServer(('10.200.16.214', 8022), ESLRequestHandler)
server.serve_forever()
В этом файле описывается логика работы сервера. Сервер запускается и прослушивает Ip 10.200.16.214 и порт 8022. При установлении соединения, запрашивается информация о соединении и серверу Freeswitch дается команда проигрывать абоненту звуковой файл ttk/fon.wav.
Файл должен быть помещен в папку: /usr/local/freeswitch/sounds/en/us/callie/ttk/ (папка ttk должна быть создана).
Для работоспособности схемы, необходимо запустить сервер командой:
# ./servcall.py
Вывод событий Freeswitch в stdout linux, используя ESL и PYTHON и подключаясь к Freeswitch в режиме inbound.
В дистрибутиве ESL Freeswitch существует скрипт-сервер, написанный на PYTHON, который подключается к серверу, подписывается на события и выводит все поступившие события в консоль. Скрипт лежит здесь - /usr/local/src/freeswitch/libs/esl/python/events.py
На его основе создадим свой файл-скрипт-сервер events2.py.
Содержимое файла:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import string
import sys
from ESL import *
con = ESLconnection("127.0.0.1","8021","ClueCon")
#are we connected?
if con.connected:
con.events("plain", "all");
while 1:
#my $e = $con->recvEventTimed(100);
e = con.recvEvent()
if e:
print e.serialize()
При запуске этого скрипта все, произойдет подключение к Freeswitch на ip 127.0.0.1 и порт 8021. Устанавливается режим получения сообщений – plain. Параметр all говорит о том, что мы хотим получать все сообщения. Затем все полученные события будут выводиться в консоль.
Пример:
#./events2.py
При поступлении вызова мы увидим множество сообщений:
Event-Name: HEARTBEAT
…
Event-Name: RE_SCHEDULE
…
Event-Name: CHANNEL_STATE
…
Event-Name: CHANNEL_CALLSTATE
…
Event-Name: PRESENCE_IN
…
Event-Name: CHANNEL_EXECUTE
…
Event-Name: API
…
Event-Name: CHANNEL_OUTGOING
…
Event-Name: CHANNEL_CREATE
…
Event-Name: CHANNEL_ORIGINATE
…
Event-Name: CHANNEL_PROGRESS
…
Event-Name: CHANNEL_HANGUP
…
Event-Name: CHANNEL_EXECUTE_COMPLETE
…
Event-Name: CHANNEL_HANGUP_COMPLETE
…
Event-Name: CHANNEL_DESTROY
…
Комментариев нет:
Отправить комментарий