Добрый день, дорогие читатели. Мы были на WorldSkills (чему я посвящу отдельную статью) в связи с чем эта публикация долго не писалась, так же как и не обновлялся сервер.
Сегодня я расскажу об неожиданной идеи и небольшом анализе сетевых протоколов. Подробности под катом.
Так вот, начнём с протокола TCP/IP. Чем хорош этот протокол: защищённое соединение, обеспечивающее целостность в доставке пакетов, надёжность и безопасность. Я тоже долгое время так думала, пока не произошло страшное… на определённом этапе работы у меня отвалилась виртуалка (т.е. клиент) и сервер зациклился сам на себе. Он просто отправлял себе пакеты на один порт, принимал что-то другим, складывал пакет+пакет (т.е. буквально, добавлял в конец первого данные начала второго, а иногда и все данные второго) и отсылал себе на порт приёма, потом принимал, потом отсылал себе снова и это очень ело ресурсы (от слова 100%).
Я решив найти ответы в интернете, ответы в конечном счёте не нашла и разочаровавшись в этом протоколе стала просить друга помощи в создании нового протокола, на базе UDP.
Перед тем как перейдём к UDP протоколу, я хочу рассказать об изменениях на сервере:
1. Было реализовано нормальное разпоточивание клиентов (что не удавалось ранее).
2. Добавлена более адекватная (на мой взгляд) модель взаимодействия с клиентами.
Сразу перекинусь на второй пункт, так как реализовав его я смогла добиться реализации первого. Раньше я по глупости своей создавала поток, который создаёт поток, который создаёт потоки и отправляет всем сообщения… это было не правильно в корне. На данный момент всё работает по этой схеме:
Пожалуй это самая адекватная модель взаимодействия, которая была вытащена из моей головы.
И при приёме, и при отправке соединение не рвётся, а копируется и отправляется.
Взаимодействие происходит по принципу: услышал-отправил. Не происходит не какой конвертации или проверки данных, но периодически происходит проверка на жизнеспособность клиента (чтобы нам не уходить в вечный цикл). Это происходит по задаваемому параметру — количество выполненных чтений (сколько раз мы слышали от клиента сообщения).
Если он достигает нужной отметки, то на клиента отправляется сообщение спрашивающее жив ли он
И если мы принимаем от него ненулевое сообщение, то заканчиваем проверку и слушаем как и раньше
Слушаем клиентов
Далее я реализовала удаление клиентов из чтения (если сообщение не пришло), но сейчас мы не будем об этом (подробнее узнать как всё работает можно в комментариях к изменениям на моём гитхабе), я хочу перейти к более глубокому анализу сетевых протоколов, ведь это именно то чем мы занимались в своей поездки на WorldSkills (в свободное время).
Я не буду разбирать дебри системы OSI, а скажу следующее — есть два вида транспортных сетевых протоколов (на которых строятся все остальные):
1й. TCP/IP
2й. UDP/IP
Что первый, что и второй строятся на протоколе IP (поэтому и появляется '/IP').
Обсудим плюсы и минусы обоих
+ поочерёдная отправка пакетов
+ базовое шифрование (методом случайного числа)
+ повторный запрос в случае искажения пакета
+- малые порции отправки
— медленная скорость
— тройное рукопожатие при коннекте и двойное подтверждение доставки
— много других не видных на первый взгляд ошибок (с одной из них я и столкнулась)
+ скорость
+- независимость (плевать дошёл ли пакет)
+- пакет не делится на маленькие порции
+ отсутствует рукопожатие в принципе
+- нету ошибок (нету их определения, пакет просто извергается извне и летит… куда глаза его байтные глядят)
— нет очерёдности
— возможны искажения (т.к. нету проверки)
— нет шифрования
Есть так же и множество базирующихся на этих протоколах других протоколов, но мне была важна скорость работы, а не удобство восприятия/дополнительные функции.
Начнём с начала: что нам надо?
1. Стабильное соединение с клиентом (TCP)
2. Высокая скорость передачи данных (UDP)
3. Высокая скорость первоначального соединения (UDP)
4. Понимание как это работает
5. Не должно быть искажений и должна быть очерёдность (TCP)
Два TCP и два UDP свойства. Разве стоила одна ошибка в TCP (на данный момент исправленная в проекте) создание подобия TCP но со своими заморочками? Мой ответ — да!
Почему да? Полазав на форумах я поняла что TCP это случай из UDP, и в TCP есть ещё масса ошибок, которые в UDP отсутствуют (по причине того что UDP отправляет и забывает).
Структура данных пакета
Схема работы сервера на будущем протоколе на этом изображении:
Тем самым мы получаем простую схему работы, клиент подключается — мы передаём его в общий таймаут и при каждой отправки сообщения ждём подтверждения приёма у клиента, при этом работая с остальными клиентами.
Минусами этого решения является отсутствие шифрования (что мы доделаем в клиенте) и непредсказуемое поведение выражающееся в отсутствие выдачи ошибок на экран.
Теоретически получился тот же велосипед, что и на TCP, только без тройного рукопожатия и без деления пакетов на маленькие порции, что я считаю победой.
Увидеть это вы сможете совсем скоро в моём гитхабе.
Спасибо за прочтение и всего вам наилучшего!
Сегодня я расскажу об неожиданной идеи и небольшом анализе сетевых протоколов. Подробности под катом.
Так вот, начнём с протокола TCP/IP. Чем хорош этот протокол: защищённое соединение, обеспечивающее целостность в доставке пакетов, надёжность и безопасность. Я тоже долгое время так думала, пока не произошло страшное… на определённом этапе работы у меня отвалилась виртуалка (т.е. клиент) и сервер зациклился сам на себе. Он просто отправлял себе пакеты на один порт, принимал что-то другим, складывал пакет+пакет (т.е. буквально, добавлял в конец первого данные начала второго, а иногда и все данные второго) и отсылал себе на порт приёма, потом принимал, потом отсылал себе снова и это очень ело ресурсы (от слова 100%).
Я решив найти ответы в интернете, ответы в конечном счёте не нашла и разочаровавшись в этом протоколе стала просить друга помощи в создании нового протокола, на базе UDP.
Часть 1: «Изменения на сервере»
Перед тем как перейдём к UDP протоколу, я хочу рассказать об изменениях на сервере:
1. Было реализовано нормальное разпоточивание клиентов (что не удавалось ранее).
2. Добавлена более адекватная (на мой взгляд) модель взаимодействия с клиентами.
Сразу перекинусь на второй пункт, так как реализовав его я смогла добиться реализации первого. Раньше я по глупости своей создавала поток, который создаёт поток, который создаёт потоки и отправляет всем сообщения… это было не правильно в корне. На данный момент всё работает по этой схеме:
Пожалуй это самая адекватная модель взаимодействия, которая была вытащена из моей головы.
И при приёме, и при отправке соединение не рвётся, а копируется и отправляется.
Взаимодействие происходит по принципу: услышал-отправил. Не происходит не какой конвертации или проверки данных, но периодически происходит проверка на жизнеспособность клиента (чтобы нам не уходить в вечный цикл). Это происходит по задаваемому параметру — количество выполненных чтений (сколько раз мы слышали от клиента сообщения).
Если он достигает нужной отметки, то на клиента отправляется сообщение спрашивающее жив ли он
match item.stream.write(b"you") {
Ok(ok) => {}, Err(e) => {/* Выходим из приёма */ break;}
, };
И если мы принимаем от него ненулевое сообщение, то заканчиваем проверку и слушаем как и раньше
let mut buf_q:[u8; 256] = [0; 256];
let mut buf_q_else: [u8; 128] = [0; 128];
let mut recv_val = false;
let mut b_false_true = false;
loop {
item.stream.read(&mut buf_q);
if buf_q.starts_with(&buf_q_else) {
recv_val = recv_.recv().unwrap();
if recv_val == true {
b_false_true = true; break; } }
}
if b_false_true == true { break;} /* это всё в ещё одном loop цикле, который и заставляет поток не завершаться и слушать клиента дальше */
Слушаем клиентов
item.stream.read(&mut buf); println!("Принимаем сообщения [{:?}]", item.stream);
if buf.starts_with(&q) == false { sender_clone.send(buf).unwrap(); }
/* где q - пустой байтовский массив (проверка нужна для того чтобы не передавать пустые сообщения всем клиентам) */
Далее я реализовала удаление клиентов из чтения (если сообщение не пришло), но сейчас мы не будем об этом (подробнее узнать как всё работает можно в комментариях к изменениям на моём гитхабе), я хочу перейти к более глубокому анализу сетевых протоколов, ведь это именно то чем мы занимались в своей поездки на 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, только без тройного рукопожатия и без деления пакетов на маленькие порции, что я считаю победой.
Увидеть это вы сможете совсем скоро в моём гитхабе.
Спасибо за прочтение и всего вам наилучшего!
Комментарии (6)
blind_oracle
21.02.2018 16:54В гугле для решения этих проблем придумали QUIC
А в остальном да, много ошибок: никакого шифрования в TCP нет; в UDP тоже есть контрольные суммы для проверки целостности, просто нет автоматического ретрансмита битых пакетов. Двойное подтверждение — что имелось в виду непонятно. На TCP-пакет в ответ приходит один ACK.koito_tyan Автор
22.02.2018 12:04И сервер отправляет сообщение что этот аск пришёл
blind_oracle
22.02.2018 12:28Не отправляет. Three-Way-Handshake идет только при установлении соединения. Изучайте протокол TCP.
Более того, ACK приходит не на каждый пакет, а на окно (некое количество неподтвержденных пакетов), которое меняется динамически в зависимости от количества потерь в канале. За это отвечают congestion-алгоритмы.
vesper-bot
Интересно, как это клиент вместо сервера стал принимать свои же пакеты? Слетела исходящая маршрутизация, в результате чего маршрут по умолчанию оказался через lo?
koito_tyan Автор
К сожалению я потеряла те скриншоты и не смогу показать их… постараюсь восстановить старую версию сервера из гитхаба и выложить сюда ссылку. Как я поняла такая ошибка не единична (как и на форумах об этом говорили, так и наши учителя/сис админ), и так как я не понимаю с чем она связанна и почему при падении клиента не рвалось соединение, а получилось зацикливание — пришлось делать новый протокол
ZurgInq
Возможно вы простое не умеете готовить TCP/IP. Не являюсь специалистом, но некоторые утверждения касательно TCP вызывают сильные сомнения, например «шифрование» и «двойное подтверждение».
Что бы при падении клиента рвалось соединение, необходимо вручную отслеживать таймауты и сетевую активность, в ином случае сервер и клиент никогда не узнают, что соединение развалилось.