@BUTURUM

Как раздельно передать сообщения, которые не помешаются в один пакет?

В нашем прекрасном мире существует такой прекрасный протокол передачи данных как tcp/ip. Он позволяет передавать информацию дробя её на пакеты. Пакеты это хорошо, но я до сих пор не вдупляю как протоколы основанные на TCP умудряются передать цельно сообщения, в несколько пакетов. Возьмём тот же самые HTTP, вряд-ли даже GET запрос отправляться в один пакет, но каким-то обзором сервер понимает что все пакеты переданы и можно начинать разбирать запрос, притом что соединения не обрывается. Я же хотел написать протокол для своего приложения (локального мессенджера), который сможет передать зашифрованные сообщения по TCP/IP соединению от обоих его участников, желательно параллельно. Сообщения как вы понимаете в один пакет не влезет, поэтому нужно как-то объяснить получателю что все пакеты одного сообщения переданы.
  • Вопрос задан
  • 367 просмотров
Решения вопроса 1
@yaror
10 лет в мобильном телекоме
А всё просто )
Когда Вы работаете с протоколом TCP, вы не оперируете понятием "пакет": TCP скрывает от Вас тот факт, что сообщение при передаче каким-то образом разбивается на отдельно передаваемые фрагменты.
Причём, уровень шифрования, например, SSL/TLS, большинство библиотек реализуют прозрачно и незаметно для Вас, поэтому Ваша программа может и не знать, что в какой-то момент данные на отправке зашифровываются, а на приёме расшифровываются.

Логика отправки или приёма чего-то из TCP-соединения абсолютно такая же, как и при работе с дисковым файлом: есть некоторый поток байт, которые на уровне протокола никак на отдельные сообщения не разделены. Признак "началось новое сообщение" Вам надо придумать самому.

Например, популярной является разметка TLV: Tag-Length-Value
Tag: тип сообщения или некоторый маркер начала нового сообщения. Как правило, это поле фиксированной длины, например, 1 байт.
Length: длина сообщения. Поле фиксированной длины, например, 4 байта.
Value: само передаваемое сообщение переменной длины.

Какое какое именно значение передавать в Length - решать Вам.
Можно сделать так:
Length = длина_поля_в_байтах(Value)
А можно и так:
Length = длина_поля_в_байтах(Tag) + длина_поля_в_байтах(Length) + длина_поля_в_байтах(Value)

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

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

2. Будьте готовы к тому, что Вы можете начать читать некоторое сообщение в тот момент, когда его отправка с той стороны уже начата, но ещё не завершена.
Зато, Вы можете в любой момент посмотреть, сколько там байт уже прилетело и накопилось в буфере приёма.

Пример: допустим, Вы ожидаете сообщение длиной 100 байт.
Возможна ситуация, когда Вы прочитали из соединения 50 байт, а затем Вы получаете уведомление "больше данных нет". Это означает, что оставшиеся 50 байт всё ещё где-то в пути; Вам следует периодически повторять процедуру чтения из соединения до тех пор, пока Вы не получите, вероятно, за несколько приёмов, оставшиеся данные.

Получается, на псевдокоде пусть не оптимальная, но зато простейшая логика чтения каждого нового сообщения в разметке TLV может иметь следующий вид:

пока (соединение_не_закрыто):
    подождать_пока_в_буфере_приёма_не_окажется_байт(1)
    tag = прочитать_байт(1)

    подождать_пока_в_буфере_приёма_не_окажется_байт(4)
    length = прочитать_байт(4)

    подождать_пока_в_буфере_приёма_не_окажется_байт(length)
    value = прочитать_байт(length)

    обработать_новое_сообщение(tag, value)


Подобная логика позволит Вам нормально обработать ситуацию, когда кто-то отправил Вам два сообщения подряд, но сразу до Вас первое сообщение долетело целиком, а от второго - только первая половина: столько влезло в один TCP-пакет. Вторая же половина второго сообщения в следующем TCP-пакете может прилететь даже и через полчаса.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Zoominger
@Zoominger Куратор тега Компьютерные сети
System Integrator
Курите модель OSI.

Данные в TCP (4 уровень) никак не связаны с тем, что там в GET (7 уровень), ему вообще наплевать.

И ещё подробно раскурите протокол TCP (читайте Куроуза и Олиферов), там просто и понятно объясняется, как TCP понимает, как управлять потоком, как понять, на сколько частей дробить и как понять, что все части собрались воедино.

Ваш протокол 100% будет работать на прикладном, 7-ом уровне и TCP по барабану, шифрованные данные там передаются или нет.
Ответ написан
@res2001
Developer, ex-admin
TCP/IP - это набор протоколов, а не только TCP.
Конкретно протокол TCP - это потоковый протокол, ориентированный на соединение.
Потоковый - это значит, что данные для вышестоящего уровня, который использует TCP, прядставляются не в виде отдельных пакетов, а в виде потенциально безразмерного потока байтов.
Так что по TCP вы без труда можете гонять гигабайты, не заморачиваясь на разбиение их по пакетам - TCP сам все разберет на отдельные пакеты, а на принимающей стороне сам все пакеты сложит в поток.
При этом сохраняется порядок байт, а если что-то потеряется при передаче, то будет повторено еще раз.

Протокол, который ориентирован на передачу отдельных сообщений (пакетов) - UDP. Он то же входит в набор протоколов TCP/IP.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы