Эта статья написана по итогам разработки геоинформационной платформы «RndFlow.Кругозор» и конкретной прикладной системы на её основе.
В рамках этой работы нам потребовалось интегрировать в единый программно-аппаратный комплекс большое количество российской и китайской аппаратуры, которая выдаёт в систему информацию географического характера (координаты и параметры объектов) в реальном времени. При этом аппаратура, как правило, так же требует и дистанционного управления – частично тоже в реальном времени, как результат активности оператора, частично в режиме настройки и введения в эксплуатацию.
Состояние аппаратуры и канала связи с ней является критичным параметром работоспособности системы в целом, и, следовательно, тоже непрерывно мониторится.
Сама система представляет из себя интеграционную платформу, которая поддерживает
Собственно интеграцию с большим количеством достаточно разнородной аппаратуры различных вендоров
Организацию распределённого сетевого взаимодействия
Формирование и анализ исторических данных (полный слепок входящих данных плюс полная история действий оператора)
Стандартный набор эвристик для препроцессинга, фильтрации и классификации входящих данных
Инструменты для реализации скриптинга (если не хватает встроенных эвристик или требуется специфическая визуализация)
Собственно, визуализацию как статуса подключенной аппаратуры, так и поступающих геоданных
Инструменты управления тревогами и информацией об отказах оборудования
Бизнес-процессы в области квитирования и обслуживания операторами определённых ситуаций
Широкий спектр дополнительных инструментов, таких как геозоны, закладки, различные представления данных и пр.
Система реализована как набор десктопных Java приложений, интегрированных посредством стандартных (шифрованных аппаратным способом) TCP/IP каналов между собой и с интегрируемой аппаратурой.
Система работает в реальном времени. Отдельно отметим, что на рынке бытует верование, что Java не применима в системах реального времени. (Точнее сказать, считается, что всё кроме Си не применимо. Разве что ассемблер.) Мы реализовали несколько значимого размера систем реального времени на Java, причём первая появилась более 10 лет тому назад и работала на ещё тех, древних компьютерах, в условиях достаточно скромной производительности процессора. Практика показала, что существенных препятствий на этом пути нет. Впрочем, на эту тему я планирую написать отдельную статью.
Здесь же упомяну, что, безусловно, система архитектурно проектировалась именно для работы в реальном времени, но принятые проектные решения имеют общеалгоритмический характер и, практически, никак не связаны с применением конкретного языка программирования.
Единственный момент, который мне хочется упомянуть в контексте применения языка программирования со сборкой мусора – это работа с объектами, которые имеют двойное или тройное владение. Пример – закешированные битмепы, хранящие анимированные графические объекты. Ими владеют три сущности – собственно, объект, который они визуализируют, кеш графических элементов и animation engine, который генерирует поток анимационных событий по всей системе. (Честно скажем, можно перепроектировать архитектуру так, чтобы избежать множественного владения этими объектами, но у такой архитектуры будут свои минусы.)
Наивная реализация такой архитектуры приведёт к тому, что графика, которая более не востребована владельцем будет вечно храниться в кеше и незримо анимироваться, то есть – тратить зря процессор и память. Решение – применение мягких ссылок (SoftReference). Решение достаточно простое, и достаточно проработанное.
Обращу внимание, что в языке без сборки мусора эту проблему тоже нужно было бы решать, и решение потребовало бы заметно больших усилий – навскидку, счётчик ссылок с хуками, которые изымают объект из вторичных контейнеров.
Впрочем, я отвлёкся от заявленной темы статьи.
Каждая из аппаратных систем, с которой нам требовалось интегрироваться, фактически, конечно, была программно-аппаратным комплексом. Типично такой комплекс представляет собой два или три слоя компонент – собственно, аппаратура на нижнем уровне, микроконтроллерная программно-аппаратная часть на среднем и, как правило, обычный PC с Линуксом на верхнем. Иногда отсутствует 2 или 3 слой.
В сторону клиентского софта верхний слой, как правило, реализует тот или иной протокол, на базе TCP, UDP, HTTP или даже WebSockets.
Кратко классифицируем эти виды протоколов
Представление данных
Бинарный: 0x1B <pkt type> <pkt len> <binary pkt data> <CRC> – классический пример
Текстовый: JSON, XML
Использование готового решения
Протокол не требует внешних библиотек (парсер XML/JSON не считается)
Протокол опирается на protobuf, websockets, etc.
Синхронизм
Строго синхронный, без запроса клиента сервер молчит. (Пример: MODBUS)
Частично асинхронный: большинство пакетов только в ответ на запрос, но асинхронно (с гарантированной регулярностью) прилетает keepalive и/или актуальные данные.
Полностью асинхронный, ответы могут приходить не в порядке запросов (такого я не видел)
Симплексный протокол - никаких запросов вообще нет, сервер просто транслирует поток апдейтов по UDP на указанный в конфигурации адрес
Пример обмена пакетами для полностью синхронного протокола. Как правило, при такой схеме сервер требует, чтобы клиент делал запрос статуса с гарантированной регулярностью, и отсутствие запроса в течение разумного времени (например, 5 секунд) рассматривает как разрыв связи.

Пример обмена пакетами для частично асинхронного протокола. Сервер отправляет пакет статуса с гарантированной регулярностью без запроса со стороны сервера. Пакет может включать в себя только минимальный объём информации (норма/ошибка), признак наличия апдейта основной информации или сам апдейт данных от сервера.

Пример обмена пакетами для симплексного протокола

Скорость реакции
Время ответа не зависит от выполнения операции, итог операции считывается отдельно
Каждая операция выполняется синхронно, ответ может занять существенное время
Тут требуется пояснение. Одно из устройств, которые нам требовалось интегрировать, выполняло запрос на установку параметров аппаратуры действительно существенное время – буквально несколько секунд. В это время верхний уровень опрашивал микроконтроллеры блоков устройства, а контроллеры переконфигурировали аппаратуру.
Такая реализация проста и удобна разработчику аппаратуры, но с точки зрения системы в целом крайне неприятна. В процессе выполнения такой длительной операции мы не можем ничего сделать с устройством, что для системы реального времени крайне неприятно. Предпочтительна реализация, в которой длительная операция принимается на исполнение и протокол взаимодействия продолжает свою работу. Успех или неуспех длительной операции рапортуется асинхронно или отдельным запросом.
Синхронное выполнение длинного запроса. Клиент вынужден ждать, пока операция на сервере полностью завершится. Другие операции не могут быть исполнены.

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

Поллинг результатов длинного запроса. Клиент выполняет дополнительные запросы, чтобы получить результат длительной операции.

Аутентификация
Отсутствует (и это правильный выбор)
Никакая часть протокола не работает, пока не скажешь пароль
Часть протокола (keepalive, частичный статус) работает
Вообще, разработчики аппаратуры, как правило, делятся на две группы. Вообще нет аутентификации (и слава богу!), или она есть и реализована совершенно параноидально.
Одна из систем и вовсе загнала нас в угол своими требованиями к безопасности. Система требовала при соединении с ней логина и пароля, а при отсоединении – обязательного сообщения о выходе. С каждым логином мог одновременно подсоединяться только один пользователь, а разрыв TCP соединения система не рассматривала как выход клиента из системы. Если при отладке или в силу нестабильности связи (а тестовый контур работал по LTE) соединение рвалось, то последний юзер зависал в системе в залогиненном статусе навечно! Повторное соединение с таким же логином, как следствие, блокировалось до полной перезагрузки сервера.
В итоге количество организационной волокиты при работе с этой аппаратурой превзошло все мыслимые пределы. Каждому разработчику и каждому пользователю пришлось выдать по пачке логинов и паролей, которые, конечно, выглядели как вася1/вася1, вася2/вася2 и т.д.
Почему аутентификация не нужна
Потому же, почему у вас дома нет замка на входе в кухню. Все такие системы разворачиваются в закрытом контуре, и внутри контура все свои. Ключ для подсоединения к VPN – необходимая и достаточная часть защиты. Остальное только создаёт проблемы. Особенно при учёте того очевидного факта, что во всех реальных протоколах, которые нам встретились, логин и пароль передаются открытым текстом. То есть, если закрытый контур будет таки взломан, то первый же запуск wireshark сведёт ценность защиты паролем к абсолютному нулю.
Документация
Безусловно, приз зрительских симпатий в этой номинации получают китайцы. Документация абсолютно у всех написана плохо, отстаёт от реализации, содержит ошибки и разночтения, но после прогона через автоматический перевод с китайского на английский всё это принимает чудовищные формы, и некоторые фразы из таких документов лучше в слух не читать. Есть неиллюзорный шанс вызвать демонов.
В целом, документация на протокол, как правило, содержит описание отдельных пакетов и ответов на них.
К сожалению, логика, смысл и последовательность отправки пакетов, как правило, не описаны. Конечно, если протокол содержит один пакет с одним битом «лампочка горит», разработчик, вероятно, осилит реализацию и восстановит семантику даже имея скромное описание.
Но, к примеру, если у вас есть пакет «регулировка усиления», то хорошо бы хотя бы грубо понимать замысел разработчика – это конфигурационный или оперативный параметр? Его будут крутить только при первичной настройке, при профилактике (раз в месяц) или прямо во время работы каждую секунду? Иногда это знание можно извлечь из руководства оператора, но чаще и оно написано ключницей содержит только скриншот родной операторской программы с движком и подписью «подвиньте вправо, чтобы увеличить усиление».
Документация обязательно должна включать в себя описание модальности прибора – если у него есть взаимоисключающие режимы (передача и приём, например) – это должно быть описано, с указанием того, какие параметры в каких режимах применимы.
Отличная практика – включать в описание примеры пакетов и ответов на них – строго в том виде, в котором они бегают по сети. Мы имели немало счастливых минут, отлаживая реализацию протокола на основе protobuf. В документации забыли уточнить, что разработчик (ЗАЧЕМ!!?) обернул пакеты protobuf в собственные фемтопакеты – добавил перед ними двоичную длину пакета. Надо ли говорить, что это приводило к ошибке вида «взрыв на макаронной фабрике» – парсеру protobuf эта пара байт сносила крышу напрочь.
Отдельная беда документации – единицы измерения. Если написано «азимут», то опытный разработчик поймёт, что это значение в градусах по часовой стрелке от севера. А если написано «угол», то может быть и в радианах против часовой от востока. Если проектировщик протокола – взрослый ответственный человек, то всё будет в единицах СИ (не путать с Си), но с углом и это не спасает.
Кроме единиц хорошо бы указывать диапазон, типовое значение и наиболее применимый диапазон. Усиление от 1 до 100, стандартное значение 75, рекомендованный диапазон от 60 до 80.
Идеальный протокол
Ну и, наконец, мы подошли к ключевой части моей статьи. Розовым мечтам. Представлениям разработчика о том, как выглядит идеальный протокол для интеграции системы реального времени, написанной на современном языке программирования, с аппаратурой.
Транспорт
Я встречал четыре варианта. UDP, TCP, HTTP, Websockets.
Начнём с конца. Вебсокеты не решают никакой полезной задачи, усложняют жизнь тем, что их надо отдельно администрировать и, по всем статьям, являют собой пример адского оверинжиниринга. Не делайте так.
Применение HTTP чуть менее ужасно, но, аналогично, не приносит какой-то осмысленной пользы.
Оставшиеся UDP и TCP примерно равноценны по применимости. Главное преимущество TCP вовсе не в том, что он обеспечивает надёжность – в обычной проводной сети потерять пакет ещё надо суметь, а если теряется много пакетов, как, например, при плохой радиосвязи – то TCP встаёт колом, а UDP хоть что-то доносит. Так что, фактически, в такой ситуации UDP – надёжнее.
В работе с аппаратурой потеря данных не так уж критична - часто следующий пакет несёт ту же информацию, что и предыдущий, но на иной момент времени. То есть при потере пакетов мы теряем промежуточные данные, и, возможно, это лучше, чем вообще ничего не получать.
Выигрывает TCP потому, что хорошо проходит через NAT, и иногда это ценно.
Тем не менее, можно применять и UDP. Предостерегу только от кошмарных конфигураций, в которых сервер отправляет ответы по фиксированному адресу. Правильная реализация – когда сервер шлёт ответ или асинхронный пакет по адресу, от которого получен последний запрос. Такая схема проще (меньше конфигурационных параметров), не требует отдельного механизма конфигурации (как установить параметры протокола, пока протокол не работает?) и сильно облегчает разворачивание и тестовой, и боевой среды.
Пакеты и формат
Мне 300 лет, я выполз из тьмы и держал в руках дюймовую магнитную ленту с реализациями протокола Kermit на паре десятков языков программирования, среди которых Си шёл как «ну и вот вам версия на новомодном языке программирования от молодой операционной системы Юникс».
Число реализаций протокола Kermit
По данным Колумбийского университета, за всю историю протокола Kermit было написано около 180 программ на 36 различных языках программирования.
То есть - протоколов передачи данных я насмотрелся и напроектировался. От советского модема на 300 бод в формате кухонного шкафчика до современных 10-гигабитных сетей со всеми остановками типа xmodem, ymodem, zmodem, uucp, зоопарка протоколов FIDO, mobus, бит-ориентированных спутниковых протоколов и ещё чёрт-те-знает чего.
И вот выныривая из всех этих наслоений истории, я хочу сказать громко и чётко: никогда и нигде не используйте бинарных протоколов.
Я знаю, их многие нежно любят, да и сам я не могу окончательно выкинуть из сердца родные символ начала пакета, символ типа пакета, пару байт длины и неизменные CRC16 в финале.
Почему:
Потому, что уже на следующий день после того, как вы любовно распределите битики пакета между необходимыми к передаче значениями к вам зайдёт инженер и скажет, что усиление канала, которое вы так удачно влепили в 7 бит третьего байта, рядом с битом признака включения канала, теперь имеет диапазон не 0-127, а 2-255. Если протокол не бинарный, вам не придётся убивать инженера и проводить остаток жизни в тюрьме, а уже одно это – достойный аргумент, согласитесь.
Потому, что когда вы, не глядя в зеркало от отвращения к себе, добавите в пятый байт восьмой бит к тем семи, что есть в третьем байте, никто не впишет это в документацию, а если и впишет, то всё равно уже реализованные программы работать тупо перестанут. А ведь мы рассматриваем публичный протокол, который реализован ещё и в программах ваших партнёров. Кто и когда их перепишет?
Потому, что ошибка или неполнота документации для двоичного протокола – смерть, а для текстового – неприятность.
Хорошо, предположим, я вас уговорил и вы не захлопнули сейчас крышку ноутбука с криком «двоичный протокол требует меньшей пропускной способности канала!» – а если захлопнули, то хотя бы посчитали, сколько вам этой полосы надо. А то в поганые 3 мегабита плохого LTE пролезает в секунду 360 пакетов размером в килобайт. И почти 4 тысячи пакетов по 128 байт.
Кстати. Пока мы не перешли к пакетам в текстовом формате, нельзя не упомянуть protobuf. Это шикарная библиотека для организации протоколов передачи данных от Google. Двоичных протоколов.
Очень хорошая. Прекрасная. Мультиязычная. Просто прелесть.
Не используйте ее никогда.
Потому что к описанным выше минусам двоичных протоколов добавится то, что код парсинга протокола сгенерирован чужой программой, и если в нём хоть что-то пойдет не так – а для этого достаточно одного бита расхождения – вы застрелитесь искать, что именно сломалось.
Эта штука сделана для того, чтобы развернуть в одном облаке несколько компонент на разных языках и гонять между ними огромные потоки данных. Да, она экономит время на написание парсера пакета – ценой полной потери контроля.
Имейте в виду, что пакеты protobuf НЕ СОДЕРЖАТ версии спецификации протокола и не имеют никакой избыточности. Если на двух сторонах разная спецификация – библиотека будет просто сходить с ума без внятной диагностики.
Если вы очень хотите двоичный протокол и я не смог вас остановить – лучше опишите его в хорошей спецификации, и любой ИИ напишет вам по этой спеке реализацию за минуты на любом ЯП. Это займёт даже меньше времени, чем интеграция protobuf, и код будет вам подвластен.
Текстовые протоколы
Executive summary: Пакет должен представлять собой JSON в отдельной строке, которая заканчивается символом \n. Это идеал, который нельзя превзойти. Дальше статью можно не читать.
Отдельная ремарка для программистов, которые до сих пор не покидали лона великого и бессмертного языка Си: в других языках символ двоичного нуля не имеет сакрального статуса и заканчивать им строку в протоколе – плохая идея. Просто потому, что функция считывания строки до \n есть везде, а до \0 – не везде.
Почему не XML:
Закрывающие теги реально впустую тратят полосу в канале. Даже мне жалко.
Его тяжелее парсить. Это мелочь, но она будет зря потреблять ваш процессор.
Его тяжелее читать глазами. Отладка будет сложнее.
Критичной разницы нет, и если XML вам очень мил, то – пожалуйста. Но по сумме баллов он уступает везде, и ни в чём не выигрывает.
Кроме того, библиотеки работы с JSON типично поддерживают прямое преобразование пакета в объект целевого языка.
Во всяком случае при использовании Java любой вразумительный ИИ просто по примеру содержимого пакета напишет вам и код класса для хранения таких данных, и код парсинга и генерации для любой известной библиотеки. И это будет простой и компактный код. Как правило - просто пара строк вызова библиотечных методов.
Аутентификация
Как я уже написал выше, никакого смысла защита с помощью открытого логина и пароля не имеет. Просто откажитесь от аутентификации, и люди будут вам благодарны. Если же процент вахтёра в Вашей душе зашкаливает и не велит Вам принимать простые и честные решения – сделайте парольную защиту опциональной и реализуйте её в режиме запрос-ответ. Сервер передаёт клиенту случайную комбинацию байт, клиент добавляет к ней пароль, вычисляет хеш от результата и передаёт его серверу. Сервер вычисляет хеш аналогичным образом и сравнивает результат. Это обеспечивает хоть какую-то защиту.
Итоги
Завершая эту статью я хочу зафиксировать один факт: мы реализовали несколько десятков различных протоколов, разработанных авторами различных российских и китайских устройств. Ни разу не получилось так, чтобы код, реализованный согласно описанию, заработал сразу и полностью и с реализацией не случилось никаких проблем. В 100% случаев уже написанный код потребовал отладки, в описании были найдены ошибки или неполнота и в сумме объём работы по реализации протокола оказался в 3-10 раз больше, чем можно было бы предполагать.
Последняя рекомендация разработчикам аппаратуры с программными интерфейсами: иметь симулятор устройства. Даже совсем тупой симулятор, который по циклу отрабатывает типовое поведение прибора, сильно экономит время.
В целом цикл интеграции прибора выглядит примерно так:
Читаем спецификацию.
Всё понимаем, радостно киваем головой.
Пишем реализацию за полдня.
Неделю бегаем за контактом с той стороны со словами «вы обещали симулятор». За эту неделю его на той стороне дописывают до состояния «падает не сразу, а только через 15 минут».
Запускаем симулятор. Ничего не работает.
Добиваемся, чтобы дали прямой контакт разработчика, который с той стороны писал код.
Он читает спецификацию, пытается уточнить, какой нехороший человек, редиска, её писал. Вспоминает, что он сам и писал. Грустит.
Уточняет, что CRC считается по другом алгоритму, а в пакете есть ещё поле статус. Которое, правда, пока всегда нулевое, но потом обязательно будет использоваться.
Всё понимаем, грустно киваем головой. Корректируем код.
Проходят первые два пакета. Ещё пара дней работы – и жизнь с симулятором начинает налаживаться.
Настала пора тестировать на реальном железе. Тут идеально, если у разработчика железа в офисе есть экземпляр устройства, к которому можно организовать доступ по VPN. И это устройство не позапрошлой версии.
Если нет – нужно получить экземпляр на тестирование, где-то его разместить, организовать канал связи и физический доступ к устройству. Чтобы его иногда перезагружать.
Дальше выяснится, что симулятор местами большой оптимист и реальное железо работает иначе. Цикл коррекции кода может занять прилично времени.
Но – всё со временем наладится, вы напишете полноценный драйвер, UI компоненты для управления устройством (а в нашем случае ещё и транспортные компоненты для управления с других узлов нашей распределённой системы), прогоните тесты, и радостно объявите, что дело сделано, устройство интегрировано.
Устройство будет закуплено вашим заказчиком, установлено на площадку, ваша система будет запущена для работы с ним...
... И вы узнаете, что усиление канала теперь восьмибитное, и в восьмой бит лежит в пятом байте. Слева от входа.
На сём почти прощаюсь. Проектируйте простые тестовые протоколы. Или обращайтесь к нам, мы поможем.
Если эта тема вам интересна – 10 декабря мы (DZ Systems и AXIOM) планируем провести вебинар, на котором будем поднимать тему разработки на Java того, что, как было принято считать, можно написать только на Си. А именно – систем, работающих с аппаратурой в реальном времени.
Участие бесплатное, но необходима предварительная регистрация.
Комментарии (19)

buldo
26.11.2025 14:40А есть какие-то мысли/опыт по поводу MessagePack? Или всё сказанное про protobuf относится и к MessagePack?

victor_1212
26.11.2025 14:40мне 300 лет ...
мне тоже, поэтому вероятно использовал бы ASN.1, общий packet parser для всех устройств в стиле "C" - while(1) {switch (tag) .... }, и по возможности унифицировал state machines для всех протоколов, от разработчиков отдельных устройств по возможности требовал бы их табличных описаний,
от советского модема на 300 бод ...
это супер интересно, такие модемы видеть приходилось, но людей разработчиков не встречал, может быть Вам и ППД программировать приходилось, например для работы X.25?

dzavalishin Автор
26.11.2025 14:40Нет, X.25 обошёл меня стороной. :) Если не считать истории со взломом ситибанка, которая меня почти коснулась. :)
ASN.1 - интересная мысль, но, пожалуй, тоже у простого JSON не выигрывает. Для протокола на базе JSON стейт машина как таковая не нужна. Читаем строку, парсим строку в абстрактное JSON дерево, смотрим значение ключевого поля, которое обозначает тип пакета (в плохом случае, если такого поля нет, - проверяем наличие в пакете полей с данными, однозначно идентифицирующими тип), конвертируем пакет в объект соответствующего класса. Это, фактически, три оператора.

victor_1212
26.11.2025 14:40в свое время много прошлось работать с протоколами OSI/ISO, там ASN.1 основной инструмент определения, достаточно простой и удобный для расширения, поэтому для работ подобных Вашим может быть полезным,
меня таки заинтересовал Ваш исторический опыт с 300 бод модемами, что примерно Вы с ними делали?
в далеком прошлом приходилось иметь дело с модемами, но тогдв уже Hayes interface был фактически стандартом

dzavalishin Автор
26.11.2025 14:40ЦСУ СССР, сбор статистических данных с союзных республик. Там оно в основном по телеграфным каналам шло, отсюда такая работа называлась "телеграфка", но проводили и эксперименты с модемами. Мы, правда, больше внутри Москвы экспериментировали. Если память не изменяет, с Госпланом. Позже были уже компактные настольные модемы 1200КН. На них я даже делал набор номера быстрой манипуляцией линии DTR. :)

Am6er
26.11.2025 14:40Все такие системы разворачиваются в закрытом контуре, и внутри контура все свои. Ключ для подсоединения к VPN – необходимая и достаточная часть защиты. Остальное только создаёт проблемы. Особенно при учёте того очевидного факта, что во всех реальных протоколах, которые нам встретились, логин и пароль передаются открытым текстом. То есть, если закрытый контур будет таки взломан, то первый же запуск wireshark сведёт ценность защиты паролем к абсолютному нулю.
Тут же по-разному. Напр в zero trust подходе, описанный Вами случай попросту не произошёл бы, банально потому, что смотреть особо нечего, даже располагаясь внутри одного "слоя" периметра. Понятно, что это можно вытащить всё в изолированный сегмент сети, вход в который на основании ААА. Зависит от заказчика, в общем.
Хорошо, предположим, я вас уговорил и вы не захлопнули сейчас крышку ноутбука с криком «двоичный протокол требует меньшей пропускной способности канала!» – а если захлопнули, то хотя бы посчитали, сколько вам этой полосы надо. А то в поганые 3 мегабита плохого LTE пролезает в секунду 360 пакетов размером в килобайт. И почти 4 тысячи пакетов по 128 байт.
С передачей данных могут быть разные засады. Например передать нужно по BLE 4.0. Тут будешь ужиматься, как сможешь. Или через Ямал что-то тянуть с удалённого объекта.
Не спорю - текстовый, он, конечно, удобен. Но задачи - важнее. Если стоит задача - максимально удобно его читать - то спору нет.
Небольшой бенчмарк ужималки массива данных длинной 8192 байта, как раз для нужд передачи по BLE:
- chosing delta method: delta=1168 bytes, delta-of-delta=1206 bytes - chosing lzma comp: zstd=759 bytes, lzma=744 bytes orig bytes : 8192 enc bytes : 766 ratio : 10.695x equal check: TrueПочему не XML:
Закрывающие теги реально впустую тратят полосу в канале. Даже мне жалко.
Его тяжелее парсить. Это мелочь, но она будет зря потреблять ваш процессор.
Его тяжелее читать глазами. Отладка будет сложнее.
Критичной разницы нет, и если XML вам очень мил, то – пожалуйста. Но по сумме баллов он уступает везде, и ни в чём не выигрывает.
Кроме того, библиотеки работы с JSON типично поддерживают прямое преобразование пакета в объект целевого языка.
Немного не понял про проблемы парсинга XML. Генерим структуру по wsdl, делаем десериализацию... вроде всё? Или имелось что-то другое ввиду?
Это не в минус JSON сказано, просто хотел понять текст написанного.
Спасибо за статью! Очень интересная!

dzavalishin Автор
26.11.2025 14:40Конечно, ситуации, когда нужно выжать кисоньку до отказа, тоже бывают. И бинарные протоколы в этом случае - неизбежность. Пример с ямалом - вообще отличный, респект.
Парсинг XML просто жрёт больше процессора, чем JSON, и обвязка парсера получается более тяжеловесная.

dzavalishin Автор
26.11.2025 14:40А, я понял Ваш пойнт, сразу wsdl не заметил. Не будет там с той стороны никто предоставлять, как правило. Хотя, конечно, всяко бывает.

d_nine
26.11.2025 14:40Спустя несколько лет ковыряния как своих, так и чужих протоколов пришел к выводу, что лучше потратить время, но сделать поддержку как бинарного, так и текстового протокола (не обязательно json). Первый даст скорость и компактность кому это реально нужно, а второй в самом плохом варианте даст достучаться до железки на коленке, без мучительных попыток вспомнить "да где ж тот самый бит в этом месиве".
После протокола одних "коллег по цеху" глаз до сих пор дёргается, вот вроде и пакуют в стандартную строку ASCII символов, но нет же, байты бьют на "полубайты" (если что, это цитата из документации), докидывают 0x30 и начинаешь ловить по итогу и спецсимволы и текст там, где ждёшь числа. Зачем и чего ради непонятно, ибо получается и хуже битового и сложнее для парсинга, чем текстовые. И все это при фиксированной длине пакета.

dzavalishin Автор
26.11.2025 14:40О да, фиксированная длина пакета, совсем забыл про эту прелесть. Хуже только длина пакета, которая прописана в спеке протокола отдельно для каждого типа пакета. :)

nv13
26.11.2025 14:40Не хуже) длина пакета с данными программируется в dma и тот помещает весь блок данных с порта ввода в буфер соответствующего процесса

nv13
26.11.2025 14:40Перечитал свой пост ещё раз - в огороде бузина, а дядька запил) просто хотел сказать, что мы использовали протокол с фиксированным заголовком+данные для скоростного обмена в сети микропроцессоров. Текста там, правда, взяться было неоткуда, как и сериализации никакой

d_nine
26.11.2025 14:40Фиксированная длинна пакета не то чтобы прямо плохо и ужасно, если нет желания в бинарном протоколе сопровождать данные заголовками и надо гонять сообщения быстро, то вполне приемлемо (за исключением поддержки и ломающегося обмена при апдейтах). Там именно больше вопросов к тому, что какой смысл был пытаться в ASCII строку, если по факту с ней работать один черт невозможно, приходится выгребать все 0x30, обратно клеить "полубайты" в байты и работать по факту с бинарным протоколом. Ещё из "приколов" той же железки, при реверсе протокола выяснилось, что хендшейк идёт через запрос версии (ПО/железа? а вот черт его знает) и на один и тот же запрос приходят разные ответы. Всего ответов оказалось два для более новой железки и один для более старой. В итоге если по какой-либо причине терялся один из запросов для новой железки она наглухо отрубала обмен без какого-либо ответа. Игнорировалась даже команда для софт ребута. Пришлось городить логику с контролем ответа на первое сообщение и дальше уже либо слать второй запрос, либо уже работать по остальной части протокола. Не удивлюсь, если в новом железе тоже поменялся хендшейк.
Ну и вместо обычной unix-time метки был какой-то свой костыль который по сути отличался только начальной датой ¯\_(ツ)_/¯
nv13
У нас на плате несколько чипов обменивались данными через встроеный коммутатор. При использовании udp происходили пропажи датаграм и сбои функционирования, при tcp всё работало. Сами устройства по тому же протоколу типа rpc обменивались с другими устройствами того же типа по сети и с клиентскими устройствами.
При обновлении протокола сохранялась базовая обратная совместимость, что позволяло запускать апдейты на слейвах и клиентах. Для protobuf тоже можно реализовать версионирование - это пользовательская функциональность, но вроде работающая.
dzavalishin Автор
В теории protobuf много чего умеет, в том числе опциональные данные, но реальность такова, что никто не пытается разбираться с этими возможностями - просто де факто выясняется, что протокол изменился и больше ничего не работает. Вот вам новый файлик описания от protobuf, перегенерируйте и перепишите реализацию клиента. Причём изменения не просто уровня "новое поле", а "мы переосмыслили всё" - другие сущности, иначе вложены. Справедливости ради, JSON от такого тоже не очень спасает. Но там хотя бы это видно глазами и, как правило, корректировки возможны даже без документации.
А насчёт UDP - скорее всего, это значит, что потери, всё же, были, причём существенные. Я проводил опыт - если начинать в гигабитной сети флуд, то TCP ложится замертво на порядок раньше UDP. Но, конечно, использовать UDP можно только если протокол по сути идемпотентный и, в основном, состоит из апдейтов одного и того же потока данных. Например, передача очередного значения датчика температуры.
nv13
У нас между чипами было несколько потоков данных мегабит под 10 в максимуме, плюс устройство получало отправляло такие же потоки по сети. И на их фоне команды управления и статусы разные. Просто буферизации не хватало местами и udp пропадали. Это же и причина использования ethernet внутри - проще pci, пропускная способность, дешево, стандартный стек и поддержка и интегрируется с клиентами и удалёнными устройствами.
Про протобуф согласен - для версионности надо заморачиваться.
dzavalishin Автор
Кстати, а если обмен между чипами на одной плате - почему выбрали не что-то совсем низкоуровневое, типа i2c/spi?