Monday, July 9, 2007

SMS-приложение. Часть 5. "Разбор полетов"

Вопросы наших читателей.

Меня вот спросили: "А почему это Вы себя во множественном числе именуете?". Отвечаю: это не то что вы подумали (хотя, может и стоит у психоаналитика проконсультироваться на предмет мании величия ;), а издержки опыта "научнописания". В статьях считается хорошим тоном писать во множественном числе, имея ввиду коллектив авторов. Так и тут, предполагалось, что данный цикл я буду вести не в одиночку, посему и задан такой "околонаучный" тон. Однако так выходит, что заниматься этим мне приходится в "единственном числе", посему, принимая во внимание данное замечание, в дальнейшем обязуюсь повествовать от собственного лица (другого не имею :) пока-таки не появятся помощники.

Что же такое ESME?

В данной статье мы (с Вами :) побеседуем о том, как на самом деле устроены эти так называемые ESME и для чего, собственно, они предназначены. Вообще говоря, это нужно было бы сказать с самого начала, но я решил, что сначала неплохо дать читателю (т. е. Вам) присмотреться, а уж потом "делать строгое лицо". Итак, казалось бы чего проще -- подцепил к COM-порту радиопад, типа SIEMENS, и вещай себе прямо в эфир (по сути, такая конструкция эквивалентна "домашнему" сервис центру). В некоторых случаях подобный "агрегат" представляется приемлемой альтернативой, пока речь не заходит об интенсивности траффика. Именно при построении "профессиональных" сервисов, рассчитанных на охват большой аудитории, ESME и будет являться компонентой сервисных систем.

Так что же такое ESME? Для того, чтобы строго ответить на этот вопрос придется обратиться к известной OSI/ISO модели (рис. 1).




Рис. 1. Модель OSI

Ниже приведена таблица, показывающая, как на ней "размещается" ESME. Для примера приведены стэки протоколов для Microsoft Networking и регулярного TCP/IP приложения, например почтового клиента. Мы, по-прежнему, обсуждаем ESME, связывающееся с сервис центром по протоколу TCP/IP.
OSI
Layer
Microsoft
Networking
TCP/IP
Internet
ESME OSI
Protocols
Application
Layer 7
Application Programs and Protocols
for file transfer, electronic mail, DB connectivity, etc.
Presentation
Layer 6
Server
Message
Block
(SMB)
(Telnet, FTP,
SMTP, etc.)
Protocol packet
composing/parsing
module
(for example SMPP)
ISO
8823
Session
Layer 5
Network Basic
Input/Output
System
(NetBIOS)
Session management
module,
internal buffers
management module
ISO
8327
Transport
Layer 4
Network
Basic Extended
User Interface
(NetBEUI)
Transmission
Control Protocol (TCP),
Unacknowledged
Datagram Protocol (UDP)
Transmission
Control Protocol (TCP),
Unacknowledged
Datagram Protocol (UDP)
ISO
8073
TP0-4
Network
Layer 3
Internet
Protocol
(IP)
Internet
Protocol
(IP)
ISO
8473
(CLNP)
Data Link
Layer 2
Network Interface Cards: Ethernet, Token-Ring, ARCNET, StarLAN, LocalTalk, FDDI, ATM, etc.
NIC Drivers: Open Datalink Interface (ODI), Network Independent Interface Specification (NDIS)
Physical
Layer 1
Transmission Media:
Twisted Pair, Coax, Fiber Optic, Wireless Media, etc.

Теперь видно, что собственно ESME занимает то же положение на стэке, что и регулярный TCP/IP клиент. Правда, если ESME не оформлено в виде библиотеки, оно может простираться и на самый верхний -- седьмой -- уровень. Таким образом, самым верхним подлежащим уровнем для ESME будет реализация TCP/IP стэка операционной системы, что мы собственно и видели. К слову, сама программа (библиотека) может быть и не так жестко разбита на "слои", но здесь я явно их выделил. Давайте обсудим эти уровни по отдельности.

  • Session layer предназначен для решения задач, не относящихся собственно к передаче данных. В этот круг задач могут входить аутентификация сторон, восстановление физических или логических соединений, организация двунаправленных потоков (bidirectional traffic), token management, синхронизация и пр.
  • Presentation layer отвечает за преобразование данных.

Общая структура dumb_esme представлена на картинке:



Перейдем к обсуждению назначения классов, вошедших в состав приложения опираясь на уровни модели OSI.

Session layer.

