Через мобильный интернет всё иначе: что операторская сеть делает с вашими соединениями
Приложение отлично работает у всех, кто тестировал его по Wi-Fi и на кабеле в офисе, а в проде часть пользователей с мобильных телефонов жалуется на разрывы соединений, зависшие вебсокеты и звонки, которые не устанавливаются. Часто причина не в коде и не в сервере, а в том, что мобильная сеть — не «тот же интернет, только по воздуху». У неё своя архитектура на уровне оператора, которая иначе распределяет IP-адреса, иначе ведёт себя во времени и иногда сама трогает трафик по дороге. Разбираем, что происходит между телефоном и сервером в 3G/4G/5G-сети и что из этого стоит закладывать в архитектуру бэкенда.
Содержание
- CGNAT: почему у мобильного клиента нет своего IP
- Что это ломает: P2P, WebRTC и входящие соединения
- Задержка и джиттер радиоканала
- Хендоверы между вышками: обрывы, которые нужно пережить
- Спящий радиомодуль и задержка на «пробуждение»
- Операторские прокси: трафик меняется по дороге
- Как проектировать бэкенд под мобильную аудиторию
CGNAT: почему у мобильного клиента нет своего IP
На домашнем проводном интернете абонент почти всегда получает адрес, который так или иначе закреплён за одним устройством или квартирой. На мобильной сети иначе: оператор обслуживает сотни тысяч абонентов через ограниченный пул IPv4-адресов и делает это через CGNAT — Carrier-Grade NAT на пограничном оборудовании сети, а не на устройстве абонента. За одним внешним IP оператора одновременно «прячутся» тысячи телефонов, каждому из которых выделяется свой диапазон портов.
Отсюда два следствия, которые касаются серверной части напрямую:
- С сервера вы видите один и тот же внешний IP у множества разных пользователей. Любая логика, которая привязывает лимиты или бан-листы к IP-адресу, на мобильном трафике начинает бить по случайным людям — забанили одного абусера, под раздачу попала вся группа абонентов за тем же внешним адресом оператора. Подробнее о том, откуда берётся эта путаница и как её диагностировать, — в статье про двойной NAT у клиента.
- Входящие соединения к телефону оператор в общем случае не пропускает. Обращение снаружи придёт на внешний IP оператора, а он не может понять, какому из тысяч абонентов за этим адресом адресован конкретный пакет. Проброс портов и любые схемы, ждущие входящего соединения к мобильному клиенту, на практике не работают — та же проблема с другой стороны разобрана в статье про VPN через 4G-модем.
Конкретная реализация CGNAT (глубина NAT, размер диапазона портов, время жизни трансляции) у каждого оператора своя и не документируется публично — рассчитывать стоит не на конкретные цифры, а на сам факт: прямого маршрута снаружи к мобильному устройству нет и не будет.
Что это ломает: P2P, WebRTC и входящие соединения
Прямое следствие CGNAT — любая архитектура на прямом P2P-соединении между двумя клиентами на мобильной сети работает нестабильно в зависимости от того, как повезло с типом NAT у конкретного оператора в конкретный момент.
Для WebRTC это значит, что STUN-согласование (когда оба клиента узнают свой внешний адрес и пробуют достучаться друг до друга напрямую) на мобильных сетях срабатывает заметно реже, чем на обычном домашнем интернете — двойной NAT (NAT роутера у одного собеседника плюс CGNAT оператора у другого) делает прямой путь маловероятным. Рабочий сценарий для мобильной аудитории — закладывать TURN-relay как основной путь, а не резервный: TURN-сервер выступает посредником, к которому оба участника подключаются исходящим соединением. Это дороже по трафику и добавляет хоп задержки, но предсказуемо работает для доли аудитории, которой прямой P2P-путь недоступен в принципе, а не только «когда повезёт с NAT».
То же касается любого протокола, где сервер должен инициировать соединение к клиенту: push-уведомления через SSE или long-polling с постоянным исходящим от клиента соединением работают предсказуемо, а схема, где сервер сам «стучится» к мобильному клиенту по IP и порту — нет. Практический вывод: для мобильной аудитории соединение всегда инициирует клиент, а доставка данных «от сервера к телефону» — это либо платформенный push (APNs, FCM), либо постоянное исходящее соединение от клиента (WebSocket, SSE), но никогда не входящее соединение к клиенту.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверЗадержка и джиттер радиоканала
Даже без CGNAT мобильная сеть добавляет задержку, которой нет в кабеле. Радиоинтерфейс работает по расписанию: устройство и базовая станция согласуют слоты передачи, часть времени уходит на ожидание своего окна, часть — на переповтор при ошибках на радиоканале (физика радиосвязи менее надёжна, чем медь или оптика, поэтому нижние уровни протокола постоянно что-то перепередают незаметно для приложения, но с добавкой времени). Дальше пакет ещё должен пройти через core-сеть оператора и только потом выйти в обычный интернет — базовая задержка мобильного соединения почти всегда выше и заметно более переменчива, чем у проводного канала того же региона.
Переменчивость — ключевое отличие. На кабеле задержка от пакета к пакету скачет на единицы миллисекунд, на мобильной сети разброс может быть кратно больше: сигнал слабее у края зоны покрытия, канал делится между всеми абонентами в соте, устройство постоянно переключается между режимами энергосбережения (следующий раздел). Это классический джиттер — не то, насколько далеко сервер, а то, насколько предсказуемо к нему доходят пакеты. Подробный разбор механики джиттера, почему он опаснее среднего пинга для realtime-трафика и как его измерять mtr и iperf3 — в статье джиттер важнее среднего пинга. Всё сказанное там применимо к мобильной сети напрямую, только исходный уровень джиттера обычно выше, а стабилизировать его на своей стороне сервера нельзя — можно только заложить в приложение запас на то, что он будет.
Хендоверы между вышками: обрывы, которые нужно пережить
Когда абонент перемещается — идёт по улице, едет в метро или в машине, — телефон в какой-то момент переключается с одной базовой станции на другую, потому что сигнал новой вышки становится лучше сигнала текущей. Это переключение — хендовер (handover), и сеть старается провести его без разрыва, заранее готовя соединение на новой станции.
На практике идеально бесшовно получается не всегда. В момент хендовера возможна короткая пауза в передаче данных — от долей секунды до нескольких секунд в худшем случае (тоннель, зона плохого покрытия, резкая смена соты в движении), — а в отдельных случаях у устройства может обновиться сама IP-сессия, если хендовер проходит между разными сегментами опорной сети оператора. Для TCP-соединения долгая пауза с точки зрения сервера выглядит как зависшее или разорванное соединение; для UDP-потока — как всплеск потерь и джиттера.
Для серверной части это не «нестабильная сеть у клиента, которую надо чинить», а регулярный сценарий, который приложение обязано пережить, а не просто уронить:
- Не считать разрыв TCP-соединения или таймаут запроса поводом сбросить состояние сессии на сервере — пауза в секунды на мобильной сети это норма.
- Клиент должен прозрачно переустанавливать соединение (реконнект вебсокета, повторная авторизация сессии) без потери контекста работы пользователя.
- Для длительных операций закладывать возобновление с середины (
Range-запросы для скачивания, chunked upload с докачкой), а не рассчитывать, что одно TCP-соединение проживёт всю операцию целиком.
Спящий радиомодуль и задержка на «пробуждение»
Радиомодуль — самый прожорливый по энергии компонент телефона, и устройства вместе с сетью совместно борются за то, чтобы он не держал соединение активным без нужды. Между сессиями активной передачи модуль переходит в режимы с пониженным энергопотреблением — «дремлет», пока нет данных для передачи, и должен заново «договориться» с сетью, прежде чем отправить или принять следующий пакет.
Для приложения это выглядит как дополнительная, не всегда предсказуемая задержка перед первым пакетом после паузы в активности: если клиент какое-то время ничего не отправлял (пользователь читал экран, приложение было в фоне), следующий запрос может уйти с заметной добавочной задержкой на «побудку» радиомодуля. Точная величина зависит от модели устройства, настроек оператора и типа сети и заранее не предсказуема, но сам эффект стоит закладывать как систематический, а не как случайный шум.
Практические следствия:
- Первый запрос после паузы почти всегда медленнее последующих — не повод считать, что деградировал сервер.
- Слишком частые фоновые keep-alive контрпродуктивны. Heartbeat каждые несколько секунд не даёт радиомодулю уйти в энергосберегающий режим — сажает батарею клиента без реальной пользы и грузит сервер. Держите интервал keep-alive настолько редким, насколько позволяет логика, и полагайтесь на TCP keepalive/таймауты вебсокета, а не на частый собственный пинг.
- Таймауты на сервере должны быть терпимее к мобильному клиенту, чем к внутреннему вызову между микросервисами в одном дата-центре: то, что там — авария, для мобильного клиента после паузы — рядовая ситуация.
Операторские прокси: трафик меняется по дороге
Часть операторов пропускает трафик через промежуточные прокси и оптимизаторы, которые вмешиваются в содержимое соединения, а не просто маршрутизируют пакеты. Общая идея — снизить нагрузку на радиоканал и сэкономить трафик абонента: за счёт пережатия изображений на лету, кеширования статичного контента ближе к абоненту или иной модификации HTTP-ответов на пути. Конкретный набор техник и то, какие типы контента затрагиваются, у каждого оператора свои и не документируются публично — важен сам факт, что «отправлено сервером» и «получено клиентом» на мобильной сети не всегда совпадает байт в байт, если соединение идёт по обычному HTTP.
Что стоит учитывать на серверной стороне:
- HTTPS закрывает вопрос вмешательства в контент. Зашифрованное соединение оператор в общем случае модифицировать на лету не может (максимум видит метаданные, кто с кем соединяется) — для трафика, где важна точность содержимого (изображения без потери качества, файлы с проверкой контрольной суммы, API-ответы с точным
Content-Length), это снимает вопрос независимо от того, практикует ли конкретный оператор оптимизацию трафика. - Не полагайтесь на то, что заголовки кеширования долетят в исходном виде без шифрования — на HTTP-трафике прокси на пути может переинтерпретировать
Cache-ControlиETagпо-своему. - Если приложение само сжимает изображения под мобильную аудиторию, не удивляйтесь двойному пережатию на части устройств — это взаимодействие с чужой оптимизацией на пути, а не баг вашего пайплайна, и лечится тем же HTTPS.
Как проектировать бэкенд под мобильную аудиторию
Из перечисленного складывается набор принципов для архитектуры бэкенда, если заметная доля клиентов приходит с мобильных сетей.
Не полагайтесь на стабильность соединения — стройте retry-логику и идемпотентность. Любой запрос, который можно безопасно повторить, должен быть повторяемым клиентом без побочных эффектов. Для операций с побочным эффектом (заказ, списание, отправка сообщения) нужен идемпотентный ключ, который сервер сверяет перед повторным выполнением:
POST /api/orders
Idempotency-Key: 8f14e45f-ceea-467e-9e21-3a3d1c9b6c4a
{"item_id": 42, "qty": 1}
Сервер при повторном запросе с уже виденным Idempotency-Key не создаёт заказ заново, а возвращает результат первой попытки. Это снимает главный риск ретраев на нестабильной сети: клиент отправил запрос, соединение прервалось на хендовере до получения ответа, и он не знает, дошёл ли запрос — но может повторить его безопасно, не создав дубликат.
Сами ретраи на клиенте стоит делать с экспоненциальной задержкой и джиттером в паузе между попытками, а не синхронно и сразу — иначе массовый разрыв соединений (например, у абонентов одной соты после короткого сбоя оператора) превращается в синхронный всплеск повторных запросов, который сам может уложить бэкенд не хуже DDoS-атаки. Разбор именно такого инцидента на стороне бэкенда — в статье ретраи без джиттера превратили чужой сбой в наш DDoS.
Не полагайтесь на входящие P2P-соединения к мобильным клиентам. CGNAT делает это ненадёжным по определению — проектируйте протокол так, чтобы соединение всегда инициировал клиент, а для P2P-сценариев (звонки, файлообмен) закладывайте TURN-relay как основной путь для мобильной аудитории, а не запасной вариант на случай неудачи STUN.
Не привязывайте лимиты и защиту от абуза жёстко к IP-адресу. Тысячи разных пользователей за одним CGNAT-адресом оператора — норма для мобильного трафика. Rate limiting должен комбинировать IP с идентификатором сессии, устройства или пользователя, а не банить по голому адресу.
Тестируйте реальный мобильный сценарий отдельно от Wi-Fi и проводного. Эмуляция задержки и потерь через tc qdisc netem на стенде — полезный первый шаг для проверки самой retry-логики:
tc qdisc add dev eth0 root netem delay 80ms 30ms loss 2% reorder 5%
Но она не воспроизводит ни CGNAT, ни хендоверы, ни сон радиомодуля — а именно эти три вещи чаще всего ломают приложения, прекрасно прошедшие тестирование на симулированной задержке. Прежде чем считать мобильный сценарий покрытым, стоит прогнать ключевые пользовательские сценарии (авторизация, длительная загрузка, realtime-соединение) с реального SIM-устройства в движении — пешком по улице или в транспорте, где хендоверы гарантированно случатся, — а не только на стационарном телефоне рядом с роутером в офисе, где мобильная сеть по факту ведёт себя почти как Wi-Fi.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли пробросить порт на мобильном интернете, если попросить оператора?
В большинстве случаев нет — CGNAT работает на уровне оператора, и обычные тарифы не дают доступа к настройке трансляции портов на его пограничном шлюзе. Отдельные операторы предлагают платную опцию «белый IP» для мобильного интернета, но это скорее исключение, чем правило.
Правда ли, что 5G решает проблему CGNAT и джиттера?
Частично. 5G снижает базовую задержку радиоканала и повышает пропускную способность, но CGNAT — решение про нехватку IPv4-адресов у оператора, а не про поколение радиосети, и применяется одинаково и на 4G, и на 5G. Джиттер тоже никуда не девается — просто его абсолютные значения на 5G обычно ниже при прочих равных условиях покрытия.
Стоит ли делать отдельную версию API специально для мобильных клиентов?
Обычно не нужно — протокол может быть общим, но серверная логика (таймауты, retry-подсказки, требования к идемпотентности) должна одинаково хорошо работать и для мобильного, и для проводного клиента: мобильный сценарий стоит закладывать по умолчанию, а не как отдельную ветку кода.
Как понять на сервере, что проблема именно в мобильной сети клиента, а не в сервере?
Прямого способа узнать тип сети клиента нет, если он сам её не передаёт. Косвенный признак — короткие паузы в передаче данных, за которыми следует нормальное продолжение сессии без явной ошибки, и кластеризация проблем вокруг одного CGNAT-адреса оператора у разных, судя по остальным признакам, пользователей.
Обязателен ли TURN-сервер, если WebRTC и так работает у тестировщиков в офисе?
Именно тестирование в офисе на Wi-Fi и вводит в заблуждение — там NAT обычно один и предсказуемый. На мобильной аудитории доля соединений без прямого P2P-пути из-за CGNAT может быть заметной, и закладывать TURN-relay как рабочий, а не аварийный путь стоит заранее, а не после первых жалоб пользователей с мобильных сетей.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →