Танчики в консоли, статья четвёртая: «Новый сервер — новый протокол»
Добрый день, дорогие читатели. Мы были на WorldSkills (чему я посвящу отдельную статью) в связи с чем эта публикация долго не писалась, так же как и не обновлялся сервер.
Сегодня я расскажу об неожиданной идеи и небольшом анализе сетевых протоколов. Подробности под катом. Так вот, начнём с протокола TCP/IP. Чем хорош этот протокол: защищённое соединение, обеспечивающее целостность в доставке пакетов, надёжность и безопасность. Я тоже долгое время так думала, пока не произошло страшное… на определённом этапе работы у меня отвалилась виртуалка (т.е. клиент) и сервер зациклился сам на себе. Он просто отправлял себе пакеты на один порт, принимал что-то другим, складывал пакет+пакет (т.е. буквально, добавлял в конец первого данные начала второго, а иногда и все данные второго) и отсылал себе на порт приёма, потом принимал, потом отсылал себе снова и это очень ело ресурсы (от слова 100%).
Я решив найти ответы в интернете, ответы в конечном счёте не нашла и разочаровавшись в этом протоколе стала просить друга помощи в создании нового протокола, на базе UDP.
Часть 1: «Изменения на сервере»Перед тем как перейдём к UDP протоколу, я хочу рассказать об изменениях на сервере:
1. Было реализовано нормальное разпоточивание клиентов (что не удавалось ранее). 2. Добавлена более адекватная (на мой взгляд) модель взаимодействия с клиентами.
Сразу перекинусь на второй пункт, так как реализовав его я смогла добиться реализации первого. Раньше я по глупости своей создавала поток, который создаёт поток, который создаёт потоки и отправляет всем сообщения… это было не правильно в корне. На данный момент всё работает по этой схеме:
Пожалуй это самая адекватная модель взаимодействия, которая была вытащена из моей головы. И при приёме, и при отправке соединение не рвётся, а копируется и отправляется.
Взаимодействие происходит по принципу: услышал-отправил. Не происходит не какой конвертации или проверки данных, но периодически происходит проверка на жизнеспособность клиента (чтобы нам не уходить в вечный цикл). Это происходит по задаваемому параметру — количество выполненных чтений (сколько раз мы слышали от клиента сообщения).
Если он достигает нужной отметки, то на клиента отправляется сообщение спрашивающее жив ли он
И если мы принимаем от него ненулевое сообщение, то заканчиваем проверку и слушаем как и раньше
Далее я реализовала удаление клиентов из чтения (если сообщение не пришло), но сейчас мы не будем об этом (подробнее узнать как всё работает можно в комментариях к изменениям на моём гитхабе), я хочу перейти к более глубокому анализу сетевых протоколов, ведь это именно то чем мы занимались в своей поездки на WorldSkills (в свободное время).
Часть 2: «Краткая теория сетевых протоколов»Я не буду разбирать дебри системы OSI, а скажу следующее — есть два вида транспортных сетевых протоколов (на которых строятся все остальные):
1й. TCP/IP 2й. UDP/IP
Что первый, что и второй строятся на протоколе IP (поэтому и появляется '/IP'). Обсудим плюсы и минусы обоих
TCP/IP+ поочерёдная отправка пакетов + базовое шифрование (методом случайного числа) + повторный запрос в случае искажения пакета +- малые порции отправки
— медленная скорость — тройное рукопожатие при коннекте и двойное подтверждение доставки — много других не видных на первый взгляд ошибок (с одной из них я и столкнулась)
UDP/IP+ скорость +- независимость (плевать дошёл ли пакет) +- пакет не делится на маленькие порции + отсутствует рукопожатие в принципе +- нету ошибок (нету их определения, пакет просто извергается извне и летит… куда глаза его байтные глядят)
— нет очерёдности — возможны искажения (т.к. нету проверки) — нет шифрования
Есть так же и множество базирующихся на этих протоколах других протоколов, но мне была важна скорость работы, а не удобство восприятия/дополнительные функции.
Глава 3: «Строим модель и подводим итоги»Начнём с начала: что нам надо?
1. Стабильное соединение с клиентом (TCP) 2. Высокая скорость передачи данных (UDP) 3. Высокая скорость первоначального соединения (UDP) 4. Понимание как это работает 5. Не должно быть искажений и должна быть очерёдность (TCP)
Два TCP и два UDP свойства. Разве стоила одна ошибка в TCP (на данный момент исправленная в проекте) создание подобия TCP но со своими заморочками? Мой ответ — да!
Почему да? Полазав на форумах я поняла что TCP это случай из UDP, и в TCP есть ещё масса ошибок, которые в UDP отсутствуют (по причине того что UDP отправляет и забывает).
Структура данных пакета
Схема работы сервера на будущем протоколе на этом изображении:
Тем самым мы получаем простую схему работы, клиент подключается — мы передаём его в общий таймаут и при каждой отправки сообщения ждём подтверждения приёма у клиента, при этом работая с остальными клиентами.
Минусами этого решения является отсутствие шифрования (что мы доделаем в клиенте) и непредсказуемое поведение выражающееся в отсутствие выдачи ошибок на экран.
Теоретически получился тот же велосипед, что и на TCP, только без тройного рукопожатия и без деления пакетов на маленькие порции, что я считаю победой.