Комментарии 103
Часть про то, что вы ненавидите Go, была обязательной, чтобы свои впечатления выразить? То есть, никак не обойтись без того, чтобы хейтить и просто "что-нибудь про UDP" почитать? Раз от статьи не воротит настолько, что решили почитать, значит не так уж и ненавидите, наверное.
К тому же вы у себя в профиле ставите этот язык как на котором разрабатываете (на втором месте, между прочим), и подписаны на хаб (наверное, чтобы поддерживать себя в тонусе).
Сам же автор поступил немного более мудро, и не посмел обронить даже единого слова (!) подмечу: в отличии от меня с Вами. По итогу решаю обобщить нашу с Вами причастность к ейфорическому вмешательству тролля свыше, ибо его -4 вполне вероятно похоронят и нас, ведь Ваши +2 им на противовес поставлены, И МЕСТНЫЙ(АЯ) МОДЕРАТОР БОГ(ИНЯ) ФЕМИДА НЕ СМОЖЕТ ОСТАНОВИТЬ ЭТО, ведь мы абсолютно резонно ответили друг другу.
p.s. и что иронично изволить у меня и вовсе слишком мало кармы для голосования. знаете что бы я сделал? поставил обоим жирный минус за трату своего времени на объяснение этой несуразицы Вам
au revoir, синьоры
У вас с этим какие-то проблемы. Серьезно.
Вы сделали stateful мессенджер для телефона. А теперь придумайте пяток сценариев где это не будет работать вообще.
И на закуску парочку сценариев где это приведет к серьезным проблемам. Не отправленное сообщение, которое отображается отправленным, это серьезная проблема.
Совсем потом можно подумать на тему хранения сессий в WhatsApp. Миллиард активных пользователей. Как вы бы обработали на серверах stateful?
И сразу все станет понятно.
Как прототип/идея весьма неплохо. N сессий тут решается простым KV хранилищем с ключом по ID клиента.
А для синхронизации последнего отправленного сообщения (проблема "отправленного" и не пришедшего сообщения) можно использовать алгоритм из биржевого FIX. Он прост как две копейки и, по моему, отлично ложится на предложенную архитектуру.
Не претендую на истинность, но я бы в свободное время поразмыслил над этим проектом
Решается, только масштабируется не очень. Нам нужен как минимум id клиента, id сессии, ключ шифрования и хеш с таймстемпом последнего сообщения. Пара килобайт точно. Если подумать точно еще что-то вылезет что надо хранить. На миллиарде клиентов это терабайты данных.
Шардирование делать сложно. Пакеты шифрованные. В начале надо терминировать tls. Проблема явно решаемая, но не с налету.
Вот чувствую зря я про совсем потом на писал. Там и без этого пункта проблем с stateful выше крыши.
По поводу того, что вы говорите:
1) Нам нужен как минимум id клиента, id сессии, ключ шифрования и хеш с таймстемпом последнего сообщения. Пара килобайт точно
Давайте посчитаем:
id клиента — 8 байт точно хватит
id сессии — ок, 20 байт должно хватить (sha1, например)
ключ шифрования — его передавать не нужно, достаточно шифровать открытым ключом сервера
хеш с таймстемпом последнего сообщения — неясно, о каком хеше речь, но ок
timestamp — 8 байт (как в примере, с наносекундной точностью)
длина сообщения — 2 байта (больше 64 Кб по UDP в одном пакете в любом случае не послать)
само сообщение — обычно немного, 100-200 байт, плюс оверхед на шифрование, допустим те же 100 байт
Итого:
заголовки: 8+20+8+2 = 38 байт
содержимое: 200 байт
издержки на шифрование: 100 байт
Всё в сумме дает не больше 400 байт на пакет, звучит как не очень много. Максимальный рекомендуемый размер UDP-пакета, чтобы он точно доставился — 576 байт (https://serverfault.com/questions/587625/why-dns-through-udp-has-a-512-bytes-limit), вроде влезаем, даже запас есть.
2) На миллиарде клиентов это терабайты данных.
Если у вас миллиард клиентов, то терабайт метаданных вы сможете хранить с легкостью. Вам ещё и тексты сообщений надо хранить :). И, вероятно, видео, фото и аудио тоже.
3) Шардирование делать сложно.
Может быть и сложно, но это точно задача решаемая, и решений очень много. Более того, даже в том же ВК поток сообщений не такой большой, как кажется — в пике до нескольких сотен тысяч в секунду. Такое количество можно пошардировать просто по остатку от деления на, скажем, 20 серверов, и этого вам хватит надолго. Имеется в виду запросы метаданных, конечно же. Само хранение сообщений потребует большего количества серверов.
4) Пакеты шифрованные. В начале надо терминировать tls. Проблема явно решаемая, но не с налету.
Не надо. Открытый ключ шифрования для сообщения можно зашить прямо в клиент. Чтобы слать шифрованный ответ пользователю, надо один раз при регистрации нового устройства запомнить открытый ключ устройства, который он вам при регистрации пришлет (приватный ключ будет храниться на самом устройстве). Регистрацию и прочее «тяжелое» общение можно делать и по обычному HTTPS / TLS.
5) Вот чувствую зря я про совсем потом на писал. Там и без этого пункта проблем с stateful выше крыши.
Основная проблема — «чувствую» :). Все эти проблемы вполне решаемы, но для прототипа не важны.
Шифруем все в 2 слоя. Первый пакет до сервера, второй само сообщение.
Сессионный ключ клиента это не паблик ключ сервера. Это именно сессионный ключ. Чтобы в случае компрометации серверного ключа трафик нельзя было расшифровать. Да и использовать один ключ неопределенно долгое время это не очень. Азы же.
1. Вы считаете размер пакета, а надо считать размер инфы которою нужно хранить на серверах для поддержания сессии.
2. Всю историю можно хранить на дисках и не париться о скорости. Подгрузка истории это процесс не требующий особой скорости. Клиент готов подождать пока у него загрузится история на новой устройстве. А вот информация о сессиях нужна в памяти. К ней нужен очень быстрый доступ.
3. Проблема не в сообщениях, а в клиентах. С сообщениями все понятно. таймстемп последнего и хеш от него же.
При https да. Именно так и делаем и все просто. Вы же хотите кастомный udp протокол. На сервер будет приходить шифрованный массив. Надо найти от него ключ, расшифровать, найти клиента, валидировать что все ок и уже потом передать на обработку.
4. Сессионные ключи строго обязательны в 2019 году. Я выше написал.
> Шифруем все в 2 слоя. Первый пакет до сервера, второй само сообщение.
Как именно шифровать и использовать ли End2End, я специально не озвучивал, поскольку я не являюсь экспертом ни в построении мессенджеров, ни в области security :).
> 1. Вы считаете размер пакета, а надо считать размер инфы которою нужно хранить на серверах для поддержания сессии.
В любом случае это килобайты, и это очень мало :). Даже если надо хранить, скажем, 10 Кб на пользователя, это всего-лишь 10 Гб памяти на 1 млн пользователей. Сервер с 16 Гб ОЗУ стоит на амазоне 100 долларов в месяц (https://aws.amazon.com/ru/ec2/pricing/on-demand/ — r5.large), и то это пожалуй слишком много :).
> 3. Проблема не в сообщениях, а в клиентах. С сообщениями все понятно. таймстемп последнего и хеш от него же.
> При https да. Именно так и делаем и все просто. Вы же хотите кастомный udp протокол. На сервер будет приходить шифрованный массив. Надо найти от него ключ, расшифровать, найти клиента, валидировать что все ок и уже потом передать на обработку.
Ок, да, нужно сделать несколько запросов в разные сервисы. В чём здесь проблема :)?
> 4. Сессионные ключи строго обязательны в 2019 году. Я выше написал.
Ок, можно шифровать сессионным ключом, противоречий с моей идеей пока что не вижу :).
Миллион пользователей это мало. Мессенджеры нынче живут на миллиарде пользователей. И любая разработка должна быть готова к работе с таким объемом. Миллиард это 10Тб шаренной и доступной со всех узлов входа ОЗУ только на сессии. Это уже много. Очень много. Да и просто держать столько открытых tls сессий это задача ну очень нетривиальная.
Проблема что это плохо масштабируется горизонтально.
Проблема что это плохо будет работать за фаерваллами.
Это будет плохо работать за двойными натами.
Это плохо совместимо с проксями.
Это проблема консистентности статуса клиента и сервера.
Это в конце концов кастомный низкоуровневый протокол. То есть все типовые балансировщики с ним будут работать плохо или не будут вообще.
Реверс прокси работать не будет точно.
И ради чего все это? Чтобы пользователь имел повышенный шанс отправить сообщение при ухудшении сигнала? Оно того не стоит.
Учитывая, что 512 Гб на сервер — это вполне стандартная (чуть выше среднего) современная конфигурация, вам нужно всего 20 серверов, чтобы хранить это. Если учесть резервирование, то 40 серверов. Для хранения самых пользовательских данных вам потребуется в сотни раз больше, так что это не проблема.
> Реверс прокси работать не будет точно.
В целом, в этом нет особой необходимости, весь трафик может принять буквально несколько серверов. К тому же, балансировку на уровне сети никто не отменял.
> Это будет плохо работать за двойными натами.
Поясните, пожалуйста, почему именно за двойным натом это должно плохо работать.
> Проблема что это плохо масштабируется горизонтально.
Уж что-что, а UDP прекрасно масштабируется на любое число машин, поскольку нет состояния между запросами.
> Проблема что это плохо будет работать за фаерваллами.
Возможно.
> Это плохо совместимо с проксями.
Если речь про корпоративный прозрачный HTTP-прокси, то да. Речь шла про мобильный интернет в первую очередь, там нет никаких проксей обычно.
> Это в конце концов кастомный низкоуровневый протокол. То есть все типовые балансировщики с ним будут работать плохо или не будут вообще.
Вы говорите про L7 балансировку, но есть и другие виды балансировки, например L3/L4.
И я бы не сказал, что этот протокол низкоуровневый :). С каких пор UDP стал протоколом низкого уровня?
> И ради чего все это? Чтобы пользователь имел повышенный шанс отправить сообщение при ухудшении сигнала? Оно того не стоит.
А по-моему, стоит, если вы разрабатываете мессенджер и это ваш основной бизнес. Люди целые протоколы изобретают или начинают использовать просто ради улучшения качества связи, как например Apple с Multipath TCP для звонков FaceTime или iMessage — благодаря этой технологии они могут работать даже при более плохих условиях связи, и не прерывать соединение при смене сети — это очень большое конкурентное преимущество по сравнению с остальными.
Учитывая, что 512 Гб на сервер — это вполне стандартная (чуть выше среднего) современная конфигурация, вам нужно всего 20 серверов, чтобы хранить это. Если учесть резервирование, то 40 серверов. Для хранения самых пользовательских данных вам потребуется в сотни раз больше, так что это не проблема.
А если учесть, что плотность пользователей сильно зависит от локации и онлайн в разных локациях разнится в разное время суток, то в реальности даже и это — оценка сверху. Потому что в некоторых регионах и более низкие требования будут.
Совсем потом можно подумать на тему хранения сессий в WhatsApp. Миллиард активных пользователей. Как вы бы обработали на серверах stateful?
Боги послали вам редис с шардированием, например.
Отлично работает гошка прямо на мобильных. См, например, Status.im
Думаю в процессе разработки вашего «отказоустойчивого» протокола этот опыт тоже будет полезен. :)
Ну если сравнивать по latency то вотсапп например неплохо живет и на спутниковом инете — пинг 600-2500 скорость 8-40 мбит в зависимости от погоды...
Опять в очередном меседжере используется прокладка в виде сервера. Почему бы не отправлять сообщения напрямую получателю ведь интернет на это расчитан. Теперь уже и IPv6 есть которому NAT не нужен.
Кстати в GO есть пакет шифрования так что изобретать ничего не нужно.
Потому что ваш телефон извне недоступен, наверное.
С чего это вдруг? Тем более по UDP.
> In the cases of restricted cone or port restricted cone NATs, the client must send out a packet to the endpoint before the NAT will allow packets from the endpoint through to the client. STUN does not work with symmetric NAT (also known as bi-directional NAT) which is often found in the networks of large companies.
Может NAT, может файрволл у опсоса или в самом телефоне, не знаю.
Несколько раз пытался попасть на свой телефон извне — через вайфай получалось, через мобильную связь — нет.
IPv6 операторы потихоньку внедряют. Для IPv4 нужно пробивать UDP порт при помощи UDP hole punch.
К сетям опсосов это тоже относится в значительной мере.
Ну и, конечно же, P2P соединение будет устанавливаться ещё дольше. Но вы можете использовать сервер для кратковременного хранения сообщений, которые зашифрованы ключом вашего собеседника, тогда это будет более безопасно, хотя я не security эксперт, к сожалению :).
Помоему отправить UDP пакет напрямую гораздо быстрее чем через сервер. Ну и использование IPv6 избавит от необходимости преодолевать NAT.
Сервер нужен для первичного входа в DHT сеть (сразу после установки приложения либо после очень долгого отсутствия в сети). Если сеть будет достаточно крупной то вполне сможет обеспечить временное хранение шифрованных не доставленных сообщений пока оба абонента офлайн.
Авторизованные абоненты могут сигнализировать друг другу напрямую о изменении адреса и порта. Если вдруг случится что они сменят адрес одновременно то могут найти друг друга в DHT.
> Помоему отправить UDP пакет напрямую гораздо быстрее чем через сервер. Ну и использование IPv6 избавит от необходимости преодолевать NAT.
Да, если у всех есть белый IP-адрес, то отправка напрямую обычно будет быстрее. Но пока что реальность такова, что белого IP почти ни у кого нет, тем более на мобильном интернете. Ну и IPv6 пока что тоже встречается очень редко, к сожалению.
Ваши варианты интересные, но они не ориентированы на минимальную задержку отправки, поскольку для работы IPv6 без NAT сейчас нужны всякие «костыли» вроде 6to4, которые явно стабильности и предсказуемости в работе не прибавляют. В светлом будущем, когда у всех белый IPv6 и нет NAT, я верю, что ваш вариант действительно будет самым лучшим, как в плане latency, так и в плане отсутствия централизованного хранения чьих-либо данных.
У меня работает на всех провайдерах. Только я беру 64 битную зону. Тунельные провайдеры бы тоже тогда не работали.
Я сейчас нахожусь за NAT.
Перепроверил. test-ipv6.com даёт 7 из 10. Даже пинганул себя сервисом Online Ping IPv6 все пинги пришли.
Я сам 6to4 только что настроил(опять настройки слетели) на своём роутере. Провайдеры не хотят давать IPv6. NAT ни одного из провайдеров я не контролирую. От провайдеров на роутер приходит локальные IP(10.x.x.x).
- Глобальный IP я узнаю в сервисах типа 2ip.ru а далее генерирую IPv6 зону по нему.
- В сервсисе IPv4 to IPv6 Conversion получаю IPv6 адрес.
- Заменяю заменяю один ноль рандомным числом и задаю зону /64
- Задаю IPv6 роутера
- Задаю адрес сервера 192.88.99.1
Далее на всю технику IPv6 прилетает от роутера.
Единственно что если исходящего ipv6 трафика не будет то через некоторое время NAT теряет маршрут к роутеру.
Уверен. Я столько торрентов не качаю сколько вижу на iknowwhatyoudownload.com.
Проблема может быть в фрагментации пакетов. Попробуйте уменьшить MTU для IPv6.
Видимо действительно телеком что то нашаманил.
А жаль, когда работало, было быстрее любых 6in4 провайдеров.
Самое интересное что tracert до 192.88.99.1 мне выдавал тот же HE который представляет бесплатный 6in4 тунель. Только в случае 6to4 и регистироваться не надо. В обратную сторону пакет может идти через другого 6to4 провайдера тем самым не создавая узких мест.
А сейчас:
.......
5 15 ms 14 ms 15 ms 217.107.67.133
6 15 ms 14 ms 14 ms 192.88.99.1
Авторизованные абоненты могут сигнализировать друг другу напрямую о изменении адреса и порта.
На Андроиде и IOS приложение часто впадают в спячку. Операционная система рвет соедения, после выхода приложения или отключения экрана. IP адреса на мобильных устройствах могут менятся каждые пять минут
Опять в очередном меседжере используется прокладка в виде сервера. Почему бы не отправлять сообщения напрямую получателю ведь интернет на это расчитан. Теперь уже и IPv6 есть которому NAT не нужен.
Сервера нужны по-целому ряду причин:
- Без сервера сервера оба собеседника должны быть постоянно в сети и активно обмениватся пакетами присутвия. Это нереально, в особенности на мобильных устройствах, которые уходят в спячку
- Без сервера можно удобно провести целевые атаки на устройства пользователя. Я попробую пример привести: Вы заблокировали собеседника. Он решили Вам отомстить. Установив Ваш айпи, он устраивает атаку на ваш роутер и Вы лишаетесь интернета на день. Вы меняете айпи, но это не помогает, поскольку злойдей снова находит Вас по p2p. На следующий день Вам звонит Ваш провайдер и предлогает перейти на услуги для комерческих клиентов, потому что Вам нужна защита от Ддос-атак на 10К рублей. Вы покупаете это пакет, но злодей снова находит Вас, сканируют ваши порты и находит уязвимость в Виндовс и скачивает Ваши хоме фото, скан паспорта и т.д
Это лишь первые недостатки, которые пришли в голову. Можно еще много найти минусов. Я бы не назвал сервера ненужно прокладкой. Если нет сервера, то устройство выполняет роль сервера, что не всегда целесобразно.
Состояние IPv6 достаточно странное: он давно есть, ни кроме гиков его почти никто не использует
Verizon Wireless similarly reports that about 90% of its traffic uses IPv6
Однако же?
Думаю, это был только образец. Кому нужна более распределённая архитектура — тот догадается, как связать стороны с помощью только сервера локации.
Но даже при полном и окончательном IPv6 не будет лёгкости отправки чего-то кому угодно, потому что даже где нет NAT, используются файрволлы с состоянием. Просто так пускать входящие соединения нельзя, мало ли кто зачем приходит и какой незащищённый сервис сидит внутри. Нужно явное разрешение, а оно проще всего делается, если устройство с внутренней стороны явно такое попросило (в это входит установление TCP соединения или UDP ассоциации).
И пробивать такой файрволл одновременно с двух сторон не всегда получается — он должен поддерживать такое. Хотя, да, это проще, чем с NAT, когда и внешний адрес ремотной стороны может быть заранее неизвестен.
Вы застряли в лифте и решили об этом написать.
Если речь про сервер, то на одном ядре процессора реалистично обрабатывать до 50k pps, что будет примерно 25k pps на прием и 25k pps на отправку. Мне кажется, даже одного ядра без ухищрений вроде netmap хватит достаточно надолго :).
Пример использования, по идее, есть тут: godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch
Вы же понимаете, что в XMPP оверхед на одно сообщение гигантский, что объем трафика значительно увеличивает? Плюс, он архитектурно достаточно плохо переживает постоянные обрывы соединения и переключения на другой канал. А учитывая посредственную поддержку XMPP в клиентских библиотеках в настоящее время, придётся фактически заново изобретать протокол. Несмотря на то, что XEP, которые решают эту проблему, вообще говоря, есть.
Дело не в мессенджере, а в том, что tcp становится плохо при хреновой связи. Это проблема сетевого и транспортного уровней, а не уровня приложения.
Получается, нужно выкинуть из головы всю хрень про шифрование, сервера и прочую лабуду и разработать аналог tcp, который будет нормально работать при 95% потерянных пакетов…
Если для лабы то сойдет. Если для жизни и учесть все нюансы то вы сейчас изобретете недоTCP, который кстати и изобретался для надежной доставки в ненадежных сетях с высоким латенси и потерями, за счет пенальти по скорости и оверхедам по пакетам.
Готов поспорить, что разумное применение TCP будет в общем случае работать лучше, чем поделка на коленках на основе UDP.
В общем случае да, но статья рассматривает частный случай. В частных случаях больший контроль над трафиком будет иметь свои позитивные результаты.
Кроме того TCP имеет ряд недостатков, которые приводят к необходимости проверки целостности данных. То есть не смотря на то, что он как бы гарантирует доставку и целостность данных, но это не точно.
Особо интересно, когда TCP вреден. Если откинуть медиастриминг, туннелирование и реалтайм данные (к примеру, онлайн игры, биржи), то сходу можно найти проблему приоретизации данных в рамках одной сессии — TCP будет пытаться дослать вам данные, которые для приложения имеют меньший приоритет и вы никак на это не сможете повлиять. Конечно же можно для данных разного рода использовать отдельные соединения, но это не лучший выход из ситуации.
Есть хорошая книжка на эту тему — «Эффективное программирование TCP/IP» (Йон Снейдер). Она достаточно старая, но подробно рассматривает озвученную в статье проблему.
Книжка хорошо освещает вопрос в общих чертах и даже немного затрагивает достаточно частные случаи, но некоторые выводы и советы там вызывают вопросы. К примеру, в ней дается совет, что не надо изобретать TCP, но тут же сразу упоминается о том, что действительно есть отличные реализации на UDP.
Когда она вышла, были вопросы как они умудрились получить потери данных в рамках локалхоста, но в конечном итоге они написали пример «как написалось» — отсюда и потери.
Это мы приходим к вашему верному утверждению, что
любой протокол нужно уметь готовить.
Да, любой протокол нужно уметь готовить. Если в общих случаях TCP подойдет и лень заморачиваться с оберткой вокруг UDP, то используйте TCP и учитесь его готовить, но не ожидайте от него серебряную пулю и готовьтесь к внезапным неожиданным сбоям в продакшене. Для решения некоторых проблем можно попробовать SCTP, но это так же не гарантированный выход.
Кстати да, UDP так же не является гарантированно предпочтительным протоколом — не смотря на то, что он дает бОльшую свободу над контролем передаваемых данных, он так же обязывает больше за ними следить.
Для каждой задачи свои решения.
В противовес этому, в варианте с TCP нужно просто написать нормальный менеджмент сессий. Но его для UDP какой-то тоже придется делать, хоть и попроще.
Справедливости ради, у TCP есть другая неявная проблема — особенности реализации под каждую ОС, например, разные таймауты по-умолчанию — но это можно и нужно решать менеджментом сессий.
В общем, если задачу ограничить как «доставить короткое сообщение немедленно и без шифрования» — UDP подойдёт. Если вы всё-таки хотите полноценный мессенджер — рано или поздно дешевле будет от такого подхода отказаться.
Про это речи в статье не шло. Речь только про текст. Для картинок и для больших ответов или запросов уже нужен TCP.
> В общем, если задачу ограничить как «доставить короткое сообщение немедленно и без шифрования» — UDP подойдёт. Если вы всё-таки хотите полноценный мессенджер — рано или поздно дешевле будет от такого подхода отказаться.
Заметьте, что я не говорил, что всё общение должно идти по UDP. Это лишь дополнительный вариант, который нужен для ситуаций, когда у человека совсем плохая связь и он очень хочет что-то кому-то отправить в виде текста (у меня такое иногда бывает в лифте или в метро).
Заметьте, что я не говорил, что всё общение должно идти по UDP. Это лишь дополнительный вариант, который нужен для ситуаций, когда у человека совсем плохая связь и он очень хочет что-то кому-то отправить в виде текста (у меня такое иногда бывает в лифте или в метро).
Вы понимаете, что работа мессенджера в суровых условиях — это конкурентное преимущество? (хоть и не для большинства) В погоне за функциональностью и стикерами простые мессенджеры забывают просто работать.
Нет смысла щемиться сразу на несколько соединений — надо работать на самом быстром, а если не получается, то уже дальше пробовать что доступно. Включая туннелирование через ICMP или 53 порт. Да, медленно, но оно работает и даже файлы передаются.
Почему же? Если UDP заблокирован, то хотя бы за приемлемое время пользователь получит фидбек.
> Вы понимаете, что работа мессенджера в суровых условиях — это конкурентное преимущество? (хоть и не для большинства) В погоне за функциональностью и стикерами простые мессенджеры забывают просто работать.
Понимаю, поэтому и решил придумать свой протокол :). Но кастомным протоколом реально улучшить работу лишь для очень простых случаев, как отправка сообщения — всё-таки в TCP/IP и/или QUIC не зря люди столько усилий вложили. В отличие от своего протокола, TCP и QUIC умеют в congestion control, обработку переупорядочевания пакетов, защиту от DDoS и т.д.
Почему же? Если UDP заблокирован, то хотя бы за приемлемое время пользователь получит фидбек.
Проще использовать что-то типа логики доверия каналу — пробуем все варианты и потом используем лучший. Если он теряет доверие, то еще раз пробуем.
Понимаю, поэтому и решил придумать свой протокол :)
Yet another protocol… YAP!
Это больше походит на размышления о проблеме, но не протокол. Кстати, именно как размышления о проблеме статься читается хорошо.
защиту от DDoS
TCP и защита от DDoS?… Ну знаете… Эх…
Проблема с приоритезацией решается просто — двумя независимыми сессиями.
Я как раз и написал, что это не лучший выход из ситуации.
В условиях, где одна TCP сессия еле работает, вы хотите поднимать и менеджерить несколько независимых? Еще раз повторю: это не самое лучшее решение.
TCP, на самом деле, гарантирует только порядок и целостность и больше никакой магии не делает, и, в общем-то, это первое, что нужно будет придумать своё, если от него отказываться.
Я в курсе. Напомню, мы говорим про условия очень плохой связи.
На порядке вы получите затык по скорости, фризы и отвалы по таймауту, на доверии к целостности проблемы — TCP не так уж и хорошо гарантирует целостность данных.
И да, надо будет это реализовывать самому и ничего зазорного плохого и страшного в этом нет.
Следующий «ой» будет на больших сообщениях, тех же картинках — их нужно будет склеивать. Это всё только на бумаге выглядит просто и без проблем — в реальных условиях будет немало сюрпризов.
Серьезно? Такая гигантская проблема? Сюрпризы будут, но назвать их проблемами язык не поворачивается. Если не знаете решения, то подсмотрите в битторент хотя бы.
В противовес этому, в варианте с TCP нужно просто написать нормальный менеджмент сессий. Но его для UDP какой-то тоже придется делать, хоть и попроще.
Вы отталкиваетесь от того как _вам_ будет проще это написать, а не как выдать решение, которое будет работать в бОльшем количестве условий.
В общем, если задачу ограничить как «доставить короткое сообщение немедленно и без шифрования» — UDP подойдёт.
Шифрование вы прямо к TCP привязываете? Все остальное уже гарантированно без криптографии будет? Это что-то новенькое.
Если вы всё-таки хотите полноценный мессенджер — рано или поздно дешевле будет от такого подхода отказаться.
Дешевле будет отказаться от работы в условиях плохой и нестабильной связи. Но если вам надо работать в таких условиях, то сценарий будет иной.
Еще раз повторюсь: вы отталкиваетесь от того, как вам будет проще писать. Статья и мысли совсем о другом.
Да, проблема доставки сообщений и медиаданных в условиях нестабильной связи взята не с потолка, а из реальной жизни. И да, UDP показывает себя очень ощутимо лучше.
Хех, вы еще скажите, что UDP не нужен и от него нужно отказываться.
Вы сравниваете теоретический мессенджер на базе UDP с современными реальными на базе TCP и делаете достаточно жирное допущение о том, что проблемы современных мессенджеров связаны с TCP. Я говорю о том, что при разработке реального мессенджера с конкретным набором требований (шифрование, аутентификация, медиаконтент, подтверждения) — может оказаться, что не такой уж большой выигрыш дает UDP. Да, концепт без шифрования и прочих рюшек на UDP скорее всего выиграет, но не будет жизнеспособен в реальных условиях.
Я считаю, что проблемы современных мессенджеров не в том, что они используют TCP, а в том, сколько ещё уровней (часто избыточных) они используют поверх него.
Косвенно, это подтверждает мой опыт использования IRC, ICQ и Jabber через ранний GPRS — проблемнее всего тогда у меня был Jabber, и если взглянуть на его протокол — он самый избыточный. Позднее, когда компрессия стала распространенным явлением, вся тройка более-менее сравнялась и далее их юзабилити ограничивалось только глюками и особенностями конкретного протокола.
Кроме того, в общем, уже существуют протоколы, которые по крайней мере в теории поддерживают все три транспорта и используют более подходящее управление сессиями (MTProto). И да, оно лучше, но я бы не сказал, что на столько же лучше на сколько сложнее такая полная реализация.
Ну право же, SYN-ACKSYN-ACK это три очень маленьких пакета. Да, это занимает время, но это не так страшно, как установка TLS сессии и аутентификация поверх — вот с этим действительно надо бороться, а тут что по UDP что по TCP overhead будет примерно одинаковым
Вы напрасно заняли агрессивную позицию.
Так получилось. Не хотел :)
в том числе, как у вас в статье
Статья не моя :)
Вы сравниваете теоретический мессенджер на базе UDP с современными реальными на базе TCP и делаете достаточно жирное допущение о том, что проблемы современных мессенджеров связаны с TCP. Я говорю о том, что при разработке реального мессенджера с конкретным набором требований (шифрование, аутентификация, медиаконтент, подтверждения) — может оказаться, что не такой уж большой выигрыш дает UDP. Да, концепт без шифрования и прочих рюшек на UDP скорее всего выиграет, но не будет жизнеспособен в реальных условиях.
Нет. Я сравниваю реальное использование внутренней разработки. Ну условия у нас такие, что нужно выжимать максимум из канала — он совсем не ахти и от этого отталкиваемся. С шифрованием и всеми прочими плюшками включая медиаконтент (не ради котиков).
Я считаю, что проблемы современных мессенджеров не в том, что они используют TCP, а в том, сколько ещё уровней (часто избыточных) они используют поверх него.
И это тоже, но в вопросе TCP vs UDP надежнее последний.
через ранний GPRS
Раньше GPRS работал. Сейчас он есть для галки и от него остался кусок, который можно использовать через оное место.
Ну право же, SYN-ACKSYN-ACK это три очень маленьких пакета. Да, это занимает время, но это не так страшно, как установка TLS сессии и аутентификация поверх — вот с этим действительно надо бороться, а тут что по UDP что по TCP overhead будет примерно одинаковым
Ви таки не поверите, но далеко не в этих трех пакетах проблема.
Тут ниже netch80 описал одну из проблем в очередности пакетов и их гарантии доставки — это один из сценариев, когда у вас сообщения не будут доходить совсем.
Статья не моя :)
Извиняюсь, проглядел
Раньше GPRS работал
Ну, мне запомнились потери пакетов в диапазоне 30-50% на 64-байтных пингах. Сейчас, думаю, там так же или чуть хуже
Я сравниваю реальное использование внутренней разработки
Если у вас заранее известны особенности канала — понятное дело, заточенное под него решение будет работать лучше. То, о чем я пишу, я имею в виду в контексте глобальных сетей (плюс, желательно предусмотреть, чтобы толпа таких клиентов в одной локации не положила сеть лавиной ретрансмитов если вдруг аплинк на минуту пропадет)
Касательно очередности — да, согласен. Но тут имхо зависит от решаемой задачи. Лично я в контексте мессенжера предпочитаю подождать, пока соединение переподнимется и мне дойдут все сообщения, чем гадать, не потерялась ли часть смысла по пути. Если от этого отказаться — очевидно, UDP начинает подходить больше. И да, это именно то, что я имею в виду, когда говорю про аккуратное управление сессиями — то есть выставление таймаутов и переинициализация соединения, если оно залипло, если позволяет платформа — корректная установка опций сокета.
Разумеется, всё это я говорю в контексте коротких текстовых сообщений, если в ту же сессию полетят котики — разговор будет другой
Если у вас заранее известны особенности канала — понятное дело, заточенное под него решение будет работать лучше.
А далеко ходить не надо, у нас в стране связь далеко не везде достойная.
Да, в городах часто хорошая, но есть еще территории вне города, под землей итд. Даже в домах связь может барахлить как старый трактор.
Так что можно просто сразу взять 50кбпс 300-1500мс с потерей 30% и тестить. Если такие условия интересны. Обычно бизнес смотрит на такие условия как на удел неудачников, о которых не стоит думать.
Но тут имхо зависит от решаемой задачи.
Вроде бы цель озвучена изначально :) Нужен мессенджер в паршивых условиях.
Лично я в контексте мессенжера предпочитаю подождать, пока соединение переподнимется и мне дойдут все сообщения, чем гадать, не потерялась ли часть смысла по пути.
Это решается на прикладном уровне. В том же iMessage забавно наблюдать ситуации, когда отвечаешь на сообщение, а оно встает по времени выше, чем то, на которое отвечаешь.
Если от этого отказаться — очевидно, UDP начинает подходить больше.
Я только принимаю участие в дискуссии UDP vs TCP при плохой связи. Там дальше еще много приколов, но они намного проще решаются, чем обеспечение приемлемого уровня передачи данных в TCP. Бывает, что нужно передать фотографию места (к примеру, узел у черта на куличиках), но при этом не прерывать текст. Фото дойдет, текст будет раньше и это все в рамках одного потока.
Это очень веселая и интересная задача. Работа UDP тут — около 3к строк на Си. Дальше идет логика куда более занятная.
Кстати, для мессенджера один поток в сети — это, сугубо на мой взгляд, лучшее решение. Тут просто великолепно себя показывает sctp, но дальше экспериментов мы не смогли его запустить — не поддерживается. Великолепно — это выкинуто подавляющее большинство наших костылей, но не без нареканий (некоторые могут вогнать в ступор). По крайней мере это лучшее в соотношении затраченные усилия/профит.
И да, это именно то, что я имею в виду, когда говорю про аккуратное управление сессиями — то есть выставление таймаутов и переинициализация соединения, если оно залипло, если позволяет платформа — корректная установка опций сокета.
А оно вам надо? Зачем столько головняков для исправления проблем TCP когда можно сделать то же самое только без сюрпризов?
Разумеется, всё это я говорю в контексте коротких текстовых сообщений, если в ту же сессию полетят котики — разговор будет другой
Я говорю обо всех случаях с мыслью, что если решение работает достойно при плохой связи, то с большой вероятностью оно будет работать достойно и при отличном канале. Ну и с котиками, конечно же.
Мы сравнивали внутренний чат с ТГ и вацапом (как наиболее популярные).
ТГ — самый удобный, но отваливается при первом случае.
Вацап — пытается держаться до последнего, но все же не приемлемо. Что больше всего не понравилось — у него случаются моменты, что он делает вид, что держится до последнего, хотя пакеты не передает хотя при этом отображает, что сообщение ушло на сервер.
У нас только один плюс среди них — работа в полевых условиях. Одна из плюшек — передача геометки на сервер одним мелким пакетом ежеминутно. Мало ли что по пути может произойти.
Один из интересных моментов — передача файлов. Все происходит по логике bittotrent, но с учетом доверию к генералам.
Конечно, если изобрести что-то очень очень узкозаточенное, причем и по функционалу и по условиям использования — есть шанс сделать лучше. Но это будет очень узко специализированное.
А например в настройках сетевого стека tcp у линукс 100500 всяких параметров. Там наверняка есть то что нужно, надо только найти нужное )))
Конечно, если изобрести что-то очень очень узкозаточенное, причем и по функционалу и по условиям использования — есть шанс сделать лучше. Но это будет очень узко специализированное.
Про общие случаи не пишут в статьях. Они обычно рассмотрены в различной учебной литературе.
Грубо говоря, если допускается результат работы при использовании общих подходов, то лучше их и использовать. В иных случаях нужно искать другие решения.
А например в настройках сетевого стека tcp у линукс 100500 всяких параметров. Там наверняка есть то что нужно, надо только найти нужное )))
Не уверен, что будет найден нужный ключик. К примеру, приоретизация данных в рамках одной сессии. Откуда операционной системе или протоколу знать что важнее в данных конкретного приложения?
И еще как-то странно будет, если мессенджер при установке будет менять параметры всего стэка.
Нарушение необходимости присылать ранее отправленные данные — тоже раньше — основа TCP и не может быть устранена без его полной замены.
Эти вопросы более-менее отработаны в SCTP. Там есть
1) раздельные каналы (не менее 10 по стандарту), в каждом из которых своё упорядочение сообщений;
2) опции отправки сообщений с ограничением по количеству перепосылок или по времени доставки.
Но с SCTP массовая проблема в поддержке раутерами с файрволлами и NAT, многие (особенно любимые вендорами корпоративные реализации) его не умеют и не собираются уметь.
1) раздельные каналы (не менее 10 по стандарту), в каждом из которых своё упорядочение сообщений;
Кто мешает сделать 10 логических каналов в TCP? Передавайте контролинг в одном канале, онлайн сообщения в другом, картинки качайте в третьем. Причем не тупо передачей файла а побив на фрагменты.
2) опции отправки сообщений с ограничением по количеству перепосылок или по времени доставки.
Аналогично это может быть на логике приложения.
Нарушение необходимости присылать ранее отправленные данные
Тут я не понял про что вы? tools.ietf.org/html/rfc2018 — SACK — по моему уже 100 лет везде по умолчанию включена.
Нет, конечно TCP не идеален, иначе не было бы разных специфических стеков. Но IMHO мессенджеры это не та сфера где уже исчерпаны все возможности TCP, чтобы жертвовать преимуществами его широкого распространения.
1. Это 10 разных соединений, TCP иначе не даст. Причём ещё надо в протоколе предусмотреть увязку этих соединений в одно.
А если их балансер разбросал на десяток бэкэндов, ещё свести их в один… спасибо, я не люблю систематическое построение троллейбусов из буханок.
2. Против опции с ограниченной передачей сообщений это уже не поможет.
> Аналогично это может быть на логике приложения.
Нет. На TCP это не сработает в принципе, если не делать каждый раз новое сообщение.
> Тут я не понял про что вы? tools.ietf.org/html/rfc2018 — SACK — по моему уже 100 лет везде по умолчанию включена.
Натурально, не поняли.
Вот я отправляю сообщение номер 1, устанавливаю ему, например, до 7 повторов.
Вот я отправляю сообщение номер 2, устанавливаю ему те же 7 повторов.
…
Сообщение номер 100…
пусть на какое-то время связь прервалась совсем. За это время сообщения 1-80 уже проэкспайрились. Дойдут 81...100.
На TCP вы так не сделаете, ибо после восстановления связи оно все с 1 по 80 будет пересылать наново, хотя они там нафиг уже не нужны (состояние обновилось и новое переслано позже). Забит канал, вместо посылки актуальных данных качаем сводки погоды допотопья.
На UDP — сделаете, но перепосылки организуете сами.
На SCTP — оно «из коробки» так умеет, только флажок поставь.
> Но IMHO мессенджеры это не та сфера где уже исчерпаны все возможности TCP, чтобы жертвовать преимуществами его широкого распространения.
Не знаю, что вы тут видите в специфике именно мессенджеров, но почитайте дискуссию и особенно комментарии Cyberaxʼа про текущее состояние вопроса с поддержкой расширений TCP, и почему считают, что QUIC тут на пользу.
Я не считаю, что во всём он прав, но задуматься — стоит.
TCP/IP, в отличие от TCP, это название всего стека, в который входит и UDP. Следовало написать просто «по TCP».
> UDP плохо дружит с NAT — обычно есть мало (~30 сек) времени, чтобы ответить клиенту на его запрос
Этот вопрос уже много где решён. В качестве примера можно взять SIP instant messaging. В частности, там есть конкретная реализация exponential backoff (который таки желательнее вашего «повторим через секунду») и завершение транзакции за 32 секунды.
Это не поможет, естественно, установить постоянный канал для ответов — всё равно потребуется поллинг от клиента — но это решается уже в рамках протокола в целом.
> Сервер должен быть устойчив к атакам усиления — нужно гарантировать, что пакет с ответом будет не больше пакета с запросом
Если вы говорите о мессенджере, это существенно только для случая ответа не с сообщением. На заведомо некорректный пакет (malformed) надо просто не отвечать. На корректный, но с чем-то не так (типа, пароль не сошёлся) — отвечать умеренно (с ограничением суммарной полосы на всех). Ограничение полосы можно вводить и на остальных, например, с посылкой короткого «попробуй через минуту».
> Шифрование — это сложно, и если вы не эксперт по безопасности, у вас мало шансов реализовать его корректно
Верно. Поэтому надо посмотреть на что-то типа QUIC, где это уже решено.
Причём они за счёт кэширования успешных авторизаций делают сокращение хэндшейка для следующего взаимодействия тех же сторон.
Обходиться без хэндшейка при первичной авторизации не советую — в условиях современного Интернета… съедят-с.
> Если выставить интервал перепосылок неправильно (например, вместо того, чтобы пробовать заново раз в секунду, пробовать заново без остановки), то можно сделать намного хуже, чем TCP/IP
Безусловно (с поправкой на термины). И тут даже раз в секунду — плохо, надо, повторюсь, exponential backoff.
Его вариант из SIP с раздельными таймерами для каждой транзакции и с 0.5 сек для старта — для нынешнего Интернета близок к оптимальному.
> На ваш сервер может начать приходить больше трафика из-за отсутствия обратной связи в UDP и бесконечных повторных попыток отправки
См. предыдущий пункт. Да, с UDP начинает зависеть от клиента (хотя и для TCP можно ядро подхачить).
Код не читал. По сумме сочетания задумок и описанного результата:
— Следует посмотреть на мировой опыт, начиная с того же SIP. Пока что вся статья на уровне «открытия Америки».
— Самая серьёзная заточка требуется в области криптографии. Повторюсь, неавторизованного клиента надо вначале тщательно проверить (пусть даже это длинный обмен), затем можно пользоваться готовой авторизацией (сессионные ключи и т.п.), но тут без специалиста не обойтись.
> Его вариант из SIP с раздельными таймерами для каждой транзакции и с 0.5 сек для старта — для нынешнего Интернета близок к оптимальному.
Да, поиграться можно :). Я думал «в промышленном варианте» сделать вообще немного по-другому:
1) Стартуем и отправку по UDP и по HTTP одновременно — кто раньше сможет, тот и молодец :). Сервер удалит дубликаты, если нужно.
2) По UDP пробуем без exponential backoff, но с интервалом повторения в 1 секунду в течение первых 15 секунд, а потом выдаем ошибку типа «таймаут» и пусть пользователь пробует ещё раз, по нажатию кнопки, если хочет. Наверное и 0,5 секунды можно первый retry (если пакет потерялся случайно), а дальше раз в секунду, поскольку, кажется, условия связи так быстро не меняются.
3) Все «тяжелые» операции делать по обычному TCP
> См. предыдущий пункт. Да, с UDP начинает зависеть от клиента (хотя и для TCP можно ядро подхачить).
Ну ядро на сервере можно подхачить, но отправлять-то будет клиент, где у нас нет контроля за его настройками (и хорошо, что нет :)).
> — Следует посмотреть на мировой опыт, начиная с того же SIP. Пока что вся статья на уровне «открытия Америки».
Я, безусловно, надеялся, что хотя бы где-то похожий алгоритм применяется. Но не думал смотреть в сторону SIP, спасибо за наводку :).
> — Самая серьёзная заточка требуется в области криптографии. Повторюсь, неавторизованного клиента надо вначале тщательно проверить (пусть даже это длинный обмен), затем можно пользоваться готовой авторизацией (сессионные ключи и т.п.), но тут без специалиста не обойтись.
Да, очевидно, что клиент уже должен быть авторизован и в клиенте уже закеширован (на диске или в память) сессионный ключ, но он временно находится в условиях плохой сети.
> Обходиться без хэндшейка при первичной авторизации не советую — в условиях современного Интернета… съедят-с.
Что подразумевается под первичной авторизацией? Смена IP или что-то другое?
> Верно. Поэтому надо посмотреть на что-то типа QUIC, где это уже решено.
Да, я даже решил попробовать проверить с QUIC, но не нашел ни одного работающего (чтобы в Chrome показывалось использование QUIC в веб-инспекторе) сервера на Go :). На C/C++ наверное есть, но это уже слишком харкорно для моего небольшого эксперимента.
> Если вы говорите о мессенджере, это существенно только для случая ответа не с сообщением. На заведомо некорректный пакет (malformed) надо просто не отвечать. На корректный, но с чем-то не так (типа, пароль не сошёлся) — отвечать умеренно (с ограничением суммарной полосы на всех). Ограничение полосы можно вводить и на остальных, например, с посылкой короткого «попробуй через минуту».
В целом да, можно наверное просто ограничить скорость посылки пакетов в сторону одного IP. Ну и если пакет не подписан правильно или он устарел, то просто дропать его. В любом случае, надо об этом думать и желательно показать какому-нибудь security эксперту :).
> Этот вопрос уже много где решён. В качестве примера можно взять SIP instant messaging. > В частности, там есть конкретная реализация exponential backoff (который таки желательнее вашего «повторим через секунду») и завершение транзакции за 32 секунды.
> Это не поможет, естественно, установить постоянный канал для ответов — всё равно потребуется поллинг от клиента — но это решается уже в рамках протокола в целом.
У меня было соображение, что этот протокол можно использовать как запасной вариант для быстрого обмена именно текстом, причём основным всё равно должен быть TCP/IP, особенно для получения текста сообщений обратно.
С получением ответа вообще всё сложнее. У меня была идея такая — можно, когда приложение на экране, активно «пинговать» сервер раз в N секунд (где N — что-то около 20-30 секунд), чтобы NAT не «забывал» соответствие IP и портов. И сервер в таком случае просто шлет новые сообщения этому клиенту, когда они поступают. Сервер тоже должен попробовать послать ответ несколько раз перед тем, как сдаться, примерно по такому же алгоритму (пробовать раз в секунду, но не более 15 раз).
Делаем мессенджер*, который работает даже в лифте