Sunday, July 8, 2007

SMS-приложение. Часть 2

Процедура авторизации.

Мы начинаем обсуждать команды протокола. В нашем, самом первом, приложении нам их понадобится совсем немного. Начнем с процедуры авторизации, выполняемой одной из команд семейства BIND.

Команды BIND.

Как уже говорилось, это семейство включает три команды:

  • bind_receiver -- сообщает SMSC, что ESME подключается для приема сообщений. Такой режим (только прием) характерен, например, для пэйджинговых шлюзов.
  • bind_transmitter -- сообщает SMSC, что ESME подключается для передачи сообщений. Данный режим используется, в частности, при организации рассылок (в том числе, к сожалению, и спама).
  • bind_transceiver -- сообщает SMSC, что ESME подключается для приема и передачи (т. е. в полнодуплексной моде). Этот режим удобен для создания полнофункциональных шлюзов.

Ниже мы рассмотрим каждую из команд в отдельности и приведем формат пакетов. Как уже отмечалось, все команды (пакеты) SMPP должны предваряться заголовком. В заголовке команды поле command_id должно содержать соответствующее значение.

bind_receiver. command_id = 0x01

Тело пакета bind_receiver состоит из следующих полей:


Field name Size (octets) Type Description
system_id Var. max 16 C-Octet String Идентифицирует ESME. Можно рассматривать это поле, как "username".
password Var. max 9 C-Octet String Пароль для аутентификации.
system_type Var. max 13 C-Octet String Необязательное. Используется для указания категории ESME, например "WWW", "E-MAIL" и т. д. Если не используется, должно содержать один октет NULL (0x0)
interface_version 1 Integer Идентифицирует версию протокола SMPP, поддерживаемую ESME. 0x00-0x33 указывают, что поддерживается версия 3.3 или более ранняя. 0x34 указывает, что поддерживается версия 3.4
addr_ton 1 Integer Указывает тип адреса (TON - Type Of Number) ESME. (см. ниже). Если неизвестен, выставляется в NULL
addr_npi 1 Integer Указывает индикатор плана номеров (NPI -- Numbering Plan Indicator). (см. ниже). Если неизвестен, выставляется в NULL
address_range Var. max 41 C-Octet String Адрес или диапазон адресов, обслуживаемых ESME. Если неизвестен, выставляется в NULL

bind_receiver_resp. command_id = 0x80000001

Тело пакета bind_receiver_resp состоит из следующих полей (*):


Field name Size (octets) Type Description
system_id Var. max 16 C-Octet String Подтвержденный идентификатор ESME.
sc_interface_version - TLV Необязательное поле в формате TLV. Информирует о версии протокола SMPP, поддерживаемой SMSC

(*) -- В том случае, если поле command_status заголовка содержит ненулевое значение (т. е. информирует об ошибке) тело пакета не передается.

bind_transmitter. command_id = 0x02

Формат и назначение полей тела пакета bind_transmitter совпадает с форматом и назначением полей тела пакета bind_receiver.

bind_transmitter_resp. command_id = 0x80000002

Формат и назначение полей тела пакета bind_transmitter_resp совпадает с форматом и назначением полей тела пакета bind_receiver_resp.

bind_tranceiver. command_id = 0x03

Формат и назначение полей тела пакета bind_transceiver совпадает с форматом и назначением полей тела пакета bind_receiver.

bind_tranceiver_resp. command_id = 0x80000003

Формат и назначение полей тела пакета bind_transceiver_resp совпадает с форматом и назначением полей тела пакета bind_receiver_resp.

Параметры TON и NPI.

Параметры TON и NPI не являются специфичными для протокола SMPP, поэтому мы здесь не будем останавливаться на них подробно, а просто приведем их возможные значения (подробнее об этих параметрах можно прочитать в документации GSM):


TON Value (bin)
Unknown 00000000
International 00000001
National 00000010
Network Specific 00000011
Subscriber Number 00000100
Alphanumeric 00000101
Abbreviated 00000110
Reserved All others

NPI Value (bin)
Unknown 00000000
ISDN (E163/E164) 00000001
Data (X.121) 00000011
Telex (F.69) 00000100
Land Mobile (E.212) 00000110
National 00001000
Private 00001001
ERMES 00001010
Internet (IP) 00001100
WAP Client Id (to be
defined by WAP Forum)
00010010
Reserved All others

Generic Negative Result (generic_nack)

Как уже отмечалось, любая из команд, испущенная той или иной из сторон должна квитироваться ACK'ом (за единственным, упомянутым в прошлой статье исключением). При этом поле command_id заголовка ACK'а должно содержать соответствующее значение (равное command_id оригинальной команды с выставленным 31-м битом), а поле sequence_number заголовка должно указывать номер транзакции оригинальной команды. Однако в некоторых случаях, в частности, когда принимающая сторона не может распознать заголовок оригинальной команды -- например, при возникновении "мусора" в TCP/IP канале -- информация, необходимая для формирования корректного ACK'а может оказаться недоступной. В таких случаях принимающая сторона отвечает специальным пакетом, называемым generic_nack (command_id = 0x80000000). Данный пакет состоит из одного заголовка с указанным command_id. Поле command_status содержит соответствующий код ошибки, а поле sequence_number может содержать NULL (0x0), если не удалось распознать номер транзакции оригинальной команды.

Замечания.

Несколько слов по поводу направления передачи данных по соединениям, т. к. могут возникать недоразумения, связанные с названиями сессий. Например, если ESME авторизовалось с помощью команды bind_transmitter и сессия перешла в состояние BOUND_TX, не следует думать, что SMSC не может передавать данные в такое соединение (т. е., что программист избавлен от вызова recv(2) с этим сокетом). Напротив, еще раз подчеркнем, что ACK'и передаются в рамках того же соединения (т. е. в тот же сокет), что и оригинальные команды. С другой стороны, разумеется, входящие сообщения (т. е. от SMSC к ESME) не могут быть переданы по соединению, находящемуся в BOUND_TX. Это важный момент и вот почему: как мы договорились, мы будем работать в т. н. Store And Forward Mode, при этом сообщения сначала помещаются в базу данных SMSC, а потом предпринимаются попытки их доведения. Таким образом, факт помещения в базу данных (подтверждаемый ACK'ом) еще не означает, что конечный пользователь получил данное сообщение, кроме того, оно вообще может быть не доведено до истечения срока годности в том случае, например, если аппарат пользователя остается отключенным не протяжении нескольких дней. SMSC сигнализирует о переходе сообщения в финальное состояние (доставлено/не может быть доставлено) с помощью (специального вида) команды deliver_sm, т. е. той же команды с помощью которой передаются входящие сообщения, и которая ... не может быть передана в соединение находящееся в BOUND_TX! В действительности, такие сообщения, называемые delivery receipts некоторое время хранятся на SMSC и передаются в первое же, авторизовавшееся с помощью bind_receiver, соединение с тем же system_id. Таким образом, достаточно время от времени открывать сессию с помощью bind_receiver и "снимать" delivery receipt'ы и входящие сообщения, буде таковые найдутся, или просто сразу же открывать две сессии. Понятно, что для сессий в состоянии BOUND_TRX таких проблем не существует. Однако, в случаях больших загрузок предпочтительней иметь отдельные сессии на прием и передачу (возможно, в разных потоках исполнения -- threads) чтобы распараллелить эти операции.

Промежуточный итог 2

В данной главе мы обсудили процедуру авторизации поверх сокетного соединения. Как мы видим, она не слишком сложна, хотя здесь есть некоторые интересные моменты. Нам осталось обсудить команды передачи и приема сообщений и мы вплотную приблизимся к кодированию.

No comments: