Привет, Хабр... Да, я опять забыл как называются местные пользователи (хабаровцы? хабровичане? хабровички?.. :), хотя и читал об этом совсем недавно. Бывает, в любом случае, на этот раз я предлагаю простую и достаточно элегантную идею из сетевого мира. И, прежде чем начнём, я оговорюсь, что язык программирования из прошлой статьи ушёл на покой, уступив место молодым и более перспективным наследникам.
Суть
Открывать сетевой канал между субъектами на сразу определённое количество времени либо пакетов с возможностью продления на указываемое число?
Цитата выше — это моя записка на телефоне, которая описывает основополагающую мысль. Представьте, стандартную ситуацию в данном контексте: клиент отправляет серверу запрос на создание соединения и тот соглашается:
Client: OpenConnection<10 second>
Server: OpenConnection<okey>
Client: OpenConnection<5 packet>
Server: OpenConnection<okey>
Client: OpenConnection<2 hour, 1 packet>
Server: OpenConnection<okey>
Предлагаемые время жизни и количество исходящих пакетов. Всё просто, согласитесь? Также, нужно сразу представить продление подключения:
Client: ExtendConnection<128 minute>
Server: ExtendConnection<okey>
Client: ExtendConnection<18 packet>
Server: ExtendConnection<okey>
Client: ExtendConnection<8 minute, 21 packet>
Server: ExtendConnection<okey>
Поясню, что после завершения обозначенного времени либо при отсутствии его обозначения, вступает в дело счётчик оставшихся пакетов — когда он приравнивается нулю, подключение сразу считается безвозвратно закрытым. Кстати, время отсчитывается от момента отправления пакета-подтверждения у отправителя и от момента получения этого пакета у получателя, а счётчик индивидуален, обозначая входящий лимит-требование на количество. То есть:
Client: OpenConnection<1 packet>
Server: OpenConnection<okey>
Client: DataPacket
// Всё, дальше подключение молча умерло.
Client: OpenConnection<15 minute>
Server: OpenConnection<okey>
// Спустя пятнадцать минут, подключение также молча умрёт.
Client: OpenConnection<15 minute, 1 packet>
Server: OpenConnection<okey>
// Спустя пятнадцать минут, подключение будет жить, ведь есть ещё счётчик.
// Теперь сервер ждёт один пакет от клиента.
Server: ExtendConnection<1 packet>
Client: ExtendConnection<okey>
// А теперь, ещё и клиент ждёт от сервера один пакет.
Client: DataPacket
Server: DataPacket
// Подключение ликвидировано...
Думаю, вы уже заметили, что OpenConnection и ExtendConnection требуют соответствующего пакета-ответа, а DataPacket — нет. Конечно же, может быть не только согласие, но и отказ:
Client: OpenConnection<1000000 day>
Server: OpenConnection<bad, connection lifetime is too long>
// Конец, увы ;)
Сразу тогда сообщу, что есть ещё EndPacket, который нужен лишь формально — чтобы обозначить, что больше пакетов твоя сторона не будет отправлять. Иначе говоря, его использование не обязательно, и он нужен лишь для подобного трюка:
Client: OpenConnection<30 minute>
Server: OpenConnection<okey>, ExtendConnection<1 packet>
Client: ExtendConnection<okey>
// Спустя тридцать минут, когда сервер это обнаружил.
Server: EndPacket
// Подключение считается завершённым с обеих сторон.
Обратите внимание, что отправка пакета после завершения, по той или иной причине (например, из-за разницы во времени жизни подключения у клиента и сервера, так как была задержка между отправкой и получением OpenConnection<okey>), подключения у противоположной стороны является полностью корректным поведением, но тогда этот пакет будет обработан получателем как пакет без подключения. Да, можно просто отправлять пакеты:
Client: DataPacket
Формально, первый пакет подключения (OpenConnection) также отправляется одиночным образом. Предупрежу, что нельзя в подключении открыть подключение.
Ах да, теперь самое важное: как следствие, в протоколе нет разделения на клиент-сервер, только субъективное отправитель-получатель (Sender и Receiver).
Пояснения
Для тех кто назовёт это слишком сложным или потребует код, я предлагаю простой алгоритм:
Receiver: Ожидаем входящих пакетов...
Sender: Отправляет пакет EndPacket получателю.
Receiver: Получет пакет от отправителя.
Receiver: Проверяет есть ли у него запись со связкой IP адрес-порт отправителя.
Receiver: Если нет, то просто обрабатываем одиночный пакет (например, игнорируем все кроме OpenConnection от определённого диапазона адресов), иначе продолжаем:
Receiver: Проверяем timestamp (метку времени; например, 09.10.2025 12:23:56) окончания подключения.
Receiver: Если она равна нулю (условное обозначение отсутствия) или уже была пройдена (например, сейчас 09.10.2025 12:24:06), то удаляем запись и обрабатываем как одиночный пакет, иначе продолжаем:
Receiver: Проверяем входящий счётчик-лимитёр пакетов.
Receiver: Если он равен нулю, то удаляем запись и обрабатываем как одиночный пакет, иначе обрабатываем как пакет из соответствующего подключения.
И, конечно же, небольшой кусочек кода на Си:
// Напиши потом, а то сейчас лень писать эту мелочь :)
// Да и спать пора, имей совесть, Ваня...
P.s. Почему Хабр даёт выбрать лишь C++ как язык кода?..
Безусловно, нужно добавить ещё подобную проверку, но для всех записей подключений, например, в потоке для приёма/отправки пакетов, основном потоке ОС или ещё где-нибудь.
В остальном... Я представляю это как простой идентификатор в виде двухбитного big-endian числа:
0x00 — EndPacket
0x01 — DataPacket
0x10 — OpenConnection
0x11 — ExtendConnection
Такого же типа пакета, но только у последних двух:
0x00 — Отказ
0x01 — Согласие
0x10 — Зарезервировано
0x11 — Запрос
И наконец, 32-битным неотрицательным, целым числом обозначающим длину тела-содержимого-нагрузки (body/content/payload) и самим упомянутым. Нет, у EndPacket он также присутствует, но я предлагаю использовать какое-нибудь магическую строку или адрес-порт отправителя/получателя. Эквивалентно у Open- и Extend- Connection пакетов в Согласии, в Отказе — сообщение об ошибке (возможно, с её кодом в начале?) и одним/двумя аргументами в Запросе.
Что же насчёт оставшейся структуры, то во-первых, нужно этот протокол обернуть в IP, а во-вторых, я сам понятия не имею... Вот так как-то. Безответственно, но как есть ;-)
Заключение
Лично я считаю, что этот протокола, даже под другим названием, может стать официальным мировым интернет-стандартом из-за своих локаничности и детерминированности... который сможет в итоговом варианте послужить серьёзной альтернативой существующим TCP, UDP и подобным... Увидим, когда подам соответствующий документ в IETF и пройду через тернии к звёздам (ха-ха :). В любом случае, я ожидаю здесь, к статье на Хабре, комментариев с критикой и предложениями. Спасибо за чтение? Не?
Комментарии (37)

kipar
09.10.2025 10:12Он поверх TCP работает? Если нет то как будут разруливаться потери\дублирование\изменение порядка пакетов?

HardlinePeak936 Автор
09.10.2025 10:12Нет, сразу поверх IP. Читайте внимательней :)

kipar
09.10.2025 10:12Я так и предположил, но что тогда по второму вопросу?
Если открыли соединение на 2 пакета, потом клиент отправил 1 пакет а он пришел на сервер дважды - соединение закроется? А если пакет потерялся - соединение наоборот будет висеть до перезагрузки сервера? Что если потеряется EndPacket\OpenConnection\ExtendConnection - во всех случаях будут либо висящие соединения либо прием пакетов без соединения? Тогда я не понимаю чем это лучше UDP.
HardlinePeak936 Автор
09.10.2025 10:12Потери сугубо приемлемы, что уже упоминалось, а дублирование и, возможно, сохранение порядка можно ввести в протокол. Думаю не будет ничего сложного, если добавить ещё пару простых счётчиков входящих и исходящих пакетов в подключении, а также добавлять текущее значение исходящего в отправляемые пакеты, чтобы на той стороне сверялись и отбрасывали дубли либо ожидали предыдущие. Предугадывая вопрос, безусловно необходимо подумать ещё и над сохранением целостности пакетов.
P.s. Извиняюсь, я вообще не заметил второй вопрос (вторые сутки на ногах ;).

kipar
09.10.2025 10:12потери DataPacket делают бесполезным открытие соединение на заданное число пакетов, так что можно выкинуть его из протокола и оставить только время.
Потери EndPacket делают бесполезным этот тип пакетов в принципе. Зачем нужен пакет который ничего не гарантирует?
Потери OpenConnection\ExtendConnection можно разрулить повторной отправкой по таймауту пока не пришел ответ.
Но это:надо описать в протоколе (а не описывать только идеальную ситуация когда все пакеты доходят)
Раз в N минут будет возникать затык равный таймауту(*2 и больше) при плохом соединении, при этом доставка собственно данных все равно не гарантируется.
и нет, передача данных когда каждый DataPacket ждет подтверждения от сервера будет сильно менее эффективна чем в TCP, так что для надёжной передачи эффективнее использовать TCP.
кстати, а чему этот самый таймаут равен? захардкожен в реализации? определяется по результатам пинга?

HardlinePeak936 Автор
09.10.2025 10:12Выкидывание заданного числа пакета не одобряю, это необходимо, чтобы не пытаться в последнюю секунду отправить последний пакет, если нужно.
EndPacket — сугубо информационный пакет, он уведомляет, что больше ты не будешь отправлять пакеты. Иначе говоря, от его потери ты не станешь отправлять пакеты ещё. Хотя, его потеря неприятна, так как получатель мог бы не ждать от тебя пакетов.
Таймаут, который активируется лишь при ненулевом счётчике пакетов, но вышедшим временем жизни подключения, как я уже говорил в другом комментарии, также указывается в OpenConnection, но не изменяется в ExtendConnection.
В остальном, я не совсем понял о чём речь. Действительно, описывать лишь идеальную ситуацию не есть гуд, но то же самостоятельное подтверждение получения пакетов не обязательно должно быть на каждый пакет, ведь важно слово «самостоятельное» (можешь и лучше их пачками подтверждать, если тебе оно нужно).

kipar
09.10.2025 10:12Про число пакетов. Я не вижу сценария в котором "ожидать н пакетов" принципиально отличалось бы от "ожидать 999 дней" (с учетом того что любой пакет может потеряться). Сможете привести такой сценарий?
С EndPacket аналогично - во всех сценариях которые я могу представить он просто служит небольшой оптимизации - "если дойдет тогда другая сторона закроет соединение чуть раньше, не дойдет - все равно закроет по таймауту"
Клиент отправил ExtendConnection, ответ от сервера не пришел (либо потерялся запрос, либо ответ на него).Что делать клиенту? Я предположил что надо выждать н миллисекунд (скажем пинг измеренный ранее*2) и заново отправить запрос. Ок, есть другой вариант - слать раз в минуту ExtendConnection(10 минут) вперемешку с данными и не дожидаясь ответа надеяться что хоть один из десяти пройдет. Если 9 не прошли можно даже начинать "паниковать" и слать их раз в минуту, тогда вопрос с таймаутом снимается (конечно ExtendConnection должен перетирать время а не прибавлять, иначе избыток будет набегать).
В текущем виде протокола не вижу преимуществ над UDP. Любые гарантии доставки/упорядоченности пользователь должен реализовывать сам поверх протокола и в JPP и в UDP, смысл от ограничения на число сообщений в JPP неясен учитывая сказанное выше, ну а таймаут из JPP легко реализовать и на UDP - сервер может рвать соединение если от клиента не пришло пакетов в течении н секунд, а клиент слать пустые пакеты раз в секунду даже если нет данных для обмена.

HardlinePeak936 Автор
09.10.2025 10:12Просто замечу, что это неприятно — ставить timeout на 999 дней... Смысл в том, чтобы гонять любые данные условные пять минут, а когда они закончатся, отправлять либо все необходимые для завершения обмена данные не обращая внимания на время (иначе пришлось бы пытаться попасть в последние секунды, со всеми вытекающими ;) либо отправить ExtendConnection, чтобы продолжить обмен (и в конце-концов прийти к первому варианту).
И вы правы, это просто оптимизация! И EndPacket, и вся идея :)
Третий пункт... это в принципе неправильная логика и мышление. Установи примерно нужное тебе время в самом начале, а потом уже, если поймёшь, что его не хватит (элементарно, потому что подходит к концу), расширяй подключение. Не пришёл ответ? Пробуй снова! Всё равно не получилось? Создай новое подключение! Нет? А оно тебе вообще нужно?
Если и с учётом этого комментария я вас не переубедил, то мне пожалуй нечего вам сказать. Возможно, вам моя идея действительно полностью бесполезна ;)

kipar
09.10.2025 10:12неприятно — ставить timeout на 999 дней
По-моему еще неприятнее ставить ограничение 10 пакетов, чтобы дошли 9 (последний по-любому подтвердить не выйдет т.к. при успешном приёме он закрывает соединение) и в итоге результат был тот же как от 999 дней.
Установи примерно нужное тебе время в самом начале
Бывают ситуации когда время известно? Даже браузер не знает сколько времени будет качать страницу, может там гифка гигабайтная. Зато он узнает что пора закрывать соединение когда докачает.
Возможно, вам моя идея действительно полностью бесполезна ;)
Судя по комментариям она бесполезна всем)

HardlinePeak936 Автор
09.10.2025 10:12А почему последний из десяти нельзя подтвердить? Если вы про то, что десять пакетов отправлено, значит нужно закрыть, то вы забываете про входящий счётчик. Уточню, если некорректно понято: OpenConnection и ExtendConnection сообщают твои намерения, в случае со временем жизни — сколько ты предлагаешь сделать время жизни подключения в общем, с чем либо соглашается получатель (СтароеЗначение += НовоеЗначение; у вас обоих), либо отклоняет (СтароеЗначение; у вас обоих), а счётчик пакетов — сколько ты хочешь отправить пакетов. То есть, другая сторона также попросит расширения подключения на десять пакетов, чтобы позже подтверждать твои (и ответ на твой последний ты, соответственно, будешь также ждать).
По поводу известности времени в самом начале — тебе и не нужно знать его. Повторюсь, достаточно поставить условные 5 минут: хватит — прекрасно, нет — расширяй, много — отправляй EndPacket, может отправят его тебе в ответ и закроете подключение.
Эй-эй-эй, не надо так категорично :)

garwall
09.10.2025 10:12Дело осталось за малым - представить, что будет с дропом, джиттер, реордером, изменением источника в сессии, нужна ли абстракция, подобная порту, или хватит только абонента, ну и сущая мелочь, убедить хуавеи и циски, что им это надо/

JBFW
09.10.2025 10:12Ну теоретически, с учётом тенденции к запретам и регулировании по всему миру - софтовый роутер, строящий такую сеть, мог бы быть востребован, а при успехе и железячники подтянутся.
Если оно того будет стоить - и вот это совсем другой вопрос

Storm54
09.10.2025 10:12Неплохо было бы почитать про особенности работы компьютерных сетей, чтобы такую наивную дичь не постить.
В текущем виде (особенно с подсчетом пакетов) совершенно непонятно, как протокол должен работать с учетом переотправки потерянных пакетов.
Также, при таком дизайне можно заддосить сервер, открыв множество соединений на длительное время.

HardlinePeak936 Автор
09.10.2025 10:12Не понимаю, что вам непонятно в переотправке потерянных пакетов: по-умолчанию нет никаких механизмов на этот случай, но если вам необходимо, то можно элементарно присылать DataPacket в ответ, разве нет?
Что же насчёт ддоса, то он возможен и в TCP, и в UDP, так как решается не на этом уровне и достаточно просто: ты можешь отбраковывать неугодные тебе одиночные пакеты (в том числе, и на открытие соединения). Даже по причине подозрительной активности у отправителя...
Кстати, а что наивного-то? :)

Storm54
09.10.2025 10:12Поэтому и стоит сначала ознакомиться со спецификацией TCP и понять, какие проблемы этот протокол решает, прежде чем выдумывать свой протокол, который просто не будет работать в реальных условиях.
В этом и заключается Ваша наивность - эффект Даннинга - Крюгера.

HardlinePeak936 Автор
09.10.2025 10:12Как уже говорил ниже, это лишь сырая и обобщённая идея, которая и нужна для этого — чтобы повысить компетенцию, начать дискуссию и, возможно, получить нечто стоящее. С конкретным эффектом же вы промазали, я вполне себе осознаю свою некомпетентность и довольно поверхностное знание в данной теме... Что скажете? :)

Vindicar
09.10.2025 10:12Не нашел в статье ответа на вопросы:
в каком сценарии использования наличие заранее заданных ограничений по времени/числу пакетов будет полезно?
как планируется выбирать эти ограничения с учётом нестабильной работы сети?

vadimr
09.10.2025 10:12Я тоже не понял главным образом, зачем это нужно. Поддержка таблицы соединений стоит денег, зачем это, если цель – просто передавать пакеты?

HardlinePeak936 Автор
09.10.2025 10:12Насчёт таблицы соединений — осмелюсь заметить, что даже для UDP она поддерживается, просто самими провайдерами и в, так сказать, корыстных целях :)
Соглашусь, что этот протокол залез ещё и на уровне выше (уровень сессий)... но в данном случае — это достаточно обосновано и просто, идя как лёгкий довесок. Нет/да?

HardlinePeak936 Автор
09.10.2025 10:12Всё достаточно элементарно по вашим вопросам:
В любом. Это как сервисные пакеты для проверки соединения в том же TCP, но ты сразу и чётко знаешь сколько их будет или как долго будет соединение. Можете воспринимать это как контракты, например.
Методом научного тыка?.. Не уверен, честно говоря, всё-таки представлял это как: открыл соединение на N минут (предположим, N=5), через N-1 расширил соединение ещё на N, если необходимо, и так на протяжении всей работы. Можно даже производить простенькие вычисления, чтобы не накапливался остаток.
Я ответил?

Vindicar
09.10.2025 10:12Не вижу сходства. Сервисные пакеты отправляются строго по мере надобности.
Ключевой момент именно в этом: на транспортном уровне мы не знаем, что, сколько и как долго собирается передавать приложение. В лучшем случае эта информация будет доступна на уровне протокола самого приложения, подобно Keep-Alive в HTTP. И даже там она передаётся "по факту", без попыток угадать заранее.
А угадать будет непросто. Сеть подтормаживает, и наша прикидка о длительности сессии оказалась некорректной. Соединение разорвётся, несмотря на работоспособность сети? Пользователю это не понравится.
Далее, а что подразумевается под пакетом? IP пакет? Тогда сразу встаёт вопрос: как быть с фрагментацией пакетов? Фрагмент считается за пакет? Как быть с повторной отправкой?
И это все при условии что приложение вообще знает, сколько точно данных и как долго оно будет отправлять. Я вообще навскидку затрудняюсь назвать такой сценарий, кроме разве что передачи файла заранее известного размера. Так это всего один сценарий, из-за чего огород-то городить?
Конечный итог будет простой: приложение будет брать параметры с бааальшим запасом. И тогда ваш протокол мало чем будет отличаться от того же TCP.

HardlinePeak936 Автор
09.10.2025 10:12Согласен с критикой, всё-таки это сырая и обобщённая идея. На случай незнания как раз и нужен ExtendConnection, в остальном — я подумаю над дополнением статьи.

llllXl
09.10.2025 10:12Как будто бы надёжности в этом протоколе нет, да и подделать пакеты третьей стороной в сессии не представляется сложным. Будущее конечно за оптимизацией, но к сожалению не в мире сетевых технологий)

HardlinePeak936 Автор
09.10.2025 10:12Действительно, насчёт механизмов против подделки не было упомянуто, так как они отсутствуют, но их достаточно просто реализовать (как пример: подписью и её проверкой поверх протокола). Возможно, можно сразу учесть и обозначить этот функционал в протоколе... Но я не совсем уверен, стоит ли.
А вот оптимизации нужны и будут даже в сетевых технологиях, вопрос в том какие, как и когда. Разве нет? ;)

ArPi
09.10.2025 10:12Забыли упомянуть какой из фатальных недостатков имеющихся протоколов ваш решает.
Так сказать, сравнительный анализ провести.

HardlinePeak936 Автор
09.10.2025 10:12Фатальных? Скорее всего, никакой. Увы, но это просто нечто вроде оптимизации (приводя аналогию, замена неумелого бумажного самолётика на сделанный более аккуратно и ровно ;)

ArPi
09.10.2025 10:12Я так понимаю старый мем вы не узнали :)
Я как раз это и имел ввиду - в чем заключается оптимизация? Что в имеющихся протоколах на ваш взгляд сделано более неумело? Вот этого анализа не хватает.

HardlinePeak936 Автор
09.10.2025 10:12Действительно, не слышал о таком меме, но принимаю, это забавно и близко. ;)
Оптимизация же в упрощении и указании по ожиданиям, неумелость же в достаточно высокой сложности, которую можно избежать... И я подумаю над конкретным анализом и сравнением.

Nemoumbra
09.10.2025 10:12Статья представляет из себя большой мысленный эксперимент. Надо бы всё-таки чё-нибудь написать. Причём на Си я бы не советовал. Возьмите библиотеку scapy на Питоне, опишите её средствами структуру пакетов, с её помощью сбилдите несколько примеров сессий, сохраните в PCAP. Потом можно будет добавить поддержку в Wireshark через Lua-скрипт, чтобы можно было посмотреть на это в удобной форме.
К моменту, как всё это сделаете, возможно, у вас появится понимание проблем протокола и, возможно, идеи, как это чинить.

Kin77
09.10.2025 10:12Очень нравится читать подобные неопытные наброски, т.к. я также неопытен и интересно как разбирают в комментариях. Благодарю!

cypa
09.10.2025 10:12и где теоретическое моделирование? статистические оценки на реальных данных несущих сетей? примеры применения полезного где?

HardlinePeak936 Автор
09.10.2025 10:12Это сырая идея, имейте совесть! В самом деле, у меня нет возможности и желания проводить подобное «глубокое расследование», да и в этом нет никакой необходимости. ;)
P.s. Полезное применение такое же как у остальных протоколов того же уровня — везде где они используются. А если вы про «почему он лучше старого», то я уже упоминал, что лучше простотой, локаничностью и конкретизацией ожиданий по пакетам.

Cfyz
09.10.2025 10:12Как человек, поработавший с достаточным количеством самых разных сетевых протоколов, вынужден согласиться со всей критикой выше.
На мой взгляд, проблемных момента в вашем предложении два.
Во-первых, так дела не делаются =). Недостаточно просто придумать пару заголовков и мол ну что вы пристали, это пока только идея, сейчас подумаю как это вообще может работать и зачем это может быть кому-нибудь нужно.
Протокол (и особенно базовый транспортный) должен не просто решать какую-то абстрактную задачу в вакууме, типа мы добавили возможность указать количество пакетов чтобы знать сколько нужно ждать пакетов, авось кому-нибудь пригодится. Он должен решать конкретную, практическую проблему, которую неэффективно и/или приходится постоянно раз за разом решать уровнем выше или ниже.
Во-вторых, почему собственно транспортный уровень? С самого начала речь идет о времени сессии и о количестве сообщений, большая часть отмеченных в комментариях проблем из-за недостаточного на транспортном уровне контекста, решать эти проблемы предлагается уровнем выше. По-моему очевидно, что вся затея на самом деле должна быть как минимум на сеансовом, если не вообще на прикладном уровне. Вот там -- пожалуйста, таймауты и предполагаемое количество сообщений могут быть полезны. Или нет.

HardlinePeak936 Автор
09.10.2025 10:12Соглашусь, и я даже упоминал, что протокол залез на уровень выше — сеансовый, но основная проблема в том, что если его перенести выше, то будет некоторое дублирование функционала. Это не особо критично, но ведь этого можно избежать.
HardlinePeak936 Автор
Вне хабра проскочил вопрос «передачи параметров времени и лимита пакетов. Где в OpenConnection указываются те самые 10 second?» Я думал, что это и так понятно, но, на всякий случай, поясню тут и пойду спать (:
<packet id><type id><first arg — time><second arg — count> — всё...
А как кодировать? Можете предложить варианты!