войти зарегистрироваться

IP-телефония

индекс
102,70

Asterisk: обработка регистрационных событий на сервере на примере взаимодействия с VoIP-сервисом «Мультифон» из песочницы

Здравствуй, Хабрахабр! В этой статье я хочу поведать о том, как можно решить вопрос автоматического переключения режима приёма звонков в «Мультифоне» (gsm-to-gsm/gsm-to-sip) при регистрации смартфона на сервере Asterisk. Ниже будет рассказано, для чего мне это понадобилось, какие варианты решения рассматривались, и как в итоге было реализовано. Приведённый ниже пример был использован на базе домашнего сервера под управлением Debian Lenny вместе с Asterisk 1.6, но с большой вероятностью будет работать и на других распространённых Linux-платформах.

Вкратце о предыстории вопроса


Являясь абонентом Мегафона, с недавнего времени стал пользоваться их VoIP-сервисом. Одной из особенностей сервиса является то, что кроме совершения исходящих, можно принимать входящие на мобильный номер звонки через SIP-клиент. Этой возможностью мне и захотелось воспользоваться, настроив подключение своего смартфона к домашнему серверу Asterisk через SIP. На самой процедуре регистрации, подключении к услуге и настройке SIP-аккаунта, я останавливаться не буду — про это уже достаточно написано. Хотелось сделать следующее: при подключении смартфона к серверу, все входящие сотовые звонки начинают поступать через SIP; при отключении от сервера должен восстанавливаться обычный режим приёма звонков телефоном (gsm-to-gsm). Решение должно быть максимально автоматизированным и требовать от меня не более одного движения — нажатия на кнопку подключения к серверу на рабочем столе смартфона.

Что необходимо для выполнения задачи


Для реализации задуманного необходимо выполнение как минимум двух условий:
1. «Мультифон» должен предоставлять возможность переключения режима приёма сотовых звонков: приём на сотовый, приём в sip-клиент. Такая возможность у сервиса есть, и реализована она через отправку определённого https-запроса на сервер (подробнее о формате запроса можно прочитать перейдя по ссылке, указанной в конце статьи). Запрос можно выполнить из консоли (например при помощи curl или wget), что делает возможным его использование в скриптах.
2. Сервер должен «знать», что на Asterisk'e зарегистрировался определённый peer (смартфон), и при подключении/отключении выполнить https-запрос. Подробнее рассмотрим это вопрос ниже.

В поисках решения


Необходимо определить момент, когда смартфон зарегистрируется на сервере. Изучив реакцию Asterisk'a на подключение клиента и доступные средства информирования о произошедшем событии, определил для себя 2 основных варианта решения: узнать о событии через AMI (Asterisk manager API) или обрабатывать через внешний скрипт список подключенных клиентов, которые Asterisk выводит по команде «sip show peers». Мне был ближе второй вариант, поэтому я решил остановиться на нём. (Кстати, сейчас подумал о том, что есть еще третий способ — парсить вывод файла /var/log/asterisk/full на предмет наличия строк «Registered SIP 'peername'» и «Unregistered SIP 'peername'». Возможно, так было бы даже проще, но я пошел по другому пути. Если вы знаете более простые варианты, расскажите, я их с удовольствием выслушаю).

Посмотрим, что пишет сервер:

asterisk*CLI> sip show peers
Name/username              Host            Dyn Nat ACL Port     Status     
peer1/peer1                192.168.XXX.XXX  D          5060     Unmonitored 
peer2/peer2                (Unspecified)    D          5060     Unmonitored 
peer3/peer3                192.168.XXX.XXX  D          5060     Unmonitored 
multifon/7922XXXXXXX       193.201.229.35              5060     OK (34 ms) 
sipnet/sipnet		   212.53.40.40                5060     Unmonitored 
mywifipeer/mywifipeer      (Unspecified)    D          0        Unmonitored 
6 sip peers [Monitored: 1 online, 0 offline Unmonitored: 4 online, 1 offline]

В колонке «Host» у отключенных клиентов видим надпись (Unspecified), а у зарегистрированных — IP-адрес. Этих данных достаточно для того, чтобы определить состояние регистрации у клиента. Таким образом задача свелась к тому, чтобы написать скрипт, который будет опрашивать на сервере состояние необходимого sip-аккаунта (смартфон — mywifipeer), и если состояние его регистрации изменилось, отправлять https-запрос. Для контроля внесённых изменений, информацию о произошедшем событии будем дублировать в jabber. Далее скрипт помещается в cron и выполняется ежеминутно.

Поскольку скрипт будет работать по запросу, а не как демон, для определения того, что статус подключения изменился, необходимо иметь информацию о том, каким он был в момент предыдущего запуска. Если этого не сделать, наш скрипт будет DOS'ить мегафоновский сервер, отправляя ему запрос на установку GSM-режима каждый раз во время проверки, что плохо. Поэтому при первом вызове скрипт поместит информацию о текущем статусе ($peer_state_now) во внешний файл ($peer_state_last_file), а все последующие — будет читать информацию из этого файла ($peer_state_last) и сверяться с текущим статусом.

Для отправки http-запроса использовался curl, для уведомления в жаббер — sendxmpp ($xmpp_bin и $xmpp_jid). Перед использованием скрипта необходимо задать логин-пароль, соответствующий регистрационной записи Мультифон-SIP ($multifon_login и $multifon_password).

Скрипт asterisk_peer_check


#!/bin/bash                                                                                                                     
#
# press F1 for help
if [ -z "$1" ]; then
    echo "Usage: $0 <peer_name>"
    exit 1
fi

# determining the future...
peer=$1
path="/var/spool/asterisk/tmp"
peer_state_last_file="$path/peer_state_$peer"

# Multifon account
multifon_login="7922XXXXXXX@multifon.ru"
multifon_password="mypassword"

# Jabber account for report
xmpp_jid="change_me@jabberserver.ru"
xmpp_bin="sendxmpp"

#getting actual peer state from asterisk
peer_state_now=`asterisk -rx "sip show peers" | grep -i $peer | awk '{print $2}'`;

#getting previous peer state from file
if [ -f "$peer_state_last_file" ]; 
    then
        peer_state_last=`cat $peer_state_last_file`
    else
        #first run
        peer_state_last=$peer_state_now
fi

#comparing actual peer status with previous
if [ "$peer_state_now" != "$peer_state_last" ]
    then
        #peer status changed
        if [ "$peer_state_now" = "(Unspecified)" ]
        then
            #GSM-2-GSM
            multifon_routing="0"
        else
            #GSM-2-SIP
            multifon_routing="1"
        fi

        #changing multifon status
        multifon_url="https://sm.megafon.ru/sm/client/routing/set?login=$multifon_login&password=$multifon_password&routing=$multifon_routing"
        curl --silent $multifon_url >/dev/null

        #jabber announce, if sendxmpp installed
        which $xmpp_bin >/dev/null || [ $? -eq 0 ] && echo "[`hostname`] Asterisk: Megafon incoming calls set to $multifon_routing" | $xmpp_bin -i $xmpp_jid
fi

#writing actual peer state in file
echo $peer_state_now > $peer_state_last_file
#

Осталось добавить запуск скрипта в /etc/crontab
#
* *     * * *   root    /etc/cron/asterisk_peer_check mywifipeer
#

Готово! Теперь можно проверить работоспособность всей системы. Подключаемся телефоном к серверу и через минуту получаем в jabber подтверждающее сообщение. Делаем тестовый звонок на телефон и убеждаемся в том, что все работает так, как и было задумано.

Ссылки:


Мультифон: формат http-запроса, ответы сервера и обработка кодов

комментарии (14)

  • То же можно сделать через asterisk manager (http://www.voip-info.org/wiki/view/Asterisk+manager+API) правда тогда нужно писать демон, подключающийся к сокету и читающий падающие оттуда сообщения, но и скорость реакции будет выше. Сам сейчас тщательно разбираюсь с AMI т.к. нужно интегрировать * с CRM, а event flow в AMI, который относится к звонкам не сильно простой.
    • У каждого решения есть свои плюсы и минусы. В моём конкретном случае приоритет был отдан простоте реализации, надёжности и минимизации времени на разработку. Временем ожидания реакции на событие я готов был пожертвовать изначально, потому что речь шла о домашнем сервере, и количество входящих звонков не такое большое. Решение с обработкой событий через вывод списка зарегистрированных лежало на поверхности, и не потребовало много времени на реализацию. Целью написания статьи было предоставить готовое решение для обработки событий, которое можно использовать сразу под конкретную задачу, либо с минимальными переделками. Надеюсь, скрипт получился читаемый и не вызвал дополнительных вопросов.
      • для manager api есть море беблиотек под все языки программирования.
        все заморочки решаютсмя одной строчкой import, одной строчкой для включения мониторинга, и тремя строчками вызова хэндлера события.
        наврядли это шибко сложнее, чем парсить текстовый вывод
        • я не знаю как у меня получились «беблиотеки» :)
        • События это половина дела. Звонки переводятся, вешаются на холд, объединяются в конференц-связь и т.д. Плюс ко всему в AMI валятся события по ВСЕМ пирам, всем звонкам и вообще всем событиям. Вобщем там дофига всего, и обработчик событий это даже не половина дела, это намного меньше. Знаем, проходили.
  • А Вы это затеяли для экономии на роуминге или для чего-то еще?
    • В основном, действительно для роуминга. Основные плюсы осуществления звонков через домашний Asterisk-сервер уже были рассмотрены ранее в этой отличной статье. В моём случае это так же стало актуально для приёма входящих сотовых звонков при нахождении в роуминге, и совершения исходящих голосовых вызовов через 3G-модемы, подключенные к серверу, по «домашним» ценам.

      Если говорить о полезности данного решения вне роуминга, то * в какой-то мере здесь просто выступает в роли дополнительного звена между вызывающим абонентом и вызываемым, позволяя управлять логикой звонков, как нам того захочется: осуществлять callback, вести запись разговоров, создавать blacklist'ы в зависимости от времени суток или номера вызывающего абонента и т. д. Т. е. при работе через сервер я просто получаю некоторые дополнительные возможности для обработки звонков.
      • На сайте Мультифона сечас лежит версия для Symbian, а Android-приложение на подходе. Так что, вопрос роуминга частично решается более простым способом.
        Если же нужна доп.логика управления вызовом, то да — решение вполне изящно…
        • На сайте выложена версия под Symbian S60v3. Попробовал установить её на своём тачфоне (S60v5), но установленное приложение не запускается и ничего не пишет.
          • S60v3 — версия для обычных, не сенсорных Симбиан-смартфонов. Для v5 тоже должна быть в ближайшем будущем.
        • в нокиях series 60 имеется встроенный сип=клиент, который отлично подключается к мультифону, так что все преимущества родного клиента = переключение режимов, которое элементарно делается 3мя строчками в букмарках браузера
          • Если Вы пользуетесь Мультифоном в одиночку, и используете его, как единственного провайдера voip-услуг, то такое решение Вам подойдет. Сомнение вызывает только вопрос удобства пользования такой схемой. Много действий, которые нужно совершить вручную (регистрация на сервере, загрузить браузер, кликнуть по закладке). Чем больше таких действий, тем больше вероятность ошибки вследствие человеческого фактора, а значит общая надёжность решения снижается. В предлагаемом мною варианте действие только одно — нажать на телефоне кнопку подключения к *. Всё остальное сделается автоматически. Даже если связь внезапно прервётся, сервер сам восстановит режим работы GSM, отправив правильный http-запрос, и Вы не пропустите входящие звонки, которые Мегафон будет направлять в отключенный SIP-транк.

            Кроме того, вариант работать с Мультифоном напрямую с телефона мне не подходит по следующим причинам:
            1. Для звонков по межгороду уже давно используется более 1 voip-провайдера (sipnet, multifon и pctel). Создавать несколько SIP-профилей в телефоне и потом при каждом звонке выбирать из них один нужный — это не очень правильно. Поэтому все voip-провайдеры уже давно прописаны на Asterisk'е, как транки. Направления звонков определены через dialplan. Набираю номер, а * уже сам определяет, куда его отправить.
            2. По межгороду звоню не я один. К * привязана еще пара домашних SIP-телефонов, которые должны использовать любой из зарегистрированных междугородних транков для исходящих звонков. Мультифон является одним из них, и прерывать его регистрацию на сервере неправильно.
            • так я же не спорю, у самого *
              это был комментарий о бессмысленности родного клиента для симбиана
              а вашим решемием я еще может быть и воспользуюсь = если вдруг буду ездить по России (а не только по тайландам всяким, хихи)
  • Плюсанул в карму: голосование за сам топик закончилось раньше, чем он мне попался…

    Сижу и думаю над каким вопросом: у Мультифона есть пааааршивейшая привычка хреново работать в режиме «GSM+VoIP» — народ при звонке ко мне не слышит гудка и т.д. и т.п.
    Соответственно — охота сделать хитрую штуку, что-бы при моём нахождении дома включался режим VoIP для приёма на радиотрубку звонков, а когда не дома — GSM… Благо, что телефон с двумя симками и вторая симка предназначена именно для работы с АТСкой домашней — на неё дублируются вызовы…
Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.