С.Кадаков
Глава 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. Промежуточный итог
В данной части мы обсудили исключительно важный вопрос, а
именно: структуру протокола. Надеемся, пока недоразумений не возникло.
Языки программирования: разное
|