Здравствуйте, AlexGin, Вы писали:
Pzz>>Не надо при проектировании framing'а полагаться на то, что фреймы эти будут использоваться строго в режиме вопрос-ответ. Ничего особо не выигрываешь, а лишь смешиваешь два логически не связанных уровня. О чем потом и пожалеешь. AG>Ну OK — чтобы заполнить канал передачи, помимо диалога Server-Client будем передавать фильмы. AG>Или торрент-сеть организуем AG>Вот не знаю — что выбрать?
Не надо заполнять посторонними данными. Но и в прямом применении может оказаться необходимо:
1. Посылать несколько запросов от клиента сразу, чтобы, пока они ползут по сети, сервер мог их отрабатывать. Обычно это называется pipelining.
2. Делать, что сервер отвечает на один запрос несколькими ответами — например, сначала "принял, исполняю", потом "прогресс 50%, полёт нормальный", потом "завершил, вот финальные результаты", с интервалом в минуты между ответами. В таком случае нужно ожидать ответов сервера в произвольные моменты и давать сообщениям теги, связывая ответы с запросами.
3. Передавать внеочередные нотификации с каждой стороны о каких-то важных изменениях или о том, на что другая подписалась.
4. Уравнять роли клиента и сервера — два равноправных участника что-то делают и могут начать новые действия в любой момент.
Если что-то из этого может происходить — потребуется или менять протокол, или заранее подготовить его к такому расширению.
Pzz>>Я даже не о пропускной способности канала сейчас говорю, а о логической целостности твоего протокола. AG>Мой протокол — это "разговор" двух человек аппаратно-программных сущностей: сервер и клиент... AG>Что предлагается как альтернатива диалогу — передача "пустого" space-holder-а, пока требуемого контента не насобиралось?
Здравствуйте, AlexGin, Вы писали:
AG>Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>>Ну, она CRC32. В массштабах 4-гигабайтного кино вероятность пропустить одиночную ошибку больше половины. С другой стороны, кто его качает, кино, по голому TCP? AG> AG>А я то думал, что для передачи "4-гигабайтного кино" более подходит UDP протокол.
В любой физически реальной среде передачи действует принцип, аналогичный известному лозунгу: "Мы делаем быстро, качественно и дёшево — выберите любые два". (Это действует как для сетей с коммутацией пакетов, так и для сетей с коммутацией каналов, хотя мы здесь обсуждаем только TCP/IP — это коммутация пакетов.) Аналогами этих вариантов являются соответственно:
— скорость и оперативность передачи данных
— надёжность передачи данных (защита от потерь, искажений)
— экономность передачи (требовать ресурсы, сравнимые с затратой в идеальных условиях)
Три комбинации двух вариантов из трёх дают три стиля трафика:
1. Наливной (англ. bulk). Надёжность и экономность передачи достигаются за счёт снижения скорости при проблемах передачи: неподтверждённые данные пересылаются до достижения результата или определения невозможности связи. Яркие примеры применения — скачивание фильма, доступ к базе данных для выписки товара. Именно такое обычно делается поверх TCP.
2. Синхронный. Скорость и экономность важнее, допускается определённая потеря данных при передаче (которая для большинства целей применения такого типа трафика лучше, чем задержка
вследствие попыток перепосылки). Яркие примеры применения — voice-over-IP, потоковое видео... Яркий пример реализации — RTP.
Вот тут как раз место для UDP.
3. Управляющий. Важнее достучаться вовремя до получателя и получить подтверждение об этом, чем экономия ресурсов (хотя практически, безусловно, реализации не стараются поглощать всё — и дизайн протокола должен не допускать неконтролируемый рост объёмов и скоростей потоков данных). Примеры реализации — STP, OSPF, сигнальный уровень SIP.
Итого: смотрите фильм вживую — UDP (ну, для отдельных героев — SCTP с ограниченной доставкой). Скачиваете — TCP. Смешивать подходы нежелательно, у них совсем разные задачи.
Здравствуйте, уважаемый Marty, Вы писали:
... M>И какая тут принципиальная разница?
...
Блок в 10 байт — войдёт всегда и фактически без доп-проверок.
Чего совсем не сказать о блоке в 10 килобайт.
P.S. Проблему удалось решить — за счет контроля длины блоков.
Предыдущее решение — за счёт маркёра конца — не такое изящное и (по совету уважаемого товарища Pzz) я отказался от него.
Здравствуйте, AlexGin, Вы писали:
AG>Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>>Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
AG>Ты не поверишь, уважаемый Pzz, но в перспективе я бы хотел и от этого защититься. Ну а пока — вроде как не актуально.
Если используется свой протокол прикладного уровня — добавь в него контрольную сумму.
Ранее (ещё вчера) было так:
AG>Этот массив — может представлять собой целый блок (ну то-есть пользовательский_пакет) или же только его фрагмент. AG>Я же проверяю не все байты, а только четыре_последних_байта на маркер конца: AG>- если его нет, то помещаю этот массив в "накопитель" и жду недостающие фрагменты; AG>- если он есть — считаем что блок данных успешно принят — его можно применять.
Теперь:
Передаю четыре байта длины блока — в первых четырех октетах, при приёме нового блока — анализирую совпадает ли общая длина,
с объявленной в первой четвёрке байт.
— если совпадает, то считаем что блок принят и проводим его дальнейшую обработку/визуализацию;
— если не совпадает, то продолжаем заполнять "накопитель" (пока общая длина накопленного не совпадёт с объявленной в начале блока).
Прикладной протокол в моей системе выглядит так:
Это пример передачи — от клиента к серверу (в обратном направлении — всё то же самое, только клиент и сервер меняются местами):
-------------------------------------------------------------------------------------------------------------------
CLIENT's side | SERVER's side | Comments
-------------------------------------------------------------------------------------------------------------------
Marker_1 | | to Server: On the Client exist packet (message) to the Server
| Marker_2 | to Client: Server is Ready to recive packet (message) from the Client
{User's-Packet} | | to Server: transmitting of User's-Packet from Client to the Server
| Marker_3 | to Client: Success — packet (messege) correctly received and processed
-------------------------------------------------------------------------------------------------------------------
Marker_1,_2,_3 — это простые 10-ти байтные посылки (здесь проблем нет), а вот {User's-Packet} — ну точнее "блок_данных" — большой.
Были случаи, что около 30 килобайт (на JSON-варианте), теперь — с сериализацией через QDataStream — удалось выйти на размер менее 10 килобайт.
Здравствуйте, AlexGin, Вы писали:
AG>1) Система работает в специальной сети (не общего пользования), так что шутники — курят в сторонке. AG>2) Под длину блока — выделяется четыре первых байта принятого массива — какой тебе кусок на менее, чем четыре байта
Не парься о фрейминге, решай задачу прикладного уровня. Возьми какую-нибудь либу вроде protobuf (или никсмановский yas), накрути её поверх своей сетевой инфрастурктуры QT-шной и забудь про фрейминги. Либо поищи в QT что-то своё, я думаю у них есть под это дело что-то.
Здравствуйте, AlexGin, Вы писали:
AG>Прикладной протокол в моей системе выглядит так: AG>Это пример передачи — от клиента к серверу (в обратном направлении — всё то же самое, только клиент и сервер меняются местами):
А зачем сначала анонсировать наличие блока, а лишь потом его посылать? Что добавляет предварительная договоренность, кроме сложности?
AG>Были случаи, что около 30 килобайт (на JSON-варианте), теперь — с сериализацией через QDataStream — удалось выйти на размер менее 10 килобайт.
А у этого QDataStream унутренность документирована? Не получится ли так, что теперь вы к Qt намертво привязаны в т.ч. форматом данных?
Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>А зачем сначала анонсировать наличие блока, а лишь потом его посылать? Что добавляет предварительная договоренность, кроме сложности?
Передающая сторона имеет уверенность, что на приемной стороне готовы принять блок данных.
Добавляет сложности — есть такое немного
Добавляет уверенности, что в случае большой загрузки CPU приёмной стороны, передающая "повременит" с отправкой основного пакета,
дожидаясь, пока на приёмной стороне не закончиться выполнение предшествующей задачи и не освободятся вычислительные мощности.
AG>>Были случаи, что около 30 килобайт (на JSON-варианте), теперь — с сериализацией через QDataStream — удалось выйти на размер менее 10 килобайт.
Pzz>А у этого QDataStream унутренность документирована? Не получится ли так, что теперь вы к Qt намертво привязаны в т.ч. форматом данных?
1) Исходники всего Qt имеются и разобраться с ними (для специалиста, знакомого с C++) не составит большого труда.
2) Да, есть тот фактор, что я привязаываю проект к фреймворку Qt. К наиболее богатому и продвинутому из имеющихся на сегодня C++ фреймворков.
Этот момент также даёт дополнительные бонусы:
— мне и администрации нашей конторы легче найти новых девелоперов на проект. Т.к. C++ и Qt популярны в условиях минского рынка IT;
— при разработке на Qt легче найти ответы на вопросы (чем на более экзотических новомодных языках/технологиях);
— в едином стиле можно разрабатывать весь комплекс ПО: клиент, сервер, Data-base layer и т.д.
) строятся велосипеды, вместо использования готовых блоков.
Причем с помощью очередей решаются все задачи автора — и распределение задач между серверами, и уведомление клиентов, и надежность передачи между узлами.
Еше не начали разрабатывать сами клиенты и серверы, но зачем-то углубились в транспорт, хотя он, похоже, не является узким местом.
Вот я и спрашиваю, зачем протокол, зачем писать свой брокер сообщений? Чем не устраивают имеющиеся варианты?
Здравствуйте, AlexGin, Вы писали:
Pzz>>А зачем сначала анонсировать наличие блока, а лишь потом его посылать? Что добавляет предварительная договоренность, кроме сложности?
AG>Передающая сторона имеет уверенность, что на приемной стороне готовы принять блок данных. AG>Добавляет сложности — есть такое немного
Выкинь ты это нахрен, оно не нужно. И добавь лучше периодические "пинги" в состоянии простоя — TCP keepalive с этим не очень хорошо справляется.
AG>2) Да, есть тот фактор, что я привязаываю проект к фреймворку Qt. К наиболее богатому и продвинутому из имеющихся на сегодня C++ фреймворков.
Ты понимаешь разницу между СПЕЦИФИКАЦИЕЙ протокола, и его РЕАЛИЗАЦИЕЙ? Нет ничего плохого в том, чтобы привязать реализацию к какому-либо фреймворку. Все равно, на чем-то реализовывать надо. Но вот спецификацию лучше бы сохранить нейтральной по отношению к языкам и фреймворкам.
) строятся велосипеды, вместо использования готовых блоков. Б>Причем с помощью очередей решаются все задачи автора — и распределение задач между серверами, и уведомление клиентов, и надежность передачи между узлами.
Тут автору надо объявить тендер. Уже прозвучало предложение сделать все по HTTP, по HTTP/2 и через вебсокеты. Ты предлагаешь очереди и брокер сообщений. Думаю, автору стоит озаботиться правильным выбором
P.S. Лично я бы выбирал между HTTP/1.1, если нет и не появится необходимости что-либо получать с сервера по инициативе сервера, и самодельным протоколом, если такая необходимость есть хотя бы в перспективе.
Здравствуйте, уважаемый Pzz, Вы писали:
AG>>Передающая сторона имеет уверенность, что на приемной стороне готовы принять блок данных. AG>>Добавляет сложности — есть такое немного
Pzz>Выкинь ты это нахрен, оно не нужно. И добавь лучше периодические "пинги" в состоянии простоя — TCP keepalive с этим не очень хорошо справляется.
Выкидывать — не надо, это годная фича. Я уже сообщал, для чего она предназначена.
Добавить ping-и: на перспективу неплохо, я и сам думал об этом.
Но на данном этапе — это как раз выглядит усложнением. Предполагаю, что с этим можно и подождать.
AG>>2) Да, есть тот фактор, что я привязаываю проект к фреймворку Qt. К наиболее богатому и продвинутому из имеющихся на сегодня C++ фреймворков.
Pzz>Ты понимаешь разницу между СПЕЦИФИКАЦИЕЙ протокола, и его РЕАЛИЗАЦИЕЙ? Нет ничего плохого в том, чтобы привязать реализацию к какому-либо фреймворку. Все равно, на чем-то реализовывать надо. Но вот спецификацию лучше бы сохранить нейтральной по отношению к языкам и фреймворкам.
Так спецификация — она и есть нейтральна.
Хочешь — делай на POSIX, хочешь — на STL/boost, хочешь — на Python.
Лично мне нравится для этого применять именно Qt.
P.S. Данную тему можно считать исчерпанной. Вам, уважаемый Pzz, большая благодарность за участие и подсказки.
На данный момент — я ушёл от выявления маркера конца и работаю по длине блока, передаваемой в начале блока.
Здравствуйте, AlexGin, Вы писали:
AG>Здравствуйте, уважаемый Pzz, Вы писали:
Pzz>>Ну строго говоря, у TCP очень паршивая контрольная сумма. Если гнать большие объемы данных по шумному каналу, данные иногда будут приходить попорченными. Но ТС вряд ли именно от этого защищается.
AG>Ты не поверишь, уважаемый Pzz, но в перспективе я бы хотел и от этого защититься.
HMAC — контроль приолжением целостности данных как для зашумления, так и для злонамеренного искажения данных. А если сверху TLS как средство транспортного шифрования навернуть (как HTTPS делает) — так вообще красота.
Здравствуйте, netch80, Вы писали:
Pzz>>Я где-то читал, что эти needfrag не всегда доходят и не всегда посылаются, поэтому с ними бывают всякие разные проблемы. Не помню, включен ли MTU path discovery в линухе по умолчанию, а проверять лень...
N>Включен. Не всегда посылаются — да, бывает, но последние лет 10 я про такое уже не слышал. Может, даже самые тупые и ленивые наконец починились...
Если гейт посередине закрывает/зацикливает ICMP (например, для защиты от флуд-атак или как одно из мероприятий по усложнению сканирования структуры сети), то будет резать весь ICMP даже если стороны посылают друг другу fragmentation-инструкции.