Делаем мессенджер*, который работает даже в лифте

    *на самом деле мы напишем только прототип протокола.

    Возможно, вы встречались с подобной ситуацией – сидите в любимом мессенджере, переписываетесь с друзьями, заходите в лифт/тоннель/вагон, и интернет вроде ещё ловит, но отправить ничего не получается? Или иногда ваш провайдер связи неправильно конфигурирует сеть и 50% пакетов пропадает, и тоже ничего не работает. Возможно, вы думали в этот момент — ну ведь можно же наверное как-то сделать, чтобы при плохой связи всё равно можно было отправить тот маленький кусочек текста, который вы хотите? Вы не одни.

    Источник картинки

    В этой статье я расскажу про свою идею для реализации протокола на основе UDP, который может помочь в этой ситуации.

    Проблемы TCP/IP


    Когда у нас плохое (мобильное) соединение, то начинает теряться большой процент пакетов (или ходить с очень большой задержкой), и протокол TCP/IP может воспринимать это как сигнал о том, что сеть перегружена, и всё начинает работать оооочень медленно, если работает вообще. Не добавляет радости тот факт, что установление соединения (особенно TLS) требует отправки и приема нескольких пакетов, и даже небольшие потери сказываются на его работе очень плохо. Также часто требуется обращение к DNS перед тем, как установить соединение — ещё пара лишних пакетов.

    Итого, проблемы типичного REST API, основанного на TCP/IP при плохом соединении:

    • Плохая реакция на потери пакетов (резкое уменьшение скорости, большие таймауты)
    • Установление соединения требует обмена пакетами (+3 пакета)
    • Часто нужен «лишний» DNS-запрос, чтобы узнать IP сервера (+2 пакета)
    • Часто нужен TLS (+2 пакета минимум)

    Суммарно это означает, что только для соединения с сервером нам нужно послать 3-7 пакетов, и при высоком проценте потерь соединение может занять существенное количество времени, а мы ещё даже ничего не отправили.

    Идея реализации


    Идея состоит в следующем: нам требуется всего-лишь отправить один UDP-пакет на заранее зашитый IP-адрес сервера с необходимыми данными авторизации и с текстом сообщения, и получить на него ответ. Все данные можно дополнительно зашифровать (этого в прототипе нет). Если ответ в течение секунды не пришел, то считаем, что запрос потерялся и пробуем отправить его заново. Сервер должен уметь убирать дубли сообщений, поэтому повторная отправка не должна создать проблем.

    Возможные подводные камни для production-ready реализации


    Ниже перечислены (далеко не все) вещи, которые нужно продумать перед тем, как использовать что-либо подобное в «боевых» условиях:

    1. UDP может «резаться» провайдером — нужно уметь работать и по TCP/IP
    2. UDP плохо дружит с NAT — обычно есть мало (~30 сек) времени, чтобы ответить клиенту на его запрос
    3. Сервер должен быть устойчив к атакам усиления — нужно гарантировать, что пакет с ответом будет не больше пакета с запросом
    4. Шифрование — это сложно, и если вы не эксперт по безопасности, у вас мало шансов реализовать его корректно
    5. Если выставить интервал перепосылок неправильно (например, вместо того, чтобы пробовать заново раз в секунду, пробовать заново без остановки), то можно сделать намного хуже, чем TCP/IP
    6. На ваш сервер может начать приходить больше трафика из-за отсутствия обратной связи в UDP и бесконечных повторных попыток отправки
    7. IP-адресов у сервера может быть несколько, и они могут меняться со временем, поэтому кеш нужно уметь обновлять (у Telegram хорошо получается :))

    Реализация


    Напишем сервер, который будет отдавать ответ по UDP и присылать в ответе номер запроса, который к нему пришел (запрос выглядит как «request-ts текст сообщения»), а также timestamp получения ответа:

    // Это Go.
    // Обработка ошибок убрана для краткости
    buf := make([]byte, maxUDPPacketSize)
    
    // Начинаем слушать UDP
    addr, _ := net.ResolveUDPAddr("udp", fmt.Sprintf("0.0.0.0:%d", serverPort))
    conn, _ := net.ListenUDP("udp", addr)
    
    for {
    	// Читаем из UDP, нам обязательно нужен обратный адрес
    	n, uaddr, _ := conn.ReadFromUDP(buf)
    	req := string(buf[0:n])
    	parts := strings.SplitN(req, " ", 2)
    
    	// Высчитываем время на сервере по сравнению с временем клиента
    	curTs := time.Now().UnixNano()
    	clientTs, _ := strconv.Atoi(parts[0])
    
    	// Тут можно сходить в базу или куда-нибудь ещё и непосредственно сохранить сообщение
    
    	// Отправляем ответ
    	conn.WriteToUDP([]byte(fmt.Sprintf("%d %d", curTs, clientTs)), uaddr)
    }
    

    Теперь сложная часть — клиент. Мы будем отправлять сообщения по одному и дожидаться ответа сервера перед тем, как послать следующее. Слать будем текущий timestamp и кусок текста — timestamp будет служить идентификатором запроса.

    // Создаем сокеты
    addr, _ := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", serverIP, serverPort))
    conn, _ := net.DialUDP("udp", nil, addr)
    
    // В UDP запись и чтение будут идти независимо, поэтому используем канал для удобства.
    resCh := make(chan udpResult, 10)
    go readResponse(conn, resCh)
    
    for i := 0; i < numMessages; i++ {
      requestID := time.Now().UnixNano()
      send(conn, requestID, resCh)
    }
    

    Код функций:

    func send(conn *net.UDPConn, requestID int64, resCh chan udpResult) {
      for {
        // Отправляем пакет до тех пор, пока не получим ответ на своё сообщение.
        conn.Write([]byte(fmt.Sprintf("%d %s", requestID, testMessageText)))
        if waitReply(requestID, time.After(time.Second), resCh) {
          return
        }
      }
    }
    
    // Ждем свой ответ, или таймаут.
    // В сети пакеты могут как теряться, так и дублироваться, поэтому нужно
    // проверять, что присланный ответ действительно относится к тому сообщению,
    // которое мы посылали.
    func waitReply(requestID int64, timeout <-chan time.Time, resCh chan udpResult) (ok bool) {
      for {
        select {
          case res := <-resCh:
            if res.requestTs == requestID {
              return true
            }
          case <-timeout:
            return false
        }
      }
    }
    
    // Распарсенный ответ сервера
    type udpResult struct {
      serverTs  int64
      requestTs int64
    }
    
    // Функция для чтения ответа из соединения и засовывания ответа в канал.
    func readResp(conn *net.UDPConn, resCh chan udpResult) {
      buf := make([]byte, maxUDPPacketSize)
    
      for {
        n, _, _ := conn.ReadFromUDP(buf)
        respStr := string(buf[0:n])
        parts := strings.SplitN(respStr, " ", 2)
    
        var res udpResult
        res.serverTs, _ = strconv.ParseInt(parts[0], 10, 64)
        res.requestTs, _ = strconv.ParseInt(parts[1], 10, 64)
    
        resCh <- res
      }
    }
    

    Также я реализовал то же самое на основе (более-менее) стандартного REST: с помощью HTTP POST посылаем те же requestTs и текст сообщения и дожидаемся ответа, после чего переходим к следующему. Обращение делалось по доменному имени, кеширование DNS в системе не запрещалось. HTTPS не использовался, чтобы сравнение было более честным (в прототипе шифрования нет). Таймаут был выставлен в 15 секунд: в TCP/IP уже есть перепосылки потерянных пакетов, а сильно больше 15 секунд пользователь, скорее всего, ждать не станет.

    Тестирование, результаты


    При тестировании прототипа измерялись следующие вещи (всё в миллисекундах):

    1. Время ответа на первый запрос (first)
    2. Среднее время ответа (avg)
    3. Максимальное время ответа (max)
    4. H/U — соотношение «время HTTP» / «время UDP» — во сколько раз меньше задержка при использовании UDP

    Делалось 100 серий по 10 запросов — симулируем ситуацию, когда нужно послать буквально несколько сообщений и после этого уже становится доступен нормальный интернет (например Wi-Fi в метро, или 3G/LTE на улице).

    Протестированные виды связи:


    1. Профиль «Very Bad Network» (10% потерь, 500 мс latency, 1 мбит/сек) в Network Link Conditioner — «Very Bad»
    2. EDGE, телефон в холодильнике («лифте») — fridge
    3. EDGE
    4. 3G
    5. LTE
    6. Wi-Fi

    Результаты (время в миллисекундах):




    (то же самое в формате CSV)

    Выводы


    Вот, какие выводы можно сделать из получившихся результатов:

    1. Если не считать аномалию с LTE, то разница при посылке первого сообщения тем больше, чем хуже связь (в среднем в 2-3 раза быстрее)
    2. Последующая отправка сообщений в HTTP не сильно медленней — в среднем в 1,3 раза медленней, а на стабильном Wi-Fi вообще разницы нет
    3. Время ответа на основе UDP намного стабильнее, что косвенно видно по максимальному времени ожидания — оно тоже меньше в 1,4-1,8 раз

    Другими словами, в соответствующих («плохих») условиях наш протокол будет работать намного лучше, особенно при посылке первого сообщения (часто это всё, что необходимо отправить).

    Реализация прототипа


    Прототип выложен на github. Не используйте его в продакшене!

    Команда для запуска клиента на телефоне или компьютере:
    instant-im -client -num 10
    . Сервер пока что запущен :). Нужно смотреть в первую очередь на время первого ответа, а также на максимальную задержку. Все эти данные печатаются в конце.

    Пример запуска в лифте


    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 103

      –24
      Оу, спасибо. Я ненавижу Go, просто как раз хотел что нибудь про UDP почитать.
        +10

        Часть про то, что вы ненавидите Go, была обязательной, чтобы свои впечатления выразить? То есть, никак не обойтись без того, чтобы хейтить и просто "что-нибудь про UDP" почитать? Раз от статьи не воротит настолько, что решили почитать, значит не так уж и ненавидите, наверное.


        К тому же вы у себя в профиле ставите этот язык как на котором разрабатываете (на втором месте, между прочим), и подписаны на хаб (наверное, чтобы поддерживать себя в тонусе).

          –35
          мсье, вы бомбанули ярче сверх-новой, и что удивительно даже не по поводу своей статьи *удивление*

          Сам же автор поступил немного более мудро, и не посмел обронить даже единого слова (!) подмечу: в отличии от меня с Вами. По итогу решаю обобщить нашу с Вами причастность к ейфорическому вмешательству тролля свыше, ибо его -4 вполне вероятно похоронят и нас, ведь Ваши +2 им на противовес поставлены, И МЕСТНЫЙ(АЯ) МОДЕРАТОР БОГ(ИНЯ) ФЕМИДА НЕ СМОЖЕТ ОСТАНОВИТЬ ЭТО, ведь мы абсолютно резонно ответили друг другу.

          p.s. и что иронично изволить у меня и вовсе слишком мало кармы для голосования. знаете что бы я сделал? поставил обоим жирный минус за трату своего времени на объяснение этой несуразицы Вам
          au revoir, синьоры
          image
          +26
          Я знаю отличную шутку про UDP, но не факт, что она до вас дойдет.
            +1
            Повторю, что она до вас не дойдет.
              +2

              Повторю еще раз с увеличенной задержкой что-ли.

                +4
                Как же я ненавижу UDP! Никогда не знаешь, дошел ли пакет, или нет, приходится повторять отправку.
                  +6
                  Как же я ненавижу UDP! Никогда не знаешь, дошел ли пакет, или нет, приходится повторять отправку.
                  0
                  Я повторю вам шутку про TCP, если она до вас не дойдет, но не про UDP.
                0

                У вас с этим какие-то проблемы. Серьезно.

                0
                В реальном мире у нас есть авторизации, ключи, сессии.
                Вы сделали stateful мессенджер для телефона. А теперь придумайте пяток сценариев где это не будет работать вообще.
                И на закуску парочку сценариев где это приведет к серьезным проблемам. Не отправленное сообщение, которое отображается отправленным, это серьезная проблема.
                Совсем потом можно подумать на тему хранения сессий в WhatsApp. Миллиард активных пользователей. Как вы бы обработали на серверах stateful?

                И сразу все станет понятно.
                  +1

                  Как прототип/идея весьма неплохо. N сессий тут решается простым KV хранилищем с ключом по ID клиента.
                  А для синхронизации последнего отправленного сообщения (проблема "отправленного" и не пришедшего сообщения) можно использовать алгоритм из биржевого FIX. Он прост как две копейки и, по моему, отлично ложится на предложенную архитектуру.
                  Не претендую на истинность, но я бы в свободное время поразмыслил над этим проектом

                    +3
                    Это даже на идею не тянет. Не говоря уже о прототипе. Отправка по UDP пакета и ожидание ответа сервера перед отправкой следующего максимум на лабораторку тянет.

                    Решается, только масштабируется не очень. Нам нужен как минимум id клиента, id сессии, ключ шифрования и хеш с таймстемпом последнего сообщения. Пара килобайт точно. Если подумать точно еще что-то вылезет что надо хранить. На миллиарде клиентов это терабайты данных.

                    Шардирование делать сложно. Пакеты шифрованные. В начале надо терминировать tls. Проблема явно решаемая, но не с налету.

                    Вот чувствую зря я про совсем потом на писал. Там и без этого пункта проблем с stateful выше крыши.
                      0
                      Я не обещал в статье, что остальное реализовать легко. Напротив, я большую часть проблем озвучил :).

                      По поводу того, что вы говорите:

                      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 выше крыши.

                      Основная проблема — «чувствую» :). Все эти проблемы вполне решаемы, но для прототипа не важны.
                        +1
                        Вы вот так вот сразу отказали от End2End шифрования. В 2019 году. Вы это серьезно?
                        Шифруем все в 2 слоя. Первый пакет до сервера, второй само сообщение.

                        Сессионный ключ клиента это не паблик ключ сервера. Это именно сессионный ключ. Чтобы в случае компрометации серверного ключа трафик нельзя было расшифровать. Да и использовать один ключ неопределенно долгое время это не очень. Азы же.

                        1. Вы считаете размер пакета, а надо считать размер инфы которою нужно хранить на серверах для поддержания сессии.

                        2. Всю историю можно хранить на дисках и не париться о скорости. Подгрузка истории это процесс не требующий особой скорости. Клиент готов подождать пока у него загрузится история на новой устройстве. А вот информация о сессиях нужна в памяти. К ней нужен очень быстрый доступ.

                        3. Проблема не в сообщениях, а в клиентах. С сообщениями все понятно. таймстемп последнего и хеш от него же.
                        При https да. Именно так и делаем и все просто. Вы же хотите кастомный udp протокол. На сервер будет приходить шифрованный массив. Надо найти от него ключ, расшифровать, найти клиента, валидировать что все ок и уже потом передать на обработку.

                        4. Сессионные ключи строго обязательны в 2019 году. Я выше написал.
                          0
                          > Вы вот так вот сразу отказали от End2End шифрования. В 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 году. Я выше написал.

                          Ок, можно шифровать сессионным ключом, противоречий с моей идеей пока что не вижу :).
                            +1
                            Как именно шифровать это не важно в данном случае. Но необходимо понимать общий принцип шифрования. Пакет отдельно, пользовательские данные в нем отдельно. Ключ от пакета сессионный, ключ от пользовательских данных только у получателя.

                            Миллион пользователей это мало. Мессенджеры нынче живут на миллиарде пользователей. И любая разработка должна быть готова к работе с таким объемом. Миллиард это 10Тб шаренной и доступной со всех узлов входа ОЗУ только на сессии. Это уже много. Очень много. Да и просто держать столько открытых tls сессий это задача ну очень нетривиальная.

                            Проблема что это плохо масштабируется горизонтально.
                            Проблема что это плохо будет работать за фаерваллами.
                            Это будет плохо работать за двойными натами.
                            Это плохо совместимо с проксями.
                            Это проблема консистентности статуса клиента и сервера.
                            Это в конце концов кастомный низкоуровневый протокол. То есть все типовые балансировщики с ним будут работать плохо или не будут вообще.
                            Реверс прокси работать не будет точно.

                            И ради чего все это? Чтобы пользователь имел повышенный шанс отправить сообщение при ухудшении сигнала? Оно того не стоит.
                              0
                              > Миллиард это 10Тб шаренной и доступной со всех узлов входа ОЗУ только на сессии. Это уже много. Очень много.

                              Учитывая, что 512 Гб на сервер — это вполне стандартная (чуть выше среднего) современная конфигурация, вам нужно всего 20 серверов, чтобы хранить это. Если учесть резервирование, то 40 серверов. Для хранения самых пользовательских данных вам потребуется в сотни раз больше, так что это не проблема.

                              > Реверс прокси работать не будет точно.
                              В целом, в этом нет особой необходимости, весь трафик может принять буквально несколько серверов. К тому же, балансировку на уровне сети никто не отменял.

                              > Это будет плохо работать за двойными натами.
                              Поясните, пожалуйста, почему именно за двойным натом это должно плохо работать.

                              > Проблема что это плохо масштабируется горизонтально.
                              Уж что-что, а UDP прекрасно масштабируется на любое число машин, поскольку нет состояния между запросами.

                              > Проблема что это плохо будет работать за фаерваллами.
                              Возможно.

                              > Это плохо совместимо с проксями.
                              Если речь про корпоративный прозрачный HTTP-прокси, то да. Речь шла про мобильный интернет в первую очередь, там нет никаких проксей обычно.

                              > Это в конце концов кастомный низкоуровневый протокол. То есть все типовые балансировщики с ним будут работать плохо или не будут вообще.
                              Вы говорите про L7 балансировку, но есть и другие виды балансировки, например L3/L4.
                              И я бы не сказал, что этот протокол низкоуровневый :). С каких пор UDP стал протоколом низкого уровня?

                              > И ради чего все это? Чтобы пользователь имел повышенный шанс отправить сообщение при ухудшении сигнала? Оно того не стоит.
                              А по-моему, стоит, если вы разрабатываете мессенджер и это ваш основной бизнес. Люди целые протоколы изобретают или начинают использовать просто ради улучшения качества связи, как например Apple с Multipath TCP для звонков FaceTime или iMessage — благодаря этой технологии они могут работать даже при более плохих условиях связи, и не прерывать соединение при смене сети — это очень большое конкурентное преимущество по сравнению с остальными.
                                0
                                Учитывая, что 512 Гб на сервер — это вполне стандартная (чуть выше среднего) современная конфигурация, вам нужно всего 20 серверов, чтобы хранить это. Если учесть резервирование, то 40 серверов. Для хранения самых пользовательских данных вам потребуется в сотни раз больше, так что это не проблема.

                                А если учесть, что плотность пользователей сильно зависит от локации и онлайн в разных локациях разнится в разное время суток, то в реальности даже и это — оценка сверху. Потому что в некоторых регионах и более низкие требования будут.

                    +1
                    Совсем потом можно подумать на тему хранения сессий в WhatsApp. Миллиард активных пользователей. Как вы бы обработали на серверах stateful?

                    Боги послали вам редис с шардированием, например.

                    0
                    У Вас клиентская часть тоже на голэнге? А как доставлять такое приложение на мобильник, я просто не в курсе технологии.
                      +1
                      Это лишь пример, поэтому да, тоже на го :). На самом деле go вполне встраивается в мобилки, есть даже утилита для построения интерфейса целиком на go под названием gomobile. Если интересно, как я тестировал этот «клиент» на телефоне, то всё довольно просто: для Android есть приложение Termux, (небольшой дистрибутив Linux) котором можно в качестве одного из пакетов поставить golang, после чего я склонировал репозиторий, собрал его прямо на телефоне, написал скрипт для серийного запуска и положил в холодильник гонять тесты, для симуляции ситуации «в лифте».
                        0
                        Спасибо, познавательно. Хотя я повременю это изучать, надеюсь все-таки на снятие проклятия с PWA :)
                        +1

                        Отлично работает гошка прямо на мобильных. См, например, Status.im

                        0
                        Когда я последний раз изобретал свой протокол для Интернет, то столкнулся с тем что примерно 10% моих пользователей работают в корпоративных сетях за всякими фаерволами, и с ними можно взаимодействовать только по http(s). Что привело меня тогда к необходимости передавать реалтаймовые данные по tcp.
                        Думаю в процессе разработки вашего «отказоустойчивого» протокола этот опыт тоже будет полезен. :)
                          0

                          Ну если сравнивать по latency то вотсапп например неплохо живет и на спутниковом инете — пинг 600-2500 скорость 8-40 мбит в зависимости от погоды...

                            0

                            Опять в очередном меседжере используется прокладка в виде сервера. Почему бы не отправлять сообщения напрямую получателю ведь интернет на это расчитан. Теперь уже и IPv6 есть которому NAT не нужен.


                            Кстати в GO есть пакет шифрования так что изобретать ничего не нужно.

                              0

                              Потому что ваш телефон извне недоступен, наверное.

                                0

                                С чего это вдруг? Тем более по UDP.

                                  0
                                  NAT может этому помешать. На практике до 50% мобильных абонентов не «видят» друг друга напрямую, даже если использовать техники вроде STUN. Подробнее можно почитать об этом, например, тут: en.wikipedia.org/wiki/STUN#Limitations:

                                  > 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.
                                    0

                                    Может NAT, может файрволл у опсоса или в самом телефоне, не знаю.


                                    Несколько раз пытался попасть на свой телефон извне — через вайфай получалось, через мобильную связь — нет.

                                      +1

                                      IPv6 операторы потихоньку внедряют. Для IPv4 нужно пробивать UDP порт при помощи UDP hole punch.

                                        0
                                        Спасибо за ссылку :)! У меня, как оказалось, уже давно подключена услуга IPv6, но «просто так» IPv6 при этом не работал, нужно ещё настройки APN было подкрутить. Тем не менее, test IPv6 теперь у меня выдает 9/10, что уже неплохо.
                                          0

                                          Телефон доступен извне?

                                            +1
                                            Удивительно, но да, выдается белый (но, вероятно, динамический) IP.
                                          +1
                                          Это ж кошмар для прослушивающих дядей будет, если все начнут друг-другу что-то слать, как это мониторить, как этим управлять?
                                            0
                                            > UDP hole punching will not work with symmetric NAT devices (also known as bi-directional NAT) which tend to be found in large corporate networks.

                                            К сетям опсосов это тоже относится в значительной мере.
                                      0
                                      Всё равно нужно как-то координировать действия, и, зачастую, как уже сказали выше, установление прямого соединения пока что невозможно.

                                      Ну и, конечно же, P2P соединение будет устанавливаться ещё дольше. Но вы можете использовать сервер для кратковременного хранения сообщений, которые зашифрованы ключом вашего собеседника, тогда это будет более безопасно, хотя я не security эксперт, к сожалению :).
                                        0

                                        Помоему отправить UDP пакет напрямую гораздо быстрее чем через сервер. Ну и использование IPv6 избавит от необходимости преодолевать NAT.


                                        Сервер нужен для первичного входа в DHT сеть (сразу после установки приложения либо после очень долгого отсутствия в сети). Если сеть будет достаточно крупной то вполне сможет обеспечить временное хранение шифрованных не доставленных сообщений пока оба абонента офлайн.


                                        Авторизованные абоненты могут сигнализировать друг другу напрямую о изменении адреса и порта. Если вдруг случится что они сменят адрес одновременно то могут найти друг друга в DHT.

                                          0
                                          В целом, всё звучит разумно, но учитывайте, что целью моего эксперимента было достижение минимально возможной задержки, и «поиск друг друга в DHT» явно будет препятствовать быстрой первоначальной отправке, а в этом был основной смысл.

                                          > Помоему отправить UDP пакет напрямую гораздо быстрее чем через сервер. Ну и использование IPv6 избавит от необходимости преодолевать NAT.

                                          Да, если у всех есть белый IP-адрес, то отправка напрямую обычно будет быстрее. Но пока что реальность такова, что белого IP почти ни у кого нет, тем более на мобильном интернете. Ну и IPv6 пока что тоже встречается очень редко, к сожалению.
                                            0

                                            6to4 поможет получить IPv6 даже за натом. Если его конечно специально не будут резать.

                                              0
                                              В общем, в текущих условиях, особенно если речь про мобилки, отправка на сервер с белым IPv4 — это самый предсказуемый по latency вариант, который точно будет работать. Будет ли работать именно UDP — вопрос открытый, но если, опять же, речь про мобильную связь, то обычно UDP работает без проблем, по крайней мере у большой тройки.

                                              Ваши варианты интересные, но они не ориентированы на минимальную задержку отправки, поскольку для работы IPv6 без NAT сейчас нужны всякие «костыли» вроде 6to4, которые явно стабильности и предсказуемости в работе не прибавляют. В светлом будущем, когда у всех белый IPv6 и нет NAT, я верю, что ваш вариант действительно будет самым лучшим, как в плане latency, так и в плане отсутствия централизованного хранения чьих-либо данных.
                                                0
                                                6to4 не работает за NAT.
                                                  0

                                                  У меня работает на всех провайдерах. Только я беру 64 битную зону. Тунельные провайдеры бы тоже тогда не работали.

                                                    0
                                                    Для 6to4 нужен маршрутизируемый IP-адрес, он не работает за NAT. Туннельные провайдеры, использующие 6in4, тоже не работают за NAT.
                                                      0

                                                      Я сейчас нахожусь за NAT.


                                                      Перепроверил. test-ipv6.com даёт 7 из 10. Даже пинганул себя сервисом Online Ping IPv6 все пинги пришли.

                                                        0
                                                        Значит, вам провайдер выдает IPv6, или вы находитесь не за NAT провайдера, а за подконтрольным вам NAT на вашем маршрутизаторе.
                                                          0

                                                          Я сам 6to4 только что настроил(опять настройки слетели) на своём роутере. Провайдеры не хотят давать IPv6. NAT ни одного из провайдеров я не контролирую. От провайдеров на роутер приходит локальные IP(10.x.x.x).


                                                          1. Глобальный IP я узнаю в сервисах типа 2ip.ru а далее генерирую IPv6 зону по нему.
                                                          2. В сервсисе IPv4 to IPv6 Conversion получаю IPv6 адрес.
                                                          3. Заменяю заменяю один ноль рандомным числом и задаю зону /64
                                                          4. Задаю IPv6 роутера
                                                          5. Задаю адрес сервера 192.88.99.1

                                                          Далее на всю технику IPv6 прилетает от роутера.


                                                          Единственно что если исходящего ipv6 трафика не будет то через некоторое время NAT теряет маршрут к роутеру.

                                                            0
                                                            Вы уверены, что ваш провайдер не делает one-to-one NAT, который не транслирует порты и протоколы, а только перенаправляет весь трафик с одного адреса на другой?
                                                              0

                                                              Уверен. Я столько торрентов не качаю сколько вижу на iknowwhatyoudownload.com.

                                                                0
                                                                Это очень странно, либо вы что-то не договариваете. Даже если у вашего провайдера есть conntrack helper для протокола 6in4, то всё равно нельзя определить, кому за NAT предназначается входящее соединение. Полноценно это работать не будет.
                                                                  0

                                                                  Если NAT понимает инкапсулированный IPv6 то ему проще. Запомнил с какого локального IPv4 адреса прилетел IPv6 и соответственно все пакеты в обратную сторону с этим IPv6 адресом в получателе отправлять на тот локальный IPv4.


                                                                  Можно даже ограничится запоминанием 64 бит потсети.

                                                              0
                                                              Везёт вам, у меня 192.88.99.1 скорее всего Ростелеком блочит. Пинги есть, 6in4 не идёт туда. А жаль, когда работало, было быстрее любых 6in4 провайдеров.
                                                                0

                                                                Проблема может быть в фрагментации пакетов. Попробуйте уменьшить MTU для IPv6.


                                                                How to tell what MTU is being used in Windows XP

                                                                  0
                                                                  Пинги то маленькие, должны проходить, я ими туннель тестирую
                                                                    0

                                                                    Видимо действительно телеком что то нашаманил.


                                                                    А жаль, когда работало, было быстрее любых 6in4 провайдеров.

                                                                    Самое интересное что tracert до 192.88.99.1 мне выдавал тот же HE который представляет бесплатный 6in4 тунель. Только в случае 6to4 и регистироваться не надо. В обратную сторону пакет может идти через другого 6to4 провайдера тем самым не создавая узких мест.

                                                                      0
                                                                      Раньше вроде сюда шло: tcinet.ru/press-centre/news/37
                                                                      А сейчас:
                                                                      .......
                                                                      5 15 ms 14 ms 15 ms 217.107.67.133
                                                                      6 15 ms 14 ms 14 ms 192.88.99.1
                                                +1
                                                Авторизованные абоненты могут сигнализировать друг другу напрямую о изменении адреса и порта.

                                                На Андроиде и IOS приложение часто впадают в спячку. Операционная система рвет соедения, после выхода приложения или отключения экрана. IP адреса на мобильных устройствах могут менятся каждые пять минут
                                              +1
                                              Опять в очередном меседжере используется прокладка в виде сервера. Почему бы не отправлять сообщения напрямую получателю ведь интернет на это расчитан. Теперь уже и IPv6 есть которому NAT не нужен.

                                              Сервера нужны по-целому ряду причин:
                                              • Без сервера сервера оба собеседника должны быть постоянно в сети и активно обмениватся пакетами присутвия. Это нереально, в особенности на мобильных устройствах, которые уходят в спячку
                                              • Без сервера можно удобно провести целевые атаки на устройства пользователя. Я попробую пример привести: Вы заблокировали собеседника. Он решили Вам отомстить. Установив Ваш айпи, он устраивает атаку на ваш роутер и Вы лишаетесь интернета на день. Вы меняете айпи, но это не помогает, поскольку злойдей снова находит Вас по p2p. На следующий день Вам звонит Ваш провайдер и предлогает перейти на услуги для комерческих клиентов, потому что Вам нужна защита от Ддос-атак на 10К рублей. Вы покупаете это пакет, но злодей снова находит Вас, сканируют ваши порты и находит уязвимость в Виндовс и скачивает Ваши хоме фото, скан паспорта и т.д

                                              Это лишь первые недостатки, которые пришли в голову. Можно еще много найти минусов. Я бы не назвал сервера ненужно прокладкой. Если нет сервера, то устройство выполняет роль сервера, что не всегда целесобразно.
                                                0
                                                Потому что ОПСОСы предоставляют, почему-то именно IPv4 адрес. ( Посмотрите на своем телефоне). Состояние IPv6 достаточно странное: он давно есть, ни кроме гиков его почти никто не использует…
                                                0
                                                > Почему бы не отправлять сообщения напрямую получателю ведь интернет на это расчитан.

                                                Думаю, это был только образец. Кому нужна более распределённая архитектура — тот догадается, как связать стороны с помощью только сервера локации.

                                                Но даже при полном и окончательном IPv6 не будет лёгкости отправки чего-то кому угодно, потому что даже где нет NAT, используются файрволлы с состоянием. Просто так пускать входящие соединения нельзя, мало ли кто зачем приходит и какой незащищённый сервис сидит внутри. Нужно явное разрешение, а оно проще всего делается, если устройство с внутренней стороны явно такое попросило (в это входит установление TCP соединения или UDP ассоциации).
                                                И пробивать такой файрволл одновременно с двух сторон не всегда получается — он должен поддерживать такое. Хотя, да, это проще, чем с NAT, когда и внешний адрес ремотной стороны может быть заранее неизвестен.
                                                0
                                                Уже существует DTLS — TLS через UDP.
                                                  0
                                                  Да, но у DTLS тоже есть немаленький handshake, что в моём случае сводит весь профит от отсутствия хэндшейка в UDP на нет.
                                                    0
                                                    Не думаю, что это принципиально, ведь соединение перед заходом в лифт будет уже установлено.
                                                      0
                                                      Не факт. Или предполагается «соединение» вообще не закрывать и сохранять сессионные ключи на диск, если приложение выгружено из памяти? Если так, то звучит интересно, конечно.
                                                        0
                                                        Предполагается, что если у вас возникает желание написать кому-то, будучи в лифте, вы, с большой вероятностью, писали что-то кому-то до захода в лифт, а значит, сессия уже открыта.
                                                          0
                                                          Не обязательно. Мне мог придти пуш (вот там соединение и правда уже было открыто, поэтому он и пришел) и я хочу на него ответить. Хочется по-максимуму не завязываться на то, что соединение уже открыто, потому что когда оно открыто, то в целом уже всё работает достаточно хорошо.
                                                            +1

                                                            Вы застряли в лифте и решили об этом написать.

                                                              0

                                                              P.S. не нашёл как в мобильной версии изменять сообщение. В общем вспомнил ваш пост как застрял в лифте.
                                                              P.P.S. и часто наблюдал как связь теряется у владельцев айфона.

                                                      0
                                                      Но важным преимуществом DTLS является его устойчивость к DDoS атакам с амплификацией, поэтому для каких-то вещей его точно можно использовать :).
                                                      0
                                                      Предлагаю в тестирование добавить еще кол-во syscall на 1 мбит/с потока данных, cpu load, pps.
                                                        0
                                                        Я ведь отправляю текстовые сообщения, поэтому там не будет большого PPS. Одно клиентское приложение во время активной отправки будет генерировать от силы с десяток PPS.

                                                        Если речь про сервер, то на одном ядре процессора реалистично обрабатывать до 50k pps, что будет примерно 25k pps на прием и 25k pps на отправку. Мне кажется, даже одного ядра без ухищрений вроде netmap хватит достаточно надолго :).
                                                          0
                                                          Конкретно в Go UDP против TCP гораздо более скромные результаты в PPS. Основная причина в кратном увеличении кол-ве системных вызовов.
                                                      –1
                                                      XMPP сервера тоже могут работать через UPD. По умолчанию идут с TCP, но можно перекллючить протокол на UPD
                                                        0

                                                        Вы же понимаете, что в XMPP оверхед на одно сообщение гигантский, что объем трафика значительно увеличивает? Плюс, он архитектурно достаточно плохо переживает постоянные обрывы соединения и переключения на другой канал. А учитывая посредственную поддержку XMPP в клиентских библиотеках в настоящее время, придётся фактически заново изобретать протокол. Несмотря на то, что XEP, которые решают эту проблему, вообще говоря, есть.

                                                        0
                                                        Господа. Вы решаете не ту задача.

                                                        Дело не в мессенджере, а в том, что tcp становится плохо при хреновой связи. Это проблема сетевого и транспортного уровней, а не уровня приложения.
                                                        Получается, нужно выкинуть из головы всю хрень про шифрование, сервера и прочую лабуду и разработать аналог tcp, который будет нормально работать при 95% потерянных пакетов…
                                                          +1
                                                          Я никогда не понимал, почему разработчики своих apps никогда не тестируют свои творения с плохой связью и не оптимизируют их для работы при плохой связи. Причем речь идет о топовых apps. Я говорю не только про мессенджеры.
                                                            0

                                                            Если для лабы то сойдет. Если для жизни и учесть все нюансы то вы сейчас изобретете недоTCP, который кстати и изобретался для надежной доставки в ненадежных сетях с высоким латенси и потерями, за счет пенальти по скорости и оверхедам по пакетам.

                                                              0
                                                              Как показывает практика, зачастую недоTCP лучше, чем TCP. Протокол не знает, какие вам данные нужны и будет гарантировать доставку и очередность всех пакетов. Таким образом вы одной картинкой сможете повесить прием сообщений всех пользователей с плохой связью в вашем чатике.
                                                                0
                                                                Практика обычно показывает, что любой протокол нужно уметь готовить. REST без дополнительных ухищрений прверх TLS всегда будет плохо работать в нестабильной среде, но это не значит, что TCP плох. Готов поспорить, что разумное применение TCP будет в общем случае работать лучше, чем поделка на коленках на основе UDP. Есть хорошая книжка на эту тему — «Эффективное программирование TCP/IP» (Йон Снейдер). Она достаточно старая, но подробно рассматривает озвученную в статье проблему.
                                                                  +1
                                                                  Готов поспорить, что разумное применение TCP будет в общем случае работать лучше, чем поделка на коленках на основе UDP.

                                                                  В общем случае да, но статья рассматривает частный случай. В частных случаях больший контроль над трафиком будет иметь свои позитивные результаты.

                                                                  Кроме того TCP имеет ряд недостатков, которые приводят к необходимости проверки целостности данных. То есть не смотря на то, что он как бы гарантирует доставку и целостность данных, но это не точно.
                                                                  Особо интересно, когда TCP вреден. Если откинуть медиастриминг, туннелирование и реалтайм данные (к примеру, онлайн игры, биржи), то сходу можно найти проблему приоретизации данных в рамках одной сессии — TCP будет пытаться дослать вам данные, которые для приложения имеют меньший приоритет и вы никак на это не сможете повлиять. Конечно же можно для данных разного рода использовать отдельные соединения, но это не лучший выход из ситуации.

                                                                  Есть хорошая книжка на эту тему — «Эффективное программирование TCP/IP» (Йон Снейдер). Она достаточно старая, но подробно рассматривает озвученную в статье проблему.

                                                                  Книжка хорошо освещает вопрос в общих чертах и даже немного затрагивает достаточно частные случаи, но некоторые выводы и советы там вызывают вопросы. К примеру, в ней дается совет, что не надо изобретать TCP, но тут же сразу упоминается о том, что действительно есть отличные реализации на UDP.
                                                                  Когда она вышла, были вопросы как они умудрились получить потери данных в рамках локалхоста, но в конечном итоге они написали пример «как написалось» — отсюда и потери.
                                                                  Это мы приходим к вашему верному утверждению, что
                                                                  любой протокол нужно уметь готовить.

                                                                  Да, любой протокол нужно уметь готовить. Если в общих случаях TCP подойдет и лень заморачиваться с оберткой вокруг UDP, то используйте TCP и учитесь его готовить, но не ожидайте от него серебряную пулю и готовьтесь к внезапным неожиданным сбоям в продакшене. Для решения некоторых проблем можно попробовать SCTP, но это так же не гарантированный выход.

                                                                  Кстати да, UDP так же не является гарантированно предпочтительным протоколом — не смотря на то, что он дает бОльшую свободу над контролем передаваемых данных, он так же обязывает больше за ними следить.

                                                                  Для каждой задачи свои решения.
                                                                    0
                                                                    Проблема с приоритезацией решается просто — двумя независимыми сессиями. TCP, на самом деле, гарантирует только порядок и целостность и больше никакой магии не делает, и, в общем-то, это первое, что нужно будет придумать своё, если от него отказываться. Следующий «ой» будет на больших сообщениях, тех же картинках — их нужно будет склеивать. Это всё только на бумаге выглядит просто и без проблем — в реальных условиях будет немало сюрпризов.

                                                                    В противовес этому, в варианте с TCP нужно просто написать нормальный менеджмент сессий. Но его для UDP какой-то тоже придется делать, хоть и попроще.

                                                                    Справедливости ради, у TCP есть другая неявная проблема — особенности реализации под каждую ОС, например, разные таймауты по-умолчанию — но это можно и нужно решать менеджментом сессий.

                                                                    В общем, если задачу ограничить как «доставить короткое сообщение немедленно и без шифрования» — UDP подойдёт. Если вы всё-таки хотите полноценный мессенджер — рано или поздно дешевле будет от такого подхода отказаться.
                                                                      0
                                                                      > Следующий «ой» будет на больших сообщениях, тех же картинках — их нужно будет склеивать. Это всё только на бумаге выглядит просто и без проблем — в реальных условиях будет немало сюрпризов.

                                                                      Про это речи в статье не шло. Речь только про текст. Для картинок и для больших ответов или запросов уже нужен TCP.

                                                                      > В общем, если задачу ограничить как «доставить короткое сообщение немедленно и без шифрования» — UDP подойдёт. Если вы всё-таки хотите полноценный мессенджер — рано или поздно дешевле будет от такого подхода отказаться.

                                                                      Заметьте, что я не говорил, что всё общение должно идти по UDP. Это лишь дополнительный вариант, который нужен для ситуаций, когда у человека совсем плохая связь и он очень хочет что-то кому-то отправить в виде текста (у меня такое иногда бывает в лифте или в метро).
                                                                        0
                                                                        Заметьте, что я не говорил, что всё общение должно идти по UDP. Это лишь дополнительный вариант, который нужен для ситуаций, когда у человека совсем плохая связь и он очень хочет что-то кому-то отправить в виде текста (у меня такое иногда бывает в лифте или в метро).

                                                                        Вы понимаете, что работа мессенджера в суровых условиях — это конкурентное преимущество? (хоть и не для большинства) В погоне за функциональностью и стикерами простые мессенджеры забывают просто работать.
                                                                        Нет смысла щемиться сразу на несколько соединений — надо работать на самом быстром, а если не получается, то уже дальше пробовать что доступно. Включая туннелирование через ICMP или 53 порт. Да, медленно, но оно работает и даже файлы передаются.
                                                                          0
                                                                          > Нет смысла щемиться сразу на несколько соединений
                                                                          Почему же? Если UDP заблокирован, то хотя бы за приемлемое время пользователь получит фидбек.

                                                                          > Вы понимаете, что работа мессенджера в суровых условиях — это конкурентное преимущество? (хоть и не для большинства) В погоне за функциональностью и стикерами простые мессенджеры забывают просто работать.
                                                                          Понимаю, поэтому и решил придумать свой протокол :). Но кастомным протоколом реально улучшить работу лишь для очень простых случаев, как отправка сообщения — всё-таки в TCP/IP и/или QUIC не зря люди столько усилий вложили. В отличие от своего протокола, TCP и QUIC умеют в congestion control, обработку переупорядочевания пакетов, защиту от DDoS и т.д.
                                                                            0
                                                                            Почему же? Если UDP заблокирован, то хотя бы за приемлемое время пользователь получит фидбек.

                                                                            Проще использовать что-то типа логики доверия каналу — пробуем все варианты и потом используем лучший. Если он теряет доверие, то еще раз пробуем.

                                                                            Понимаю, поэтому и решил придумать свой протокол :)

                                                                            Yet another protocol… YAP!
                                                                            Это больше походит на размышления о проблеме, но не протокол. Кстати, именно как размышления о проблеме статься читается хорошо.

                                                                            защиту от DDoS

                                                                            TCP и защита от DDoS?… Ну знаете… Эх…
                                                                              0
                                                                              > TCP и защита от DDoS?… Ну знаете… Эх…

                                                                              Я имел в виду атаки усиления, когда ваш сервер используют как промежуточный сервер для DDoS. Сам по себе TCP к DDoS так себе устойчив, согласен.
                                                                        0
                                                                        Проблема с приоритезацией решается просто — двумя независимыми сессиями.

                                                                        Я как раз и написал, что это не лучший выход из ситуации.
                                                                        В условиях, где одна TCP сессия еле работает, вы хотите поднимать и менеджерить несколько независимых? Еще раз повторю: это не самое лучшее решение.

                                                                        TCP, на самом деле, гарантирует только порядок и целостность и больше никакой магии не делает, и, в общем-то, это первое, что нужно будет придумать своё, если от него отказываться.

                                                                        Я в курсе. Напомню, мы говорим про условия очень плохой связи.
                                                                        На порядке вы получите затык по скорости, фризы и отвалы по таймауту, на доверии к целостности проблемы — TCP не так уж и хорошо гарантирует целостность данных.
                                                                        И да, надо будет это реализовывать самому и ничего зазорного плохого и страшного в этом нет.

                                                                        Следующий «ой» будет на больших сообщениях, тех же картинках — их нужно будет склеивать. Это всё только на бумаге выглядит просто и без проблем — в реальных условиях будет немало сюрпризов.

                                                                        Серьезно? Такая гигантская проблема? Сюрпризы будут, но назвать их проблемами язык не поворачивается. Если не знаете решения, то подсмотрите в битторент хотя бы.

                                                                        В противовес этому, в варианте с TCP нужно просто написать нормальный менеджмент сессий. Но его для UDP какой-то тоже придется делать, хоть и попроще.

                                                                        Вы отталкиваетесь от того как _вам_ будет проще это написать, а не как выдать решение, которое будет работать в бОльшем количестве условий.

                                                                        В общем, если задачу ограничить как «доставить короткое сообщение немедленно и без шифрования» — UDP подойдёт.

                                                                        Шифрование вы прямо к TCP привязываете? Все остальное уже гарантированно без криптографии будет? Это что-то новенькое.

                                                                        Если вы всё-таки хотите полноценный мессенджер — рано или поздно дешевле будет от такого подхода отказаться.

                                                                        Дешевле будет отказаться от работы в условиях плохой и нестабильной связи. Но если вам надо работать в таких условиях, то сценарий будет иной.
                                                                        Еще раз повторюсь: вы отталкиваетесь от того, как вам будет проще писать. Статья и мысли совсем о другом.

                                                                        Да, проблема доставки сообщений и медиаданных в условиях нестабильной связи взята не с потолка, а из реальной жизни. И да, UDP показывает себя очень ощутимо лучше.
                                                                        Хех, вы еще скажите, что UDP не нужен и от него нужно отказываться.
                                                                          0
                                                                          Вы напрасно заняли агрессивную позицию. На самом деле вопрос интересный и простого ответа (в том числе, как у вас в статье) тут быть не может.

                                                                          Вы сравниваете теоретический мессенджер на базе UDP с современными реальными на базе TCP и делаете достаточно жирное допущение о том, что проблемы современных мессенджеров связаны с TCP. Я говорю о том, что при разработке реального мессенджера с конкретным набором требований (шифрование, аутентификация, медиаконтент, подтверждения) — может оказаться, что не такой уж большой выигрыш дает UDP. Да, концепт без шифрования и прочих рюшек на UDP скорее всего выиграет, но не будет жизнеспособен в реальных условиях.

                                                                          Я считаю, что проблемы современных мессенджеров не в том, что они используют TCP, а в том, сколько ещё уровней (часто избыточных) они используют поверх него.

                                                                          Косвенно, это подтверждает мой опыт использования IRC, ICQ и Jabber через ранний GPRS — проблемнее всего тогда у меня был Jabber, и если взглянуть на его протокол — он самый избыточный. Позднее, когда компрессия стала распространенным явлением, вся тройка более-менее сравнялась и далее их юзабилити ограничивалось только глюками и особенностями конкретного протокола.

                                                                          Кроме того, в общем, уже существуют протоколы, которые по крайней мере в теории поддерживают все три транспорта и используют более подходящее управление сессиями (MTProto). И да, оно лучше, но я бы не сказал, что на столько же лучше на сколько сложнее такая полная реализация.

                                                                          Ну право же, SYN-ACKSYN-ACK это три очень маленьких пакета. Да, это занимает время, но это не так страшно, как установка TLS сессии и аутентификация поверх — вот с этим действительно надо бороться, а тут что по UDP что по TCP overhead будет примерно одинаковым
                                                                            0
                                                                            Вы напрасно заняли агрессивную позицию.

                                                                            Так получилось. Не хотел :)

                                                                            в том числе, как у вас в статье

                                                                            Статья не моя :)

                                                                            Вы сравниваете теоретический мессенджер на базе UDP с современными реальными на базе TCP и делаете достаточно жирное допущение о том, что проблемы современных мессенджеров связаны с TCP. Я говорю о том, что при разработке реального мессенджера с конкретным набором требований (шифрование, аутентификация, медиаконтент, подтверждения) — может оказаться, что не такой уж большой выигрыш дает UDP. Да, концепт без шифрования и прочих рюшек на UDP скорее всего выиграет, но не будет жизнеспособен в реальных условиях.

                                                                            Нет. Я сравниваю реальное использование внутренней разработки. Ну условия у нас такие, что нужно выжимать максимум из канала — он совсем не ахти и от этого отталкиваемся. С шифрованием и всеми прочими плюшками включая медиаконтент (не ради котиков).

                                                                            Я считаю, что проблемы современных мессенджеров не в том, что они используют TCP, а в том, сколько ещё уровней (часто избыточных) они используют поверх него.

                                                                            И это тоже, но в вопросе TCP vs UDP надежнее последний.

                                                                            через ранний GPRS

                                                                            Раньше GPRS работал. Сейчас он есть для галки и от него остался кусок, который можно использовать через оное место.

                                                                            Ну право же, SYN-ACKSYN-ACK это три очень маленьких пакета. Да, это занимает время, но это не так страшно, как установка TLS сессии и аутентификация поверх — вот с этим действительно надо бороться, а тут что по UDP что по TCP overhead будет примерно одинаковым

                                                                            Ви таки не поверите, но далеко не в этих трех пакетах проблема.
                                                                            Тут ниже netch80 описал одну из проблем в очередности пакетов и их гарантии доставки — это один из сценариев, когда у вас сообщения не будут доходить совсем.
                                                                              0
                                                                              Статья не моя :)

                                                                              Извиняюсь, проглядел

                                                                              Раньше GPRS работал

                                                                              Ну, мне запомнились потери пакетов в диапазоне 30-50% на 64-байтных пингах. Сейчас, думаю, там так же или чуть хуже

                                                                              Я сравниваю реальное использование внутренней разработки

                                                                              Если у вас заранее известны особенности канала — понятное дело, заточенное под него решение будет работать лучше. То, о чем я пишу, я имею в виду в контексте глобальных сетей (плюс, желательно предусмотреть, чтобы толпа таких клиентов в одной локации не положила сеть лавиной ретрансмитов если вдруг аплинк на минуту пропадет)

                                                                              Касательно очередности — да, согласен. Но тут имхо зависит от решаемой задачи. Лично я в контексте мессенжера предпочитаю подождать, пока соединение переподнимется и мне дойдут все сообщения, чем гадать, не потерялась ли часть смысла по пути. Если от этого отказаться — очевидно, UDP начинает подходить больше. И да, это именно то, что я имею в виду, когда говорю про аккуратное управление сессиями — то есть выставление таймаутов и переинициализация соединения, если оно залипло, если позволяет платформа — корректная установка опций сокета.

                                                                              Разумеется, всё это я говорю в контексте коротких текстовых сообщений, если в ту же сессию полетят котики — разговор будет другой
                                                                                0
                                                                                Если у вас заранее известны особенности канала — понятное дело, заточенное под него решение будет работать лучше.

                                                                                А далеко ходить не надо, у нас в стране связь далеко не везде достойная.
                                                                                Да, в городах часто хорошая, но есть еще территории вне города, под землей итд. Даже в домах связь может барахлить как старый трактор.
                                                                                Так что можно просто сразу взять 50кбпс 300-1500мс с потерей 30% и тестить. Если такие условия интересны. Обычно бизнес смотрит на такие условия как на удел неудачников, о которых не стоит думать.

                                                                                Но тут имхо зависит от решаемой задачи.

                                                                                Вроде бы цель озвучена изначально :) Нужен мессенджер в паршивых условиях.

                                                                                Лично я в контексте мессенжера предпочитаю подождать, пока соединение переподнимется и мне дойдут все сообщения, чем гадать, не потерялась ли часть смысла по пути.

                                                                                Это решается на прикладном уровне. В том же iMessage забавно наблюдать ситуации, когда отвечаешь на сообщение, а оно встает по времени выше, чем то, на которое отвечаешь.

                                                                                Если от этого отказаться — очевидно, UDP начинает подходить больше.

                                                                                Я только принимаю участие в дискуссии UDP vs TCP при плохой связи. Там дальше еще много приколов, но они намного проще решаются, чем обеспечение приемлемого уровня передачи данных в TCP. Бывает, что нужно передать фотографию места (к примеру, узел у черта на куличиках), но при этом не прерывать текст. Фото дойдет, текст будет раньше и это все в рамках одного потока.
                                                                                Это очень веселая и интересная задача. Работа UDP тут — около 3к строк на Си. Дальше идет логика куда более занятная.
                                                                                Кстати, для мессенджера один поток в сети — это, сугубо на мой взгляд, лучшее решение. Тут просто великолепно себя показывает sctp, но дальше экспериментов мы не смогли его запустить — не поддерживается. Великолепно — это выкинуто подавляющее большинство наших костылей, но не без нареканий (некоторые могут вогнать в ступор). По крайней мере это лучшее в соотношении затраченные усилия/профит.

                                                                                И да, это именно то, что я имею в виду, когда говорю про аккуратное управление сессиями — то есть выставление таймаутов и переинициализация соединения, если оно залипло, если позволяет платформа — корректная установка опций сокета.

                                                                                А оно вам надо? Зачем столько головняков для исправления проблем TCP когда можно сделать то же самое только без сюрпризов?

                                                                                Разумеется, всё это я говорю в контексте коротких текстовых сообщений, если в ту же сессию полетят котики — разговор будет другой

                                                                                Я говорю обо всех случаях с мыслью, что если решение работает достойно при плохой связи, то с большой вероятностью оно будет работать достойно и при отличном канале. Ну и с котиками, конечно же.
                                                                                  0
                                                                                  Интересный опыт. Спорить не буду, вы это уже написали, я — нет. Интересно было бы сравнить две качественные реализации в диких условиях.
                                                                                    0
                                                                                    Справедливости ради стоит отметить, что именно я ничего не писал — я всего лишь техПМ (или как это сейчас называется). В общем ПМ, но в курсе того как это работает, как и почему именно так реализовано итд со всеми сопутствующими вопросами и познаниями.

                                                                                    Мы сравнивали внутренний чат с ТГ и вацапом (как наиболее популярные).
                                                                                    ТГ — самый удобный, но отваливается при первом случае.
                                                                                    Вацап — пытается держаться до последнего, но все же не приемлемо. Что больше всего не понравилось — у него случаются моменты, что он делает вид, что держится до последнего, хотя пакеты не передает хотя при этом отображает, что сообщение ушло на сервер.
                                                                                    У нас только один плюс среди них — работа в полевых условиях. Одна из плюшек — передача геометки на сервер одним мелким пакетом ежеминутно. Мало ли что по пути может произойти.

                                                                                    Один из интересных моментов — передача файлов. Все происходит по логике bittotrent, но с учетом доверию к генералам.
                                                                    0

                                                                    Конечно, если изобрести что-то очень очень узкозаточенное, причем и по функционалу и по условиям использования — есть шанс сделать лучше. Но это будет очень узко специализированное.


                                                                    А например в настройках сетевого стека tcp у линукс 100500 всяких параметров. Там наверняка есть то что нужно, надо только найти нужное )))

                                                                      0
                                                                      Конечно, если изобрести что-то очень очень узкозаточенное, причем и по функционалу и по условиям использования — есть шанс сделать лучше. Но это будет очень узко специализированное.

                                                                      Про общие случаи не пишут в статьях. Они обычно рассмотрены в различной учебной литературе.
                                                                      Грубо говоря, если допускается результат работы при использовании общих подходов, то лучше их и использовать. В иных случаях нужно искать другие решения.

                                                                      А например в настройках сетевого стека tcp у линукс 100500 всяких параметров. Там наверняка есть то что нужно, надо только найти нужное )))

                                                                      Не уверен, что будет найден нужный ключик. К примеру, приоретизация данных в рамках одной сессии. Откуда операционной системе или протоколу знать что важнее в данных конкретного приложения?
                                                                      И еще как-то странно будет, если мессенджер при установке будет менять параметры всего стэка.
                                                                        0
                                                                        Безусловная сетевая «доброжелательность» TCP, которая реализуется в виде exponential backoff, не выключается ни в одном известном стеке.
                                                                        Нарушение необходимости присылать ранее отправленные данные — тоже раньше — основа TCP и не может быть устранена без его полной замены.

                                                                        Эти вопросы более-менее отработаны в SCTP. Там есть
                                                                        1) раздельные каналы (не менее 10 по стандарту), в каждом из которых своё упорядочение сообщений;
                                                                        2) опции отправки сообщений с ограничением по количеству перепосылок или по времени доставки.

                                                                        Но с SCTP массовая проблема в поддержке раутерами с файрволлами и NAT, многие (особенно любимые вендорами корпоративные реализации) его не умеют и не собираются уметь.
                                                                          0
                                                                          Ну на самом деле странные доводы. Если сравнивать «Тупой Rest поверх TCP/IP» со специализированным протоколом… то наверное это как то бессмысленно.
                                                                          1) раздельные каналы (не менее 10 по стандарту), в каждом из которых своё упорядочение сообщений;

                                                                          Кто мешает сделать 10 логических каналов в TCP? Передавайте контролинг в одном канале, онлайн сообщения в другом, картинки качайте в третьем. Причем не тупо передачей файла а побив на фрагменты.

                                                                          2) опции отправки сообщений с ограничением по количеству перепосылок или по времени доставки.

                                                                          Аналогично это может быть на логике приложения.

                                                                          Нарушение необходимости присылать ранее отправленные данные

                                                                          Тут я не понял про что вы? tools.ietf.org/html/rfc2018 — SACK — по моему уже 100 лет везде по умолчанию включена.

                                                                          Нет, конечно TCP не идеален, иначе не было бы разных специфических стеков. Но IMHO мессенджеры это не та сфера где уже исчерпаны все возможности TCP, чтобы жертвовать преимуществами его широкого распространения.
                                                                            0
                                                                            > Кто мешает сделать 10 логических каналов в 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 тут на пользу.
                                                                            Я не считаю, что во всём он прав, но задуматься — стоит.
                                                                    0
                                                                    > UDP может «резаться» провайдером — нужно уметь работать и по TCP/IP

                                                                    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. Пока что вся статья на уровне «открытия Америки».
                                                                    — Самая серьёзная заточка требуется в области криптографии. Повторюсь, неавторизованного клиента надо вначале тщательно проверить (пусть даже это длинный обмен), затем можно пользоваться готовой авторизацией (сессионные ключи и т.п.), но тут без специалиста не обойтись.
                                                                      0
                                                                      > Безусловно (с поправкой на термины). И тут даже раз в секунду — плохо, надо, повторюсь, exponential backoff.
                                                                      > Его вариант из 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 раз).

                                                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                    Самое читаемое