The Jerusalem PostBoy George announces second concert in Israel dedicated to Nova music festival survivorsCNN TürkTürkiye otomotiv ihracatı 9 ayda 31,1 milyar dolara ulaştıESPNThrough Dak Prescott's eyes: The Cowboys QB breaks down a play and all he seesPunchFirst Niger Bridge set to reopen soon – Federal controllerBollywood HungamaRhea Chakraborty launches ‘Caught Blushin’ with Staze Beauty, inspired by her viral The Traitors lookInquirerWalang pasok: Earthquake prompts work, class suspensions in GenSanCollider‘Twilight’ Star Jackson Rathbone Rejects “Soulless” AI-Generated Art [Exclusive]ZDF heuteAktuelle Pressemitteilungen des ZDFSouth China Morning PostIndia aims to anchor military ties with US as Indo-Pacific ‘resident power’Sky TG24L'esorcista: Martiri, il teaser trailer dell'horrorThe South AfricanTransfer boost: Bafana star wins award as England, Spain, Germany, France and Italy watchThe IndependentThree sisters who drowned off Brighton beach took their own lives, coroner rules
The Daily Newsstand · Free, Always
Thursday, October 8, 2026

Пережить обрыв связи и потерю пакетов: как мы выбирали транспорт для протокола Pulsar

Translate

Привет. Я Александр Донин, я технический менеджер платформы VDI и терминального доступа Termit.  В прошлой статье мы рассказывали, почему делаем собственный протокол удаленного доступа Pulsar с нуля, а не форкаем Open Source. Это не значит, что мы вообще против Open Source — используем его, когда это экономит время и не создает проблем: например, в Termit мы используем PostgreSQL для хранения данных и NGINX как веб-сервер, да и начинал Termit с RDP и X2Go. Транспорт в Pulsar — это еще один пример, когда компонент Open Source дал нам как раз то, что требовалось, и вошел в протокол в виде модуля.

Именно на уровне транспорта решается, помешает ли передача тяжелого файла мыши и клавиатуре, сколько потерь пакетов протокол выдержит, переживет ли сессия обрыв связи. Расскажем, почему остановились на QUIC, а не на легаси-протоколах, и с чем пришлось разбираться при реализации.

Зачем вообще отдельный транспорт

Pulsar рассчитан на сценарии, которые для Enterprise-заказчиков не редкость, а норма: нестабильные каналы связи, много одновременных сессий при большом числе пользователей, переключение сети без разрыва соединения. Для банков, нефтегазовых и энергетических компаний, у которых много удаленных площадок со слабой связью, это не форс-мажор, а обычный рабочий день.

Заказчики хотят простую и надежную систему, которая не разваливается на плохом канале и не требует отдельной настройки под каждый сценарий подключения. Транспортный уровень — это ровно то место, где эта надежность либо есть, либо ее нет, вне зависимости от того, насколько хорошо сделано все остальное в протоколе.

Что не устроило в TCP и в голом UDP

Есть старая айтишная шутка про эти протоколы:

  • Я знаю отличную шутку про UDP, но не факт, что она до вас дойдет. 

  • Я знаю отличную шутку про TCP, но если она до вас не дойдет, я буду повторять ее снова и снова, пока вы ее не подтвердите.

Шутка почти в точности описывает суть: UDP ничего не гарантирует, а TCP гарантирует доставку ценой постоянных подтверждений и повторов. Нам не подошел ни один вариант в чистом виде.

Решающая проблема TCP — не задержка на установке соединения, хотя она тоже есть. Дело в том, как TCP ведет себя, если по одному соединению передавать несколько разных потоков данных: скажем, изображение экрана, сигналы мыши и клавиатуры, буфер обмена. TCP гарантирует доставку и порядок, и ради этого блокирует всю очередь. Если один пакет потерялся или повредился, TCP обязан переслать именно его, а все, что идет следом, включая данные из совершенно других логических потоков, ждет, пока это не случится. Хуже того: если механизм Selective Acknowledgment (SACK) не сработал, при потере пакета TCP пересылает все, начиная с последнего подтвержденного, даже если что-то после потерянного пакета уже успешно дошло.

Протоколы, которые исторически строились на TCP, решают это в лоб: открывают отдельное TCP-соединение под каждый канал данных. Десять типов данных — десять соединений. Это работает, но обходится дорогой ценой: каждое соединение — это отдельные сетевые ресурсы и отдельный порт, а держать открытыми десятки портов на сервере это еще и лишняя поверхность для атаки, с которой потом разбирается администратор.

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

Почему QUIC

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

QUIC умеет то, что нам было нужно, из коробки: несколько независимых стримов данных внутри одного соединения. Технически это не «многоканальность» в смысле обвязки поверх TCP, а собственный механизм QUIC. 

Если по одному из таких стримов идет тяжелая передача, например, юзер решил скопировать файл на пару гигабайт, остальные потоки продолжают работать как ни в чем не бывало: курсор мыши двигается, экран обновляется. С TCP в одном соединении такого нет в принципе, а с несколькими TCP-соединениями это возможно, но ценой, о которой говорилось выше.

Также QUIC дает возможность расставлять приоритеты между стримами: управляющий сигнал, то есть движение мыши или нажатие клавиши, можно сделать гарантированно проходящим, а что-то менее критичное, например передачу в буфер обмена, оставить по остаточному принципу.

Если начистоту, то мы не тестировали TCP и UDP на прототипах, чтобы прийти к QUIC методом проб и ошибок. Решение было принято на этапе формирования требований к протоколу — по совокупности уже известных свойств QUIC и накопленной боли с протоколами, которые работают поверх классического TCP. Переизобретать то, что в QUIC уже решено, было бы нерационально.

QUIC при этом пока не полностью стандартизирован, и крупные компании относятся к нему настороженно (и мы в том числе). Но на выбор повлияло и то, что на QUIC уже делают ставку игроки с большими ресурсами: Chrome использует его под капотом, Microsoft — участник рабочей группы по стандартизации QUIC и разработчик msquic — одной из лучших доступных реализаций для C++, которую мы и взяли за основу.

За что QUIC многие хвалят, так это за миграцию соединений. Архитектурно QUIC привязывает сессию не к IP-адресу, а к идентификатору соединения, поэтому смена IP теоретически не должна ее рвать. 

Сессия Pulsar, кстати, переживает и смену активной сетевой карты и рестарт сервера. Соединение само восстанавливается.

Как справились с множеством потоков в одном соединении

Про внутреннюю механику, а именно: что торчит наружу как API, расскажем в одном из следующих материалов. Если коротко: стримы QUIC — это транспортный уровень, а поверх него нам понадобился свой канальный уровень, который умеет из одного физического соединения разложить данные по логическим каналам и правильно их адресовать между клиентом, сервером и другими внутренними процессами. 

Для этого мы используем фреймворк сериализации и RPC, который позволяет распределенным компонентам общаться друг с другом, как будто они находятся в одном процессе, не задумываясь о том, что на самом деле работают по сети.

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

Ну и немного про многопоточность и асинхронность: количество потоков у нас не фиксировано и меняется в зависимости от нагрузки, а асинхронная модель поверх многопоточности используется для оптимизации производительности. Это деталь реализации, которая на поведение протокола для читателя не влияет, поэтому дальше в нее углубляться не будем.

Что показали тесты

Устойчивость к потере пакетов мы тестировали с помощью traffic control (tc) — эмулировали потери и ограничение полосы и смотрели, как ведет себя протокол. 

По предварительным замерам Pulsar переживает потери на уровне 10–20% без критичной деградации качества. Как раз к публикации пересчитали эти цифры заново на более актуальной инфраструктуре, их можно увидеть ниже.

Что касается изображения: у Pulsar пока нет видеокодека, так как мы сознательно вложились в другое. Протокол ориентирован на офисные сценарии, а в них экран в течение рабочего дня в основном статичен: если весь день на экране открыта таблица, в которую изредка что-то вписывают, по сети имеет смысл передавать не поток видео, а только сжатое изображение изменившейся области — иногда это буквально небольшой прямоугольник. Для таких сценариев кодирование дельт картинки эффективнее видеокодека. В будущем к этой теме вернемся, но пока это не в приоритете: с точки зрения транспорта ничего не поменяется, в канале изображения с сервера просто пойдут другие данные.

С ГОСТ-шифрованием поверх QUIC ситуация такая: это технически возможно, но пока не реализовано. В первой статье писали, что TLS выбран основой криптографии именно с расчетом на будущую поддержку ГОСТ. Это по-прежнему так: даже если для ГОСТ-сертификатов понадобится отдельная сборка клиента под другой набор шифров, сам фундамент не меняется, речь не о пересмотре подхода к шифрованию, а о сборке под конкретный набор алгоритмов.

Что дальше

API, на котором строится интеграция с Termit, разберем в следующей статье. А мы продолжаем развивать Pulsar в сторону версии, которая полностью заменит X2Go — именно этого разрыва сегодня реально не хватает на Linux. Затем наступит очередь и реализации под Windows и для веб-клиента. А следом возьмемся зв видеокодеки.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.