SMPP-сессия держится стабильно, если клиент регулярно отправляет enquire_link, контролирует ответ enquire_link_resp и умеет переподключаться после тайм-аута или разрыва TCP-соединения. В статье разберём, как настроить heartbeat, определить момент сбоя, восстановить bind-транзакцию и не потерять сообщения в очереди. Эти правила подходят для API-шлюза, собственной SMS-платформы и интеграции малого бизнеса с CRM или интернет-магазином.
Зачем SMPP нужен enquire_link?
SMPP работает поверх TCP, а TCP-соединение не всегда сразу сообщает приложению о проблеме. Кабель мог отключиться, сеть могла смениться, промежуточный firewall мог закрыть неактивное соединение. При этом программа продолжает считать сессию открытой и отправляет новые PDU в канал, который уже не доставляет данные.
enquire_link — служебный PDU для проверки состояния SMPP-сессии. Клиент отправляет его SMSC, а SMSC отвечает enquire_link_resp. Если ответ пришёл, соединение отвечает на heartbeat. Если ответ не появился в установленный срок, клиент получает основание закрыть сессию и начать восстановление.
Heartbeat не проверяет доставку SMS конечному абоненту. Он показывает только состояние соединения между вашей системой и SMPP-шлюзом. Статус доставки нужно отслеживать отдельно через DLR, то есть delivery receipt. Для сервисов, где важна история каждого сообщения, полезно заранее настроить мониторинг SMS-интеграции с отдельными сигналами для сети, SMPP-сессии и доставки.
Как настроить heartbeat и тайм-ауты?
Сначала зафиксируйте четыре параметра: интервал отправки enquire_link, время ожидания ответа, допустимое число пропущенных ответов и паузу перед повторным подключением. Конкретные значения зависят от требований SMPP-провайдера и сетевой инфраструктуры, поэтому их берут из технических условий подключения, а не выбирают случайно.
Пример последовательности выглядит так:
- Клиент открывает TCP-соединение с SMPP-шлюзом.
- Клиент выполняет
bind_transceiverлибо другой согласованный тип bind. - После успешного bind запускается отдельный таймер heartbeat.
- По таймеру клиент отправляет один
enquire_link. - До следующей отправки клиент ждёт
enquire_link_resp. - При ответе он сбрасывает счётчик пропущенных heartbeat.
- При превышении тайм-аута закрывает сокет и переводит сессию в состояние reconnecting.
Поток heartbeat лучше отделить от потока отправки SMS. Если рабочая очередь занята большим объёмом сообщений, служебный PDU не должен ждать окончания массовой отправки. Иначе приложение может показывать активный трафик, хотя сервер уже считает соединение потерянным.
Ещё одна практическая деталь — защита от параллельных heartbeat. Пока система ждёт ответ на предыдущий enquire_link, она не должна отправлять следующий. Иначе при задержке сети появится несколько незавершённых запросов, а диагностика станет неточной.
Как понять, что сессию пора переподключать?
Сбой определяется по совокупности признаков: TCP-сокет сообщил об ошибке, чтение из соединения завершилось, пришёл SMPP-ответ с ошибкой или истёк тайм-аут ожидания enquire_link_resp. Один медленный ответ ещё не всегда означает полную потерю канала. Решение принимают по правилам, согласованным с провайдером: например, после заданного числа последовательных пропусков.
Состояние интеграции удобно хранить как простую машину состояний:
DISCONNECTED— TCP-соединения нет;CONNECTING— приложение устанавливает TCP-соединение;BINDING— отправлен bind и ожидается ответ;BOUND— сессия готова к обмену PDU;RECONNECTING— старый канал закрывается, система готовит новое подключение.
Новые SMS следует отправлять только в состоянии BOUND. При переходе в RECONNECTING очередь замораживает передачу, но сохраняет сообщения и их внутренние идентификаторы. После нового bind приложение продолжает работу с очередью, а не создаёт повторные записи для тех же запросов.
Отдельно контролируйте sequence number. Он нужен для сопоставления запроса с ответом в рамках SMPP-сессии, но после переподключения нельзя считать старый канал действующим. Ответ, который пришёл от закрытой или уже заменённой сессии, не должен подтверждать операцию в новой сессии.
Как реализовать переподключение без бесконечного цикла?
После сбоя клиент закрывает старый сокет, отменяет таймеры и очищает состояние bind. Затем он ждёт перед новой попыткой. Постоянные подключения без паузы создают лишнюю нагрузку и затрудняют работу провайдера, особенно если проблема находится на сетевом участке, который ещё не восстановился.
Для повторных попыток применяют возрастающую задержку: первая попытка идёт почти сразу, следующие выполняются через более длинные интервалы, а после установленного предела задержка перестаёт увеличиваться. К ней добавляют небольшой случайный разброс, чтобы несколько экземпляров приложения не подключались одновременно после общего сбоя.
| Событие | Действие клиента | Что проверить в журнале |
|---|---|---|
| Нет ответа на enquire_link | Зафиксировать тайм-аут; после заданного порога закрыть сессию | Время отправки PDU и время последнего ответа |
| Ошибка TCP | Остановить отправку и перейти к переподключению | Код сетевой ошибки, адрес шлюза, состояние сокета |
| Ошибка bind | Не отправлять SMS; повторить попытку по политике backoff | Команда bind, код SMPP-ошибки, версия конфигурации |
| Новый bind успешен | Возобновить обработку очереди с контролем дублей | Время bind_resp, идентификатор сессии, размер очереди |
При восстановлении соединения не отправляйте всю накопившуюся очередь одним рывком. Скорость передачи ограничивают параметрами провайдера, числом параллельных запросов и настройками окна SMPP. Для раздельной обработки срочных и обычных сообщений пригодится схема очереди SMS в API и SMPP с rate limit и приоритетами.
Как не отправить SMS повторно после сбоя?
Сложность появляется в момент, когда приложение отправило submit_sm, но не получило submit_sm_resp. Система не знает, принял ли шлюз сообщение до разрыва связи. Если автоматически повторить отправку, абонент может получить два одинаковых SMS.
Для этого вводят внутренний идентификатор операции и сохраняют его до получения окончательного результата. Запись должна содержать текст сообщения, номер получателя, время постановки в очередь, состояние отправки и связанный message ID провайдера, если он был получен. После reconnect обработчик отдельно разбирает записи со статусами «ожидает ответа», «подтверждено» и «ошибка».
У SMPP нет универсальной гарантии, которая во всех ситуациях исключает повторную доставку при потере ответа. Поэтому политику повторной отправки выбирают для каждого типа сообщения. Для одноразового кода повтор может привести к путанице, а для уведомления о заказе нужно оценить риск пропуска. Логику защиты от повторов удобно проверить по отдельному чек-листу защиты API и SMPP от дублей.
Какие ошибки чаще всего ломают сессию?
- Heartbeat отправляют только при отсутствии SMS-трафика, хотя сетевой разрыв может произойти во время активной очереди.
- Приложение считает TCP-соединение живым без проверки
enquire_link_resp. - После reconnect сразу отправляют очередь, не дождавшись успешного
bind_resp. - Таймер heartbeat запускается повторно при каждом событии, поэтому одновременно работают несколько таймеров.
- Все сообщения повторно отправляют после тайм-аута без разборки неопределённых операций.
- В журнал пишут только текст ошибки без времени, sequence number и состояния сессии.
Минимальный набор метрик включает число активных SMPP-сессий, время последнего успешного heartbeat, количество переподключений, длительность восстановления, размер очереди и число неопределённых submit_sm. Эти показатели помогают отличить краткий сетевой сбой от ошибки авторизации или превышения лимита шлюза.
Три шага, которые можно сделать на этой неделе:
- Описать состояния соединения и записать, при каком событии система переходит между ними.
- Добавить отдельный heartbeat с тайм-аутом, журналом PDU и защитой от параллельных
enquire_link. - Проверить сценарий разрыва после
submit_sm: очередь, повторную отправку, DLR и восстановление bind на тестовом подключении.


