Привет, Хабр... Да, я опять забыл как называются местные пользователи (хабаровцы? хабровичане? хабровички?.. :), хотя и читал об этом совсем недавно. Бывает, в любом случае, на этот раз я предлагаю простую и достаточно элегантную идею из сетевого мира. И, прежде чем начнём, я оговорюсь, что язык программирования из прошлой статьи ушёл на покой, уступив место молодым и более перспективным наследникам.

Суть

Открывать сетевой канал между субъектами на сразу определённое количество времени либо пакетов с возможностью продления на указываемое число?

Цитата выше — это моя записка на телефоне, которая описывает основополагающую мысль. Представьте, стандартную ситуацию в данном контексте: клиент отправляет серверу запрос на создание соединения и тот соглашается:

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 числа:

  1. 0x00 — EndPacket

  2. 0x01 — DataPacket

  3. 0x10 — OpenConnection

  4. 0x11 — ExtendConnection

Такого же типа пакета, но только у последних двух:

  1. 0x00 — Отказ

  2. 0x01 — Согласие

  3. 0x10 — Зарезервировано

  4. 0x11 — Запрос

И наконец, 32-битным неотрицательным, целым числом обозначающим длину тела-содержимого-нагрузки (body/content/payload) и самим упомянутым. Нет, у EndPacket он также присутствует, но я предлагаю использовать какое-нибудь магическую строку или адрес-порт отправителя/получателя. Эквивалентно у Open- и Extend- Connection пакетов в Согласии, в Отказе — сообщение об ошибке (возможно, с её кодом в начале?) и одним/двумя аргументами в Запросе.

Что же насчёт оставшейся структуры, то во-первых, нужно этот протокол обернуть в IP, а во-вторых, я сам понятия не имею... Вот так как-то. Безответственно, но как есть ;-)

Заключение

Лично я считаю, что этот протокола, даже под другим названием, может стать официальным мировым интернет-стандартом из-за своих локаничности и детерминированности... который сможет в итоговом варианте послужить серьёзной альтернативой существующим TCP, UDP и подобным... Увидим, когда подам соответствующий документ в IETF и пройду через тернии к звёздам (ха-ха :). В любом случае, я ожидаю здесь, к статье на Хабре, комментариев с критикой и предложениями. Спасибо за чтение? Не?