Содержание статьи +
- Кратко
- Зачем это читать
- Надёжность – требование защиты данных, а не только вопрос UX
- Почему связь рвётся посреди приёма
- Машина состояний: как заметить, что начались проблемы
- ICE restart: главный способ восстановления
- Лестница эскалации: от ICE restart до полной пересборки
- Плавная деградация: уступить, но не сломаться
- Переход на телефон: когда интернета не хватает
- Надёжность сигналинга: канал, о котором забывают
- Частые ошибки
- Где здесь Фора Софт
- Главное
- Что читать дальше
Это инженерные рекомендации, а не юридическая консультация. Детали уточняйте у квалифицированного юриста.
Кратко
Клинический видеозвонок должен пережить плохую сеть. Если приём оборвался, пациент теряет доверие к сервису, врач – время, а 152-ФЗ и приказ ФСТЭК № 21 требуют защищать медданные не только от утечки, но и от блокирования, то есть обеспечивать их доступность. Главный инструмент восстановления – ICE restart (restartIce()): браузер находит новый сетевой путь и переводит на него звонок, пока по старому ещё идёт медиа, поэтому пациент видит короткую паузу, а не обрыв. Если сеть не пропала, а просто слабая, звонок должен терять качество постепенно и по плану: сначала хуже видео, затем отключаются лишние видеослои, потом остаётся только звук, а в крайнем случае разговор переходит на телефон. В статье – машина состояний, код реконнекта, лестница деградации и резервные каналы: всё то, чем клинический продукт отличается от демоверсии.
Зачем это читать
Если вы решаете, строить телемед-платформу или покупать готовую, надёжность – как раз то, чего не видно на демонстрации и без чего не обойтись в продакшене. Демо проходит на офисном Wi-Fi, а реальные приёмы – на старом ADSL у пожилой пациентки, на мобильном интернете в машине и в больничной сети, где закрыта половина сайтов. Пока механизмы, удерживающие звонок в таких условиях, работают, их никто не замечает, а без них всё рассыпается: застывшая картинка во время сверки лекарств или оценки признаков инсульта – уже угроза безопасности пациента, а не мелкое неудобство. Статья написана для основателя, продакт-менеджера или руководителя клинической ИТ-службы, которому нужно задать вендору или инженеру правильные вопросы: что произойдёт, когда пациент выйдет из зоны Wi-Fi? Как быстро восстановится звонок? Что пациент увидит, пока идёт восстановление? Если внятного ответа нет, вы нашли слабое место.
Надёжность – требование защиты данных, а не только вопрос UX
Большинство команд относят надёжность звонка к пользовательскому опыту: меньше обращений в поддержку, довольнее пациенты. Это так, но цена вопроса выше. 152-ФЗ требует от оператора защищать персональные данные от неправомерного или случайного доступа, уничтожения, изменения, блокирования, копирования и распространения (ст. 19 ч. 1), а блокирование закон определяет как временное прекращение обработки (ст. 3). Сведения о здоровье конкретного человека относятся к специальной категории ПДн, и живой приём – это те же медданные, только передаваемые в реальном времени. В приказе ФСТЭК № 21 обеспечению доступности персональных данных посвящена отдельная группа мер, а ст. 19 152-ФЗ требует ещё и восстанавливать данные после инцидентов. Платформа, которая теряет звонки при обычных сетевых сбоях, не обеспечивает доступность данных, а это уже вопрос не только UX. Подробно об этих мерах – в статье о требованиях к защите ПДн в 2026 году.
У того же требования есть и клиническая сторона. Приказ Минздрава 193н требует проводить консультацию в экстренной форме в срок от 30 минут до 2 часов с момента поступления запроса, в неотложной – от 3 до 24 часов (п. 19), и время идёт независимо от того, работает ли ваша платформа. Если же вы обрабатываете данные по поручению клиники, доступность закреплена ещё и в SLA вашего договора. Поэтому работу над надёжностью стоит считать частью комплаенса: когда безопасник клиники или проверяющий спросит, как система сохраняет доступность медданных при сбое сети, «ICE restart плюс переход на звук и телефон» будет содержательным ответом.
Дальше будем исходить из этого: реконнект – мера обеспечения доступности, и на него стоит заложить время и бюджет.
Почему связь рвётся посреди приёма
Сеть может отказать по-разному, и способ восстановления зависит от типа отказа. Четыре сценария описывают почти всё, что встречается в телемедицине.
Первый – тайм-аут NAT. NAT (Network Address Translation, трансляция сетевых адресов) позволяет домашнему роутеру делить один публичный адрес между многими устройствами. Для этого роутер хранит временные привязки: какое соединение принадлежит какому внутреннему устройству. Если по соединению какое-то время ничего не передаётся, привязка истекает. В тихий момент приёма, когда врач читает карту и все молчат, она может пропасть, и медиапоток обрывается без всяких ошибок, хотя интернет у обеих сторон работает.
Второй – смена сети. Пациент выходит из клиники или из дома на улицу, телефон теряет Wi-Fi и переключается на мобильную сеть. Устройство получает новый IP-адрес, и все медиапути, привязанные к старому адресу, сразу перестают работать.
Третий – перегрузка и потери пакетов: сеть работает, но полный поток не вытягивает. Кто-то в квартире смотрит видео в 4K, базовая станция перегружена, DSL-линия теряет качество в дождь. Здесь звонок не должен обрываться: ему нужно снижать качество, об этом – в разделе о деградации.
Четвёртый – полный обрыв: устройство совсем теряет связь. Тоннель, лифт, зависшая точка доступа, закрытая крышка ноутбука. Пока связь не вернётся, восстанавливать нечего: задача в том, чтобы дождаться её и аккуратно вернуть пациента в звонок.
Надёжная платформа справляется со всеми четырьмя. Типичная ошибка – проектировать под идеальную сеть, которая никогда не меняется, и узнавать об этих сценариях из обращений в поддержку.
Машина состояний: как заметить, что начались проблемы
Прежде чем восстанавливать звонок, код должен заметить, что со связью что-то не так. WebRTC (Web Real-Time Communication – браузерная технология для видео- и аудиосвязи в реальном времени) сообщает о состоянии соединения через два связанных свойства объекта RTCPeerConnection: iceConnectionState и более общее connectionState. По ним приложение и узнаёт, что происходит в сети.
Важных состояний немного. connected и completed: медиа идёт, всё в порядке. disconnected: связь прервалась, но может восстановиться сама – при коротком сбое, при NAT-привязке, которая ещё может обновиться, при мгновенной потере пакетов. failed: связь потеряна и сама не восстановится, пора вмешиваться. closed: соединение закрыто окончательно.
На практике реакцию делают двухступенчатой. Когда соединение перешло в disconnected, не спешите: запустите короткий таймер на несколько секунд и покажите пациенту ненавязчивый индикатор «Переподключение…». Многие такие сбои проходят сами, прежде чем таймер сработает. Если соединение дошло до failed или таймер истёк, а связь не вернулась, запускайте активное восстановление. Две ступени нужны потому, что восстановление не бесплатно: оно добавляет сигнальный трафик и само может ненадолго сбить картинку, так что запускать его стоит, только когда ожидание не помогло. Некоторые команды сознательно начинают восстановление уже на disconnected, не дожидаясь failed: немного лишнего сигнального трафика в обмен на более быстрый возврат. В клинике, где застывший звонок тревожит и врача, и пациента, обычно лучше выбрать быстрое восстановление.
// Двухступенчатое восстановление: сначала наблюдаем, потом действуем.
let recoveryTimer = null;
pc.addEventListener("connectionstatechange", () => {
if (pc.connectionState === "disconnected") {
showPatientNotice("Переподключение…");
recoveryTimer = setTimeout(() => attemptRepair(pc), 3000);
}
if (pc.connectionState === "connected") {
clearTimeout(recoveryTimer);
hidePatientNotice();
}
if (pc.connectionState === "failed") {
clearTimeout(recoveryTimer);
attemptRepair(pc); // действуем сразу
}
});Одно правило избавит от целого класса ошибок: не пишите в незащищённое хранилище логов сырые данные о соединении, IP-адреса и любые метаданные сессии, которые можно связать с пациентом. События реконнекта хочется логировать подробно, но IP-адрес вместе с учётной записью пациента – уже персональные данные, а в контексте приёма – медданные. Храните такие логи в защищённом контуре, как любые другие данные пациента: в России, с контролем доступа и записью в журнал аудита.
ICE restart: главный способ восстановления
Если медиапуть перестал работать, звонок почти никогда не нужно сбрасывать и начинать заново. Достаточно найти новый путь и перевести звонок на него. Этот приём называется ICE restart, и в клиническом WebRTC это главный механизм надёжности.
Сначала коротко о терминах. ICE (Interactive Connectivity Establishment) – процедура, с помощью которой WebRTC в начале звонка перебирает возможные маршруты между устройствами (напрямую, через домашний роутер или через сервер-ретранслятор) и выбирает рабочий. ICE restart запускает этот поиск заново посреди звонка, чтобы найти маршрут взамен сломавшегося. Механизм описан в стандарте IETF RFC 8445, а в браузере вызывается одним методом – RTCPeerConnection.restartIce(), который с апреля 2021 года поддерживают все основные браузеры.
ICE restart подходит для этой задачи по двум причинам. Во-первых, медиа продолжает идти во время перезапуска: браузер держит старый путь открытым, пока не готов новый, и только потом переключается, поэтому пациент замечает короткую паузу, а не обрыв. Во-вторых, перезапуск обходится дёшево: меняются только сетевые учётные данные и маршруты-кандидаты. Ключи шифрования (DTLS-SRTP, которым защищено медиа) и согласованные кодеки сохраняются, так что не нужно ни заново проходить полное рукопожатие, ни пересогласовывать параметры звонка. Поэтому восстановление занимает секунду-две, а не несколько секунд, как при полной пересборке.
Кода здесь немного. Сложность в другом: вызвать его в нужный момент и убедиться, что другая сторона поддержит перезапуск.
function attemptRepair(pc) {
// restartIce() запускает повторный сбор сетевых маршрутов у обеих сторон.
// Перезапуск уйдёт со следующим offer, который отправит ваш сигналинг.
pc.restartIce();
// Затем обработчик negotiationneeded отправляет новый offer
// по сигнальному каналу и завершает перезапуск.
}Сам вызов restartIce() ничего не отправляет другому устройству. Он лишь помечает соединение, чтобы следующий offer, отправленный по сигнальному каналу, содержал новые ICE-учётные данные. Значит, ICE restart работает, только пока жив сигнальный канал. Если он тоже упал (например, WebSocket оборвался одновременно с медиа), сначала переподключите сигналинг, затем перезапускайте ICE. К надёжности сигналинга вернёмся ниже.
Медиасервер – частая ловушка. В звонке один на один два браузера договариваются о перезапуске напрямую. Но большинство клинических платформ пропускает медиа через сервер – SFU (Selective Forwarding Unit), который раздаёт поток каждого участника остальным; он нужен, как только участников трое или требуется запись. Чтобы ICE restart работал через SFU, его должен поддерживать сам SFU. Одни медиасерверы, в том числе open-source, делают это хорошо, другие, особенно построенные вокруг SIP, – нет. Если вы покупаете видео-API или выбираете SFU, вопрос «поддерживает ли сервер ICE restart для восстановления посреди звонка?» должен войти в требования к закупке, а не остаться в сноске. Устройство протоколов ICE, STUN и TURN разобрано в разделе о видеостриминге, здесь повторять не будем; см. NAT, STUN, TURN и ICE простыми словами.
Лестница эскалации: от ICE restart до полной пересборки
ICE restart устраняет большинство отказов сетевого пути, но не все. Если он не помог, переходите к следующей ступени. Каждая следующая медленнее и заметнее для пациента, поэтому не поднимайтесь выше, чем нужно.
Первая ступень – сам ICE restart: секунда-две, медиа не прерывается, пациент почти ничего не замечает. Если перезапуск не сработал (например, потому что сеть у устройства ещё не поднялась), вторая ступень – пересоздать RTCPeerConnection: удалить зависший объект соединения и создать новый. Это медленнее (заново рукопожатие и согласование), пауза для пациента длиннее, зато так можно выйти из состояний, с которыми ICE restart не справляется. Третья ступень – полный перезаход клиента: приложение пациента переподключает сигналинг, заново входит в комнату приёма и заново поднимает медиа. Это тот же вход, только автоматический и с восстановленным состоянием клинической сессии, чтобы никому не пришлось ничего вводить снова. Последняя ступень нужна, когда у устройства просто нет связи: ждать и возобновить. Сессия остаётся открытой на сервере, пациент видит понятное сообщение «Ожидаем переподключения…», а когда связь вернётся, приложение само войдёт обратно.
| Шаг восстановления | Типичное время | Непрерывность медиа | Когда применять |
|---|---|---|---|
| ICE restart (restartIce()) | ~1–2 с | Сохраняется при переключении | Путь сменился или перестал работать; сигналинг жив |
| Пересоздать RTCPeerConnection | ~3–6 с | Короткий чёрный экран | ICE restart не помог; объект завис |
| Полный перезаход клиента | ~5–10 с | Чёрный экран, состояние восстановлено | Сигналинг упал; нужно перезайти в комнату |
| Ждать и возобновить | До возврата сети | Пауза | Полный обрыв: восстанавливать нечего |
Таблица 1. Лестница восстановления. Начинайте с самой дешёвой ступени, которая может устранить отказ, и переходите к следующей, только если она не сработала.
Посчитаем, окупается ли эта работа. Пусть платформа проводит 1 000 приёмов в день, и без логики восстановления в 4% из них сетевой сбой обрывает звонок. Это 40 сорванных приёмов в день: 40 врачей ждут, 40 пациентов записываются заново. Допустим, ICE restart незаметно спасает 80% таких звонков: 40 × 0,80 = 32, остаётся 8. Ступени пересоздания и перезахода вернут почти весь остаток. Оборвавшийся звонок превращается в обращение в поддержку, а двухсекундную паузу никто и не вспомнит.
Плавная деградация: уступить, но не сломаться
Полная потеря пути – лишь один вид отказа. Чаще бывает менее заметный: сеть работает, но полный поток не вытягивает. Здесь звонок нужно удержать, то есть плавно деградировать: снижать качество по заранее заданному плану, а не зависать и не отключаться.
Главный принцип понятен и врачу, и пациенту: звук важнее видео. Приём можно продолжать с застывшей или размытой картинкой, пока собеседники хорошо слышат друг друга, а с пропадающим звуком – нельзя. Поэтому при любом снижении качества в первую очередь защищают звук, а жертвуют видео.
Переходом между ступенями управляет оценка доступной полосы. WebRTC постоянно пересчитывает её сам по обратной связи от получателя (механизмы управления перегрузкой TWCC и REMB; как они работают, рассказано в разделе о видеостриминге, см. оценка полосы в WebRTC). По мере того как оценка падает:
Сначала снижаются разрешение и частота кадров: кодер выдаёт картинку поменьше. Приоритет задаёт настройка degradationPreference: maintain-framerate сохраняет плавность движения за счёт разрешения (подходит, когда пациент двигается), maintain-resolution сохраняет детализацию за счёт частоты кадров (подходит для осмотра кожи или раны). Что выбрать, зависит от клинической задачи, и это полноценное продуктовое решение; подробнее – в статье о клинической планке «достаточно хорошо».
Затем медиасервер отключает лишние видеослои. В современных клинических системах клиент отправляет свой поток сразу в нескольких вариантах качества (simulcast) или одним многослойным потоком (SVC, Scalable Video Coding). Когда сеть у получателя слабеет, SFU просто отдаёт этому участнику слой пониже, не трогая качество у остальных. Подробности – в статье о simulcast и SVC.
Дальше видео ставится на паузу, остаётся только звук. Если даже небольшой видеопоток мешает звуку, перестаньте отправлять видео и прямо скажите об этом пациенту («Видео приостановлено, чтобы звук был чистым»): приём при этом продолжается. Чтобы режим «только звук» работал и на плохой линии, нужна хорошая обработка звука: эхоподавление, шумоподавление, jitter-буфер, который выравнивает неравномерное поступление пакетов. Об этом – в разделе Аудио для видео.
Наконец, когда WebRTC не вытягивает даже звук, разговор переходит на телефонную линию. Это последняя ступень лестницы, и о ней стоит рассказать отдельно.
Переход на телефон: когда интернета не хватает
У некоторых пациентов интернет-звонок не работает вообще: в сёлах и на трассах есть зоны без мобильного интернета, публичный Wi-Fi бывает перегружен, устройство может оказаться слишком старым. Если платформе нечего им предложить, консультация просто не состоится. Последнее средство – самая старая из сетей, телефонная.
Голосовой режим можно обеспечить двумя способами. Проще – оставить только звук в том же WebRTC-соединении (это третья ступень лестницы). Надёжнее – полноценный дозвон через телефонную сеть общего пользования (PSTN dial-in): платформа подключает приём к обычной телефонной сети через SIP-шлюз и транк оператора связи, и пациент входит в консультацию, набрав номер с любого телефона, без приложения и без интернета. Архитектурно шлюз медиасервера связывает WebRTC-звонок с обычным телефонным, а врач остаётся в том же веб-интерфейсе приёма.
Телефонный дозвон продолжает ту же консультацию, а не открывает новую в обход правил телемедицины. Пациент уже вошёл через ЕСИА в начале сессии (ч. 6 ст. 36.2 323-ФЗ). Привяжите дозвон к этой сессии одноразовым PIN-кодом, чтобы идентификация, журнал и будущий протокол относились к ней же, а заключение врач, как обычно, оформит и подпишет УКЭП (ч. 7). Если сессия записывается, аудиозапись считается сопутствующим материалом консультации и по 193н хранится один год (п. 49–51). Допустим ли формат «только голос» для конкретного вида консультации и как он оплачивается (по ОМС, ДМС или как платная услуга), уточняйте у юриста и в договоре со страховщиком; варианты оплаты разобраны в статье оплата телемедицины: ОМС, ДМС, платные услуги. Проектируйте переход на телефон как полноценный сценарий, а не как вынужденную уступку.
Надёжность сигналинга: канал, о котором забывают
Всё описанное выше молчаливо предполагает, что сигнальный канал работает. Сигналинг – отдельное соединение (обычно WebSocket), через которое приложение настраивает звонок и управляет им: обменивается offer/answer, передаёт сообщения для ICE restart, отслеживает, кто находится в комнате. Медиа и сигналинг – два разных соединения, и отказать они могут независимо друг от друга.
Отсюда неприятная ситуация: тот же сбой, что оборвал медиапуть, часто обрывает и WebSocket. Если логика восстановления вызывает restartIce(), а сигнальный канал недоступен, offer с перезапуском некуда отправить, и восстановление тихо проваливается. Поэтому порядок такой: сначала переподключите сигналинг, затем перезапустите ICE. Сигнальный клиент должен переподключаться автоматически с экспоненциальной задержкой (сначала часто, потом всё реже, чтобы не добивать и без того перегруженный сервер), а после переподключения заново проходить авторизацию и входить в комнату приёма. Повторный вход должен быть идемпотентным: если пациент уже «числится» в комнате, ещё один вход ничего не должен ломать, ведь при нестабильной связи клиент может попытаться войти несколько раз.
Поэтому стоит продумать возобновление сессии. Храните состояние клинической сессии (кто участвует в приёме, что показано на экране, переписку в чате звонка) на сервере, а не только в браузере пациента. Тогда после переподключения комната восстанавливается в прежнем виде: пациент, у которого телефон на полминуты ушёл в спящий режим, возвращается в тот же приём, а не в пустоту.
Частые ошибки
В надёжности соединения много ловушек: на демонстрации всё работает, а в продакшене ломается. Чаще всего встречаются такие.
Пересборка звонка при любом сбое. Получив disconnected, приложение уничтожает RTCPeerConnection и собирает соединение с нуля. Сохранённые ключи шифрования теряются, а двухсекундное восстановление через ICE restart превращается в шестисекундный чёрный экран. Сначала вызывайте restartIce(), а пересобирайте соединение, только если он не помог.
disconnected принимают за failed. Состояние disconnected часто проходит само, а мгновенная реакция на каждое такое событие лишь добавляет сбоев. Используйте двухступенчатую схему с таймером.
Забыли про SFU. На клиенте ICE restart реализован безупречно, а медиасервер его не поддерживает. Клиент запускает перезапуск, сервер на него не отвечает, и восстановление не срабатывает как раз в групповых клинических сессиях, где оно важнее всего. Проверьте поддержку на стороне сервера до релиза.
Нет перехода на звук или телефон. Продуман только сценарий, в котором всё работает, а пациент с плохой связью видит лишь сообщение об ошибке. Нижние ступени лестницы нужны, чтобы помощь получили и те пациенты, до которых труднее всего дотянуться.
Медданные в логах реконнекта. Подробные логи реконнекта с идентификаторами, IP-адресами или содержимым сессии уходят в незащищённое хранилище или в зарубежный сервис логирования. Такие логи – медданные: храните их в защищённом контуре и в России.
Где здесь Фора Софт
Потребовать надёжности легко, обеспечить её трудно, и этот опыт достался нам не сразу. Фора Софт разрабатывает видеосвязь реального времени на WebRTC с тех пор, как технология только появилась: видеоконференции, e-learning, стриминг и телемедицина, где застывший звонок – клиническое событие, а не мелкий сбой. Реконнект, смену сети и плавную деградацию мы проектируем так же тщательно, как основные функции: запускаем ICE restart на нужных переходах состояний, проверяем поддержку на стороне медиасервера, строим лестницу деградации вплоть до телефонного дозвона, привязанного к сессии с ЕСИА, и храним все логи реконнекта в защищённом контуре на российской инфраструктуре. Когда доступность данных пациента – одновременно обещание продукта и обязанность по 152-ФЗ, демоверсию от клинического продукта отличает как раз этот слой.
Главное
- Надёжность – требование защиты медданных от блокирования (ст. 19 152-ФЗ, меры доступности приказа ФСТЭК № 21), а не только вопрос UX.
- Следите за connectionState: в disconnected ждите и наблюдайте, в failed действуйте сразу.
- ICE restart (restartIce()) устраняет большинство отказов сетевого пути за 1–2 с, не прерывая медиа.
- Для групповых клинических звонков SFU должен поддерживать ICE restart: проверьте это до покупки.
- Снижайте качество по порядку: разрешение, слои, пауза видео, только звук. Звук берегите до последнего.
- Сделайте телефонный (PSTN) дозвон, привязанный к той же сессии с ЕСИА; допустимость формата «только голос» и его оплату сверьте с юристом.