В нашем простом примере этот уровень не представлен со всей фундаментальной четкостью, часть функций (в т. ч. решение о закрытии соединений) делегирована основной функции (main), а восстановление соединений и поддержание внутренних циклических (FIFO) буферов и вовсе опущено. В реальных проектах приходится организовывать такие "очереди октетов" по той причине, что (в отличие от X.25) TCP/IP не гарантирует что одним вызовом recv мы получим ровно один отправленный протокольный пакет и целиком. Типичная ситуация, проявляющаяся в частности при больших загрузках: за один вызов recv приходит два целых пакета и "кусок" третьего. Разумеется, в связи с гарантированной доставкой данных по TCP-каналу, недостающий "кусок" будет получен следующим вызовом, но, возможно, еще с чем-то "на хвосте". Ситуацию, как я уже сказал, решают созданием FIFO буфера: модуль, непосредственно оперирующий сокетом, пишет в "хвост" такого буфера, а вышестоящий модуль, "откусывает помаленьку" с "головы". Кроме того, в протоколе SMPP, в отличие, скажем, от EMI или SIMD, не предусмотрены маркеры начала и конца пакета, так что при возникновении "мусора" в канале "ловля" начала следующего неиспорченного пакета превращается в серьезную (вообще говоря -- неразрешимую) проблему и проще переустановить соединение, сбросив буфер. Вообще говоря, достаточно в заголовке пакета указать неверную длину, чтобы вывести канал из строя.

В нашем простом примере проблема частично решена путем обработки входящих пакетов "в два удара": сначала обрабатывается заголовок пакета (длина которого строго фиксирована), а потом принимается из канала ровно столько октетов, сколько указано в заголовке. К слову, и при работе со внутренним буфером также приходится сначала обрабатывать заголовок, ибо именно в нем указан тип команды. Возвращаясь к модели OSI отметим, что все же многие функции обсуждаемого уровня инкапсулированы в нашем примере в двух классах.

class connector

Это простая классовая оболочка вокруг системной реализации протокола TCP/IP, именно он оперирует непосредственно с сокетом. В реальном проекте, скорее всего, здесь был бы реализован буфер FIFO. Интересной задачей могла бы являться реализация данного класса как наследника basic_iostream из стандартной библиотеки, что нибудь наподобие sockstream...

class smpp_connector

Наследник класса connector, выполняющий большинство функций, характерных для Session Layer, как то: аутентификацию (посредством SMPP bind), разбор и проверку заголовков пакетов, поддерживает внутреннюю нумерацию команд SMPP. Обращаясь к реальным проектам, следует сказать, что этот класс -- самое место для управления восстановлением соединения для ESME работающих в непрерывном (ждущем) режиме. Стоит также заметить, что есть все основания задуматься о вынесении функциональности данного класса в отдельный поток исполнения (thread).

Presentation layer.

Представлен двумя классами transmitter и receiver. Как явствует из названия, эти классы (будучи оба наследниками класса smpp_connector) инкапсулируют особенности сессий в состояниях BOUND_TX и BOUND_RX, занимаются распаковкой и упаковкой пакетов (я говорю пакеты, подразумевая, конечно же PDU) и переправляют их "вверх" приложению и "вниз" -- в сокет. Интерфейс с приложением поддерживается с помощью двух структур данных: message, являющейся абстракцией как для входящего, так и для исходящего сообщений, и status_info, отображающей факт изменения состояния сообщения (принято/не принято, доведено/не доведено).

Заключение.

Вот, собственно, и вся реализация протокола. Остальные классы, вошедшие в проект являются вспомогательными, их суть видна из названий (и откомментирована в коде).

Надеюсь, данным циклом статей, не затрагивающим, как я уже сказал, все "интересные" вопросы мне удалось показать технику написания такого класса приложений. Я старался обсудить наиболее общие вопросы, не привязываясь к определенной платформе и не "отступая далеко" от реальных проектов, в создании которых мне удалось принять участие (один из таких, кстати, оправдывая описанный подход, в настоящее время трудится в непрерывном режиме, пропуская в пиках до 5 млн. сообщений в сутки).

Что же "осталось за скобками"? Да довольно многое. Мы никак не обсудили передачу двоичных данных (графики, мелодий), сосредоточившись на SMPP, оставили без внмания другие протоколы, не коснулись интересных особенностей программирования под конкретные OS. Честно говоря, я не могу сейчас сказать, чем будет продолжен этот цикл статей, посему, это отличный повод для аудитории сделать заявки. Надеюсь на вашу активность.

No comments: