div.main {margin-left: 20pt; margin-right: 20pt} С.Кадаков
sgerr@hotmail.com
SMS-приложение. Часть 1.
Глава 1 Выбор протокола.
Что ж, вот и настала пора написать наше первое реально
работающее SMS-приложение, чем и займемся. Для "удобоварения" мы разбили
эту статью на несколько частей: сначала займемся некоторыми теоретическими
вопросами (так, сущие пустяки -- опишем протокол :), а затем, не
отвлекаясь, займемся кодированием.
В прошлый раз мы показали, как устанавливать связь с
Сервис-центром, теперь же необходимо научиться работать собственно с
протоколом -- формировать и "разбирать" пакеты. Но прежде, как легко
догадаться, необходимо выбрать протокол. Эту сложнейшую задачу мы возьмем
на себя ;) и остановимся на SMPP (Short Messages Peer-to-Peer). В
пользу такого выбора говорят не только личные пристрастия, но и то, что
данный протокол является наиблее широко распространненым, отлично
проработан и превосходно документирован. Кроме того, не исключая
возможности того, что читателям в реальности придется столкнуться с
другими протоколами, отметим, что здесь ситуация сродни изучению
иностранных языков: второй изучать легче чем первый, третий, чем второй и
т. д.
Глава 2. Протокол SMPP.
Обзор.
Протокол SMPP позволяет , как не трудно
догадаться, "внешним" устройствам обмениваться сообщениями с мобильной
сетью (PLMN) посредством SMSC и определяет:
Набор операций для обмена между SMSC и ESME, называемых также
"командами" (command).
Формат передаваемого пакета (PDU -- Protocol Data Unit),
ассоциированный с каждой из операций.
Формат ответного пакета (ACK или responce) для каждой PDU.
Данные, которыми ESME должна обмениваться с SMSC в ходе таких
операций.
Таким образом, вызову команды соответствует отправка PDU,
поэтому мы иногда вместо "вызвать submit_sm" будем говорить "послать
submit_sm" и наоборот. Следует также обратить внимание на то, что каждая
из команд в рамках сессии должна быть подтверждена ответным
пакетом (ACK), единственное исключение -- alert_notification
PDU (впрочем, эта команда нам на первых порах не понадобится).
Сессии SMPP.
Обмен сообщениями с SMSC в формате протокола SMPP (кстати
говоря -- не только) носит сессионный характер. Это означает, что обмен
должен предваряться некоторой процедурой инициализации сессии и, в
безошибочном варианте, за обменом должна следовать процедура закрытия
сессии. В ходе процедуры открытия сессии ESME открывает соединение
на уровне сокета, авторизуется и сообщает о цели открытия сессии:
Прием сообщений. (приемник -- RECEIVER)
Передача сообщений. (передатчик -- TRANSMITTER)
Прием и передача сообщений. (приемо-передатчик -- TRANCEIVER)
Процедура инициализации выполняется с помощью вызова
одной из команд bind_*:
bind_receiver
bind_transmitter
bind_tranceiver формат которых мы рассмотрим чуть
ниже. Таким образом, сессия может находиться в следующих состояниях:
OPEN -- Установлено сокетное соединение и послан один из
запросов bind_*.
BOUND_RX | BOUND_TX | BOUND_TRX -- Выполнена одна из команд
bind_*. Соединение готово к приему | передаче | приему и
передаче.
CLOSED -- Выполнена команда unbind (рассмотрим позже)
и соединение закрыто. Кроме того, SMSC в любой момент может
послать команду outbind. В ответ на эту команду ESME обязана снова
выполнить один из запросов bind_*. Правда, в реальной практике эта
команда встречается редко, но к ее обработке следует быть готовым.
Команды (PDU) SMPP
Протокол SMPP версии 3.4 предоставляет следующий набор
команд:
SMPP PDU Name
Required SMPP Session State
Issued by ESME
Issued by SMSC
bind_transmitter |
OPEN |
Yes |
No |
bind_transmitter_resp |
OPEN |
No |
Yes |
bind_receiver |
OPEN |
Yes |
No |
bind_receiver_resp |
OPEN |
No |
Yes |
bind_transceiver |
OPEN |
Yes |
No |
bind_transceiver_resp |
OPEN |
No |
Yes |
outbind |
OPEN |
No |
Yes |
unbind |
BOUND_TX BOUND_RX BOUND_TRX |
Yes Yes Yes |
Yes Yes Yes |
unbind_resp |
BOUND_TX BOUND_RX BOUND_TRX |
Yes Yes Yes
|
Yes Yes Yes |
submit_sm |
BOUND_TX BOUND_TRX |
Yes Yes |
No No |
submit_sm_resp |
BOUND_TX BOUND_TRX |
No No |
Yes Yes |
submit_sm_multi |
BOUND_TX BOUND_TRX |
Yes Yes |
No No |
submit_sm_multi_resp |
BOUND_TX BOUND_TRX |
No No |
Yes Yes |
data_sm |
BOUND_TX BOUND_RX BOUND_TRX |
Yes Yes Yes |
Yes Yes Yes |
data_sm_resp |
BOUND_TX BOUND_RX BOUND_TRX |
Yes Yes Yes |
Yes Yes Yes |
deliver_sm |
BOUND_RX BOUND_TRX |
No No |
Yes Yes |
deliver_sm_resp |
BOUND_RX BOUND_TRX |
Yes Yes |
No No |
query_sm |
BOUND_TX BOUND_TRX |
Yes Yes |
No No |
query_sm_resp |
BOUND_TX BOUND_TRX |
No No |
Yes Yes |
Команды с суффиксом _resp означают responce, т. е.
ACK (мы также употребляем термин квитанция, и говорим, что ACK
"квитирует" команду). Жирным помечены команды, которые мы рассмотрим
(остальные в нашем простейшем случае нам пока не понадобятся).
Режимы (Modes) сообщений.
Протокол SMPP v3.4 поддерживает три режима обмена
сообщениями. Мы не будем на этом особо останавливаться, просто упомянем,
что в дальнейшем будем работать в т. н. Store and Forward Message
Mode. В данном режиме сообщение сначала сохраняется в базе данных
SMSC, а потом предпринимаются попытки его доведения, и, в зависимости от
запроса, ESME уведомляется о достижении сообщением финального состояния
(доведено/не доведено). Поддерживаются также Datagram Mode и
Transaction Mode, о которых можно прочесть в соответствующей
документации.
Типы данных SMPP
Все определяемые протоколом PDU представляют собой
совокупность полей определенных типов, выстроенные друг за другом в
определенном порядке. При работе в сети принято использовать понятие
октет (octet) -- совокупность восьми битов (то что принято называть
в программировании байтом) для того, чтобы подчеркнуть
"восьмибитовость". Все определяемые SMPP типы привязаны к октету. Вот они:
Integer -- Беззнаковое целое с указанной в каждом конкретном
случае длиной в октетах.
C-Octet String -- Серия ASCII литер, завершенная нулем.
C-Octet String (Decimal) -- Серия литер (0-9), завершенная
нулем
C-Octet String (Hex) -- Серия литер (0-F), завершенная нулем.
Octet String -- Серия октетов, не обязятельно завершенная
нулем.
TLV -- Tag-Length-Value. Используется для необязательных
параметров. Данный тип возник из-за необходимость расширять уже
существующие команды с сохранением обратной совместимости.Поскольку мы
не собираемся таковые использовать :), то и останавливаться на этом не
будем, хотя вопрос, вообще говоря, важный. Про TLV можно прочитать в
спецификации протокола.
Формат PDU
Каждый пакет (PDU) состоит из двух частей:
Заголовок (header). Обязательная.
Тело (body). Необязательная. Формат заголовка общий
для всех PDU.
Заголовок (header)
Заголовок имеет длину 16 октетов и состоит из 4-х полей:
Command length -- Длина. 4 октета. Должен содержать общую
длину пакета, включая и это поле.
Command id -- Идентификатор команды. 4 октета. Указывает на
тип команды. Принимает значения от 0x0 до 0x1FF для команд и от
0x800000000 до 0x8000001FF для ответов (ACK). Каждый пакет,
представляющий собой ACK имеет command_id такой же, как и квитируемая
команда, но с выставленным 31-м битом
Command status -- Статус команды. 4 октета. Используется в
ACK'ах, в командах выставляется в 0x0.
Sequence number -- Номер последовательности. В каждом
конкретном случае содержит уникальный номер отдельной команды (не путать
с command_id) и позволяет ассоциировать ACK с командой. Назначается
испускающей стороной произвольно из диапазона 0x1-0x7FFFFFFF. Повтор
данного номера в рамках одной сессии, вообще говоря, не допустим. ACK
должен иметь тот же номер, что и квитируемая команда. Некоторые
PDU (в частности, большинство ACK'ов) состоят только из заголовка и этой
информации оказывается достаточно.
Глава 3. Промежуточный итог
В данной части мы обсудили исключительно важный вопрос, а
именно: структуру протокола. Надеемся, пока недоразумений не возникло.
|