Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>Что-то одно из двух убрать, или длину, или маркер конца. С длиной приемник будет эффективнее работать: не надо будет сканировать каждый байт в поисках маркера.
Дело в том, что в Qt реализации TCP подсистемы имеет место генерация сигнала readyRead объектом типа QTcpSocket —
в тот самый момент, когда фрагмент (или блок) данных поступил на приёмник от дальнего_конца.
Я подписываюсь (своим слотом) на вышеуказанный сигнал.
Когда данный сигнал успешно отработал, ко мне приходит массив байтов типа QByteArray (его общая длина — известна).
Этот массив — может представлять собой целый блок (ну то-есть пользовательский_пакет) или же только его фрагмент.
Я же проверяю не все байты, а только четыре_последних_байта на маркер конца:
— если его нет, то помещаю этот массив в "накопитель" и жду недостающие фрагменты;
— если он есть — считаем что блок данных успешно принят — его можно применять.
Здравствуйте, AlexGin, Вы писали:
AG>Этот массив — может представлять собой целый блок (ну то-есть пользовательский_пакет) или же только его фрагмент. AG>Я же проверяю не все байты, а только четыре_последних_байта на маркер конца: AG>- если его нет, то помещаю этот массив в "накопитель" и жду недостающие фрагменты; AG>- если он есть — считаем что блок данных успешно принят — его можно применять.
Если удаленная сторона может отправить несколько блоков данных, то тебя ждёт неприятный сюрприз. К тебе в буфер они могут прийти вместе.
Стримы, мать их за ногу...
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, уважаемый Stanislav V. Zudin, Вы писали:
SVZ>Если удаленная сторона может отправить несколько блоков данных, то тебя ждёт неприятный сюрприз. К тебе в буфер они могут прийти вместе. SVZ>Стримы, мать их за ногу...
Я при разработке протокола обмена сделал так, чтобы новый блок данных отправлялся только после того,
как противоположная сторона приняла предшествующий блок (и передала нам квитирующий блок).
То есть — приведенный выше сценарий не реален. Даже для стримов.
Здравствуйте, AlexGin, Вы писали:
AG>Этот массив — может представлять собой целый блок (ну то-есть пользовательский_пакет) или же только его фрагмент. AG>Я же проверяю не все байты, а только четыре_последних_байта на маркер конца:
А что ты будешь делать, если у тебя эти четыре байта разрезались между двумя фрагментами?
AG>- если его нет, то помещаю этот массив в "накопитель" и жду недостающие фрагменты; AG>- если он есть — считаем что блок данных успешно принят — его можно применять.
И ты сразу подрезал себе возможность посылать запросы не по одному, а сразу пачками, а потом получить пачку ответов.
Здравствуйте, AlexGin, Вы писали:
AG>Да, передавать полный размер в начале — также неплохая идея. Может стоит совместить это с маркером конца пакета? На случай потери промежуточных фрагментов, например.
О какой потере промежуточных пакетов в TCP идет речь?
Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>А что ты будешь делать, если у тебя эти четыре байта разрезались между двумя фрагментами?
То есть — последний фрагмент — менее 4-х байт? Воообще — возможно ли это?
Впрочем, вопрос вполне резонный.
Возможно — правильнее было бы передавать длину всего блока (в начале блока).
После этого делать "накопление" данных, но критерием окончания накопления — делать не маркер конца,
а факт совпадения длины с заранее заявленной.
Pzz>И ты сразу подрезал себе возможность посылать запросы не по одному, а сразу пачками, а потом получить пачку ответов.
Это протокол обмана сообщениями, или базар
Как я уже упоминал, в рамках одной сессии работает пара: Сервер + конкретный_Клиент.
Скорость обмена — устраивает Заказчика. Зачем делать такое ненужное усложнение?
Здравствуйте, Marty, Вы писали:
M>О какой потере промежуточных пакетов в TCP идет речь?
+100500
Теоретически — да, вроде как этого не должно (и не может) быть.
Практически же — есть провод (грубо говоря) и он вдруг потерял контакт. Ну скажем на пару секунд...
Теоретически — при этом или не совпадут контрольные суммы пакетов_нижнего_уровня или будет time-out.
Практически же — никто не знает. Ну или же каждый разработчик прикладного ПО на_верхнем_уровне защищается от ситуации по-своему.
Здравствуйте, AlexGin, Вы писали:
AG>Теоретически — при этом или не совпадут контрольные суммы пакетов_нижнего_уровня или будет time-out. AG>Практически же — никто не знает.
Или отвалится, или нет. Других вариантов нет.
AG>Ну или же каждый разработчик прикладного ПО на_верхнем_уровне защищается от ситуации по-своему.
Никто при использовании TCP в здравом уме не защищается
Здравствуйте, AlexGin, Вы писали:
Pzz>>А что ты будешь делать, если у тебя эти четыре байта разрезались между двумя фрагментами? AG>То есть — последний фрагмент — менее 4-х байт? Воообще — возможно ли это?
Конечно возможно.
AG>После этого делать "накопление" данных, но критерием окончания накопления — делать не маркер конца, AG>а факт совпадения длины с заранее заявленной.
Да. А маркер конца становится ненужным.
Pzz>>И ты сразу подрезал себе возможность посылать запросы не по одному, а сразу пачками, а потом получить пачку ответов. AG> AG>Это протокол обмана сообщениями, или базар
Это называется request pipelining. Полезная штука, если скорость начинает упираться в round-trip time, а не в пропускную способность канала.
AG>Как я уже упоминал, в рамках одной сессии работает пара: Сервер + конкретный_Клиент. AG>Скорость обмена — устраивает Заказчика. Зачем делать такое ненужное усложнение?
У тебя есть логический уровень, который отображает твои сообщения, имеющие начало и конец, на поток байтов, которым является TCP (назовем этот уровень framing), и логический уровень, который определяет, что сообщения ты будешь использовать в режиме запрос-ответ. А ты взял, и смешал эти два логических уровня.
Здравствуйте, Marty, Вы писали:
AG>>Ну или же каждый разработчик прикладного ПО на_верхнем_уровне защищается от ситуации по-своему.
M>Никто при использовании TCP в здравом уме не защищается
Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
AG>Ну или же каждый разработчик прикладного ПО на_верхнем_уровне защищается от ситуации по-своему. M>Никто при использовании TCP в здравом уме не защищается
IMHO — это зависит от уровня ответственности ПО:
— многопользовательская сетевая игра — да в здравом уме никто не защищается от описанной ситуации!
— система управления движением самолётов в международном аэропорту...
Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
Ты не поверишь, уважаемый Pzz, но в перспективе я бы хотел и от этого защититься. Ну а пока — вроде как не актуально.
Здравствуйте, AlexGin, Вы писали:
Pzz>>Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
AG>Ты не поверишь, уважаемый Pzz, но в перспективе я бы хотел и от этого защититься. Ну а пока — вроде как не актуально.
Если хочется железобетонный слой защиты, рекомендую HTTPS. Там и защищено всё так, что при всём желании не испортить ничего; и вопросы разбиения сообщений продуманы и реализаций в любом языке вагон и маленькая тележка. Я, конечно, понимаю, что всем нравится байты в сокете перекладывать, но с точки зрения практичности лично для меня нужны очень убедительные аргументы, чтобы НЕ использовать HTTP.
Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>Да. А маркер конца становится ненужным.
+100500
Здесь всё ясно и вполне логично.
Здесь я тебя понял и согласился с твоими аргументами.
Pzz>Это называется request pipelining. Полезная штука, если скорость начинает упираться в round-trip time, а не в пропускную способность канала.
Это поищу и подумаю — насколько мне это актуально.
Pzz>У тебя есть логический уровень, который отображает твои сообщения, имеющие начало и конец, на поток байтов, которым является TCP (назовем этот уровень framing), и логический уровень, который определяет, что сообщения ты будешь использовать в режиме запрос-ответ. А ты взял, и смешал эти два логических уровня.
Что здесь не так?
Я использовал некоторое подмножество вариантов применения TCP.
Да, может возможно было бы более рационально использовать пропускную спрсобность канала.
Но это не имеет актуальности.
Здравствуйте, уважаемый vsb, Вы писали:
vsb>Если хочется железобетонный слой защиты, рекомендую HTTPS. Там и защищено всё так, что при всём желании не испортить ничего; и вопросы разбиения сообщений продуманы и реализаций в любом языке вагон и маленькая тележка. Я, конечно, понимаю, что всем нравится байты в сокете перекладывать, но с точки зрения практичности лично для меня нужны очень убедительные аргументы, чтобы НЕ использовать HTTP.
Хорошо — при HTTP клиент играет роль активной стороны, то есть клиент инициирует обмен данными.
Что делать — если что-либо поменялось на_сервере (клиент не знает об этом факте)?
M>>Никто при использовании TCP в здравом уме не защищается
Pzz>Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
У тебя вместе с tcp 3-4 уровня (физический, Ethernet(может не быть), IP, TCP) и на каждом своя контрольная сумма. Это как должен шуметь канал, чтобы данные по нему все же бегали, но иногда на всех 4-х уровнях контрольные суммы все же совпадали.
Тут я соглашусь, что защищаться от случайного искажения данных на уровне TCP смысла нет. Только если будет умышленная подмена (не случайная) — тогда нужно считать криптографический MAC (HMAC, например). Но в таком случае возникает задача обмена ключами для проверки MAC, потому что обычный HASH злоумышленник тоже сможет подменить. Однако, такое проще завернуть в известный протокол типа TLS, чем придумывать что-то самому.
Здравствуйте, AlexGin, Вы писали:
vsb>>Если хочется железобетонный слой защиты, рекомендую HTTPS. Там и защищено всё так, что при всём желании не испортить ничего; и вопросы разбиения сообщений продуманы и реализаций в любом языке вагон и маленькая тележка. Я, конечно, понимаю, что всем нравится байты в сокете перекладывать, но с точки зрения практичности лично для меня нужны очень убедительные аргументы, чтобы НЕ использовать HTTP. AG> AG>Хорошо — при HTTP клиент играет роль активной стороны, то есть клиент инициирует обмен данными. AG>Что делать — если что-либо поменялось на_сервере (клиент не знает об этом факте)?
Ой всё
Да, тут сложней. Решение есть, даже два. Либо websockets, либо HTTP/2. Ну или делать polling, на самом деле тоже не самый плохой вариант. Но такое сам не пробовал и это уже посложней. Моя реплика в первую очередь относилась к модели запрос/ответ. Если нужно со стороны сервера инициировать сообщения, тут уже не всё так однозначно, соглашусь.
Здравствуйте, Reset, Вы писали:
Pzz>>Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
R>У тебя вместе с tcp 3-4 уровня (физический, Ethernet(может не быть), IP, TCP) и на каждом своя контрольная сумма. Это как должен шуметь канал, чтобы данные по нему все же бегали, но иногда на всех 4-х уровнях контрольные суммы все же совпадали.
У IPv4 контрольная сумма защищает только заголовок. У IPv6 по-моему вообще её нет.
Здравствуйте, Reset, Вы писали:
R>У тебя вместе с tcp 3-4 уровня (физический, Ethernet(может не быть), IP, TCP) и на каждом своя контрольная сумма. Это как должен шуметь канал, чтобы данные по нему все же бегали, но иногда на всех 4-х уровнях контрольные суммы все же совпадали.
Ну, на физическом уровне контрольной суммы обычно нет. Поэтому если на уровне Ethernet контрольная сумма почему-то отсутствует, то остается только паршивенькая контрольная сумма TCP.
R>Тут я соглашусь, что защищаться от случайного искажения данных на уровне TCP смысла нет. Только если будет умышленная подмена (не случайная) — тогда нужно считать криптографический MAC (HMAC, например). Но в таком случае возникает задача обмена ключами для проверки MAC, потому что обычный HASH злоумышленник тоже сможет подменить. Однако, такое проще завернуть в известный протокол типа TLS, чем придумывать что-то самому.
Здравствуйте, AlexGin, Вы писали:
AG>Вопросы: AG>MTU равный примерно 1.5 KBytes — это для произвольных данных? AG>Для текстовых данных этот показатель порядка 14 KBytes. Почему различие почти в десять раз?
Я не вижу там цифру типа 14KB. Вижу 1400. Вы один ноль не додумали?
По сути:
1. Как уже сказано, TCP поддерживает просто октетный поток. Что отправлено с одной стороны, придёт (если вообще придёт) на другую, в том же порядке и том же составе, без добавлений, потерь и модификаций. Ну, точнее, бывают разные случаи: бывает, что у маршрутизаторов память битая, или какое ещё безобразие. Какими порциями будет читаться на приёме — неизвестно, поэтому надеяться на передачу теми же порциями нельзя, нужно иметь метки в самом потоке.
2. MTU определяется как детектированный минимум из всех линков по дороге пакета до ремотного хоста. Именно что всех, а не только ближайших. Реально в нынешнем мире не бывает интерфейса, кроме loopback и спец. локальных настроек, у которых MTU самого линка более 1500. Меньше — бывает — если проследует через интерфейс. И ещё вычтите 52 байта на IP+TCP (иногда 40, но это всё реже и реже).
AG>Насколько вероятна ситуация, что поменяется порядок следования пакетов? То есть — переданный в сеть позже, появится на приёме раньше.
Пакетов — может. Но волновать не должно, если не стоит задача выжать дополнительные проценты скорости (а в таком случае TCP вообще не в тему).
AG>Вышеуказанные данные получены экспериментально. AG>При работе без сети (local-loopback) — просто на 127.0.0.1 — максимальная длина TCP пакета намного больше. Порядка 64 KBytes.
Это loopback, ему можно. Бывает ethernet jumbo frames, до 9000 — но только в локальных сетях.
AG>Но меня интересует вопрос — как сеть дробит пакеты TCP? AG>Как защититься от изменения порядка следования пакетов?