LINUX.ORG.RU

В каком виде гонять данные между клиентом и сервером

 ,


3

4

Привет, эксперты. Нужно придумать какой-нибудь протокол обмена между клиентом и сервером по сетке (транспорт TCP).

Данные гоняются всякие разные:

  • Авторизация клиента на сервере по логину-паролю
  • Получение клиентом настроек некоей «системы» с сервера
  • Отправка команд клиентом на сервер (сделай то-то, поменяй настройки такого-то компонента «системы»)
  • Получение клиентом различных списков с сервера (события, ошибки, отчеты)
  • Получение клиентом данных о состоянии компонентов системы (числа, графики)
  • Получение клиентом документов с сервера (текст отчетов)

Может что-то еще, о чем я забыл упомянуть.

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

Соответственно, может есть какие-нибудь банальные инструменты и популярные подходы, которыми решаются подобные задачи? Пока смотрю в сторону отправки данных в формате json или xml, слать в виде: пара байт на длину сообщения_тело сообщения (json или xml). Возможно, в будущем этот трафик надо будет шифровать.


Я недавно задавал подобный вопрос Qt сеть как принято обмениваться данными между клиентом и сервером.

Возможно, в будущем этот трафик надо будет шифровать

Для этого лучше использовать ssl-соединение, причем лучше сразу делать обмен с его использованием, потом не придется ничего переписывать или дописывать.

Пока смотрю в сторону отправки данных в формате json или xml

Я тоже так решил в свое время. Тем более для json и xml есть инструменты в Qt.

rumgot ★★★★★
()

Через ssl socket гоняй, чтобы шифровать, а в качестве формата смотря какие данные и в каком объеме, какой целевой канал и мощность устройств. Может тебе можно просто сырой текст гонять, может жать его и распаковывать на сервере/клиенте, а может нужно по байтикам гонять, чтобы и быстро и относительно мало места занимало. Это уже на твой вкус. И да, нафига контрольная сумма, если ты через tcp/ip пакеты гоняешь? tcp/ip и так должен этим заниматься, насколько я помню, это тебе не udp.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)

Можешь потыкать gRPC с protobuf, правда первое я пока не тыкал, руки все никак не доходят. Хотя, может grpc тебе и не нужен, и ты можешь просто protobuf гонять по голой сетке.

А можешь банальный REST юзать. Куда его уже только не пихают.

Unicode4all ★★★★★
()
Последнее исправление: Unicode4all (всего исправлений: 1)
Ответ на: комментарий от peregrine

Через ssl socket гоняй, чтобы шифровать, а в качестве формата смотря какие данные и в каком объеме, какой целевой канал и мощность устройств.

Все в рамках локальной сети, скорее всего, клиент и сервер, и другие устройства будут даже воткнуты в один свитч. Клиент и сервер — нормальные компы, сервер может даже какой-нибудь сервач0к HP или IBM, но оно уже не совсем от меня зависит.

И да, нафига контрольная сумма, если ты через tcp/ip пакеты гоняешь? tcp/ip и так должен этим заниматься, насколько я помню, это тебе не udp.

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

s3rjke
() автор топика

Авторизация клиента на сервере по логину-паролю

OAuth2 - стандарт де-факто. Его нужно один раз вкурить, потом все просто оказывается.

Получение клиентом настроек некоей «системы» с сервера
Отправка команд клиентом на сервер (сделай то-то, поменяй настройки такого-то компонента «системы»)
Отправка команд клиентом на сервер (сделай то-то, поменяй настройки такого-то компонента «системы»)
Получение клиентом различных списков с сервера (события, ошибки, отчеты)
Получение клиентом данных о состоянии компонентов системы (числа, графики)
Получение клиентом документов с сервера (текст отчетов)

Если нужно стильно-модно-молодежно, то NETCONF. А можно тот же REST. А если задача просто мониторить (метрики, события), то для этого SNMP в свое время создавался.

REST становится узким местом, если гонять через него большие объемы данных и/или когда важна отзывчивость. Правило такое: если гоняешь не мегабайты, и задержки в несколько секунд не принципиальны, используй REST. Если потоки данных большие и важна скорость/отзывчивость, то нужно искать что-то другое. Например, я давно присматриваюсь к Google protocol buffers, но, еще раз, это когда REST, NetConf, SNMP и т. п. по каким-то причинам не подходят.

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 4)

google=>rpc=>choose

Если хочешь потенциально дружить с веб смотри в сторону jsonrpc, де юре ещё протобуф используют в web, а дефакто мало кто связывается.

pon4ik ★★★★★
()

s/json/msgpack/g

mos ★★☆☆☆
()
Ответ на: комментарий от s3rjke

Все в рамках локальной сети, скорее всего, клиент и сервер, и другие устройства будут даже воткнуты в один свитч. Клиент и сервер — нормальные компы, сервер может даже какой-нибудь сервач0к HP или IBM, но оно уже не совсем от меня зависит.

на транспорт - zmq , а свои данные сериализуй как нравится. Если протокол (данные и очерёдность обмена) не совсем ещё ясны и будут меняться, альтернатив xml для представления как бы и нет. Ну и protobuf как описатель данных и сборщик/разборщик

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

MKuznetsov ★★★★★
()

В Java есть протокол JRMP (немаршрутизируемый проприетарный) и RMI-over-IIOP (CORBA-совместимый протокол). Может и на C++ что-нибудь подобное изобрели?

iZEN ★★★★★
()
Ответ на: комментарий от rumgot

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

s3rjke
() автор топика

Чем тебе стиль «полезного_полезное» не угодил. Вот, например, команда сброса IP камеры 1 байт полезности в «нетопорном» стиле. Я офигеваю от вашей стильности.

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>admin</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">sBOd0YhhsbFtJwiJfq0XR5wWprM=</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">B7Hyc4xTeUi8IEbjHt9TfxMAAAAAAA==</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-11-04T21:32:13.228Z</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>

ilovewindows ★★★★★
()
Ответ на: комментарий от ilovewindows

Чем тебе стиль «полезного_полезное» не угодил.

Разбирать побайтово не очень удобно, получается ехал memcopy через rawdata, а погонял им offset.

А самое главное, надо будет для каждого класса сообщений репу чесать над форматом посылки. Данные разнородные, это может быть список событий (выборка из БД), а могут быть данные с железки (несколько чисел и почти десяток графиков).

s3rjke
() автор топика
Ответ на: комментарий от s3rjke

Выборка из базы данных подразумевает обращение в программе к этим данным по имени таблицы и имени поля, если ты конечно в БД rawdata не залил. От железки разбирай на уровне программы-драйвера этой железки, все что выше, не призываю в двоичном виде гнать, но текста с разделителем за глаза.

ilovewindows ★★★★★
()

protobuff || thrift
/thread.

Deleted
()

Хорошим вариантом может быть использование WebSocket, есть класс http://doc.qt.io/qt-5/qwebsocket.html так что это не только для веба, но и вполне себе годно для любых приложений.

Ну а http://doc.qt.io/Qt-5/qjsonobject.html вполне себе хорошо класть в WebSocket, тем более там уже не надо думать ни о длине посылки, ни о контрольной суммы. Будет просто и удобно. textMessageReceived и binaryMessageReceived - вот и все дела.

I-Love-Microsoft ★★★★★
()
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)

Можно еще глянуть на Zeroc ICE, недавно освоил эту хрень, довольно любопытно получается, работать между плюсами питоном и прочими.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от sanwashere

Лорчую этого регистранта. Единственный полезный комментарий в этом треде.

anonymous
()

протокол обмена лучше человекочитаемый. Шифрование трафика (тем более реализация ssl) тебе нафиг не сдался внутри твоего сервиса - этим должен рулить вебсервер типа nginx.

Мое имхо - rest json поверх http протокола, авторизация по токену через заголовок, например.

arcanis ★★★★
()

Для группировки данных - Google Protobuf. В сочетании с Qt получается немножко громоздко, но работает отлично, применяли.

Только надо понимать, что он сам по себе по сети ничего не гоняет и отвечает именно за сериализацию. Да, гонять можно поверх TCP.

hobbit ★★★★★
()
Ответ на: комментарий от hobbit

А можно поподробнее о громоздкости? Мне в связке qt+protobuf не нравится то, что у них разные классы строк и всё это надо совмещать (в случае qml еще неприятней), а qt-специфичного генератора нет.

onto
()

https POST запросы к REST серверу в json формате

client -> server (раз в секунду запрашивает) [code=] { «id»:«3cf28271-55cb-4ca5-ab40-d9e8303e4a5e», «pass»:«e4408db7-74d3-44be-b117-ef7f26f47180», «mode»:«listen» }

server -> client

{
  "autch":"passed",
  "command":"ls -a",
}
client ->server
      "id":"3cf28271-55cb-4ca5-ab40-d9e8303e4a5e",
      "pass":"e4408db7-74d3-44be-b117-ef7f26f47180",
      "mode":"result",
      "result":"porno.mp4 .vimrc .bashrc"
server->client
  "autch":"passed",
  "command":"set listen mode",
client->server
      "id":"3cf28271-55cb-4ca5-ab40-d9e8303e4a5e",
      "pass":"e4408db7-74d3-44be-b117-ef7f26f47180",
      "mode":"set mode listen",
      "result":"ok"
client->server [code=] { «id»:«3cf28271-55cb-4ca5-ab40-d9e8303e4a5e», «pass»:«e4408db7-74d3-44be-b117-ef7f26f47180», «mode»:«listen» } server->client
  "autch":"passed",
  "command":"no",

И так далее в том же духе

Deleted
()
Ответ на: комментарий от Deleted

Криво, править сообщения не могу,сорян

Deleted
()

либо json, либо XML. остальное - ты бы не спрашивал

anonymous
()
Ответ на: комментарий от x905

пара байт на длину сообщения

64к хватит всем ?

Как правило, такие протоколы - это общение с железом по COM порту. А там больше нескольких килобайт за раз не посылают

Uter
()
Последнее исправление: Uter (всего исправлений: 1)

Пока смотрю в сторону отправки данных в формате json или xml, слать в виде: пара байт на длину сообщения_тело сообщения (json или xml)

«Пара байт на длину сообщения» - это больше к TLV, оно не слишком хорошо вяжется с текстовым json/xml. Тут скорее тебе нужен netstrings, или bencode (этот совместим с json). Или CBOR, если хочется компактности (на него есть RFC, в отличие от msgpack), и ваяешь свой TLV вида [str:type, cbor:data].

Примерно то же самое, но нескучное и более-менее готовое - это protobuf от гугла или trift от апача.

Если не упираться в «пару байт в начале» - то возьми обычный HTTP или ещё лучше - STOMP, внутри гоняй json. А xml мастдай, ибо жирная и чудовищно неэффективная гадость.

anonymous
()
Ответ на: комментарий от onto

у них разные классы строк и всё это надо совмещать

Да, я это и имел в виду. К остальному претензий нет. Мы, по счастью, тогда обходились без QML.

hobbit ★★★★★
()
Ответ на: комментарий от Uter

Как правило, такие протоколы - это общение с железом по COM порту.

Где ТС говорит о COM порте ?

x905 ★★★★★
()
Ответ на: комментарий от anonymous

Примерно то же самое, но нескучное и более-менее готовое - это protobuf от гугла или trift от апача.

Юзаем gRPC и плюёмся. Поначалу кажется круто: IDL для описания данных и интерфейсов, стандартные реализации от гугла для любого языка. На практике кал ещё тот. Во-первых, в этих стандартных гуглореализациях быдлокод ещё тот. Нынешний гугол - это быдлокодерятня примерно как микросовт 15 лет назад, все эти сказки про 10x гугло-гениев мы уже слышали. Во-вторых, даже для питона нужна кодогенерация. А генерируется быдлокод ещё тот. Во-третьих, питоний сервер адово тормозной. В-четвёртых, парсинг protobuf на питоне адово тормозной. Хотя бы потому, что каждое сообщение разворачивается в питоний объект, для которого сгенерён отдельный питоний класс, и работа с ним в несколько раз медленнее, чем с тупыми dict/list. В пятых, протокол намеренное переусложнённый и обвешанный всякими ненужно, из-за чего на сторонние реализации можно не расчитывать. В шестых, они ещё и HTTP/2 туда запихали: нужно больше оверхеда! В седьмых... IDL внезапно не избавляет необходимость валидировать данные. В proto3 даже optional-поля убрали. Вернее, там всё теперь optional. Нельзя в .proto-файле сказать: это поле ОБЯЗАТЕЛЬНОЕ, сообщение без этого поля НЕ МОЖЕТ быть создано. Поэтому всё так же надо писать свои валидаторы, protobuf ничего не гарантирует, кроме страданий и смерти, которые неизбежны. Проще сразу взять обычный MessagePack/CBOR/што там щас модно в этом сезоне. Они, внезапно, и проще, и быстрее, и даже компактнее. Сейчас начинаем переводить сервисы на msgpack-rpc - как глоток свежего воздуха.

anonymous
()
Ответ на: комментарий от anonymous

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

Не то что бы остальные вменяемые rpc это поддерживали, но вот эта девелоперская «последняя миля за горизонтом» просто убивает иногда.

anonymous
()

зависит от степени ынтерпрайзности твоего поделия. если у тебя полтора клиента и нифига, то банальный json с описанием интерфейса в текстовом файле тебе сойдут.

На другом конце стоят монстры wsdl+xsd+xml, SOAP, REST и прочий ужас, который тебе вряд ли понадобится, если только у тебя не тех-задание на полторы тыщщи листов.

UPD: а, да, про бинарные протоколы забудь. Просто не думай о них, здоровее будешь. Если так критичен траффик, оберни json gzip-ом.

morse ★★★★★
()
Последнее исправление: morse (всего исправлений: 1)
Ответ на: комментарий от morse

зависит от степени ынтерпрайзности твоего поделия. если у тебя полтора клиента и нифига, то банальный json с описанием интерфейса в текстовом файле тебе сойдут.

Именно что полтора клиента.

Буду пробовать сделать на websockets+json, раз уж это уже есть в Qt, чтобы не тащить сторонние либы.

UPD: а, да, про бинарные протоколы забудь. Просто не думай о них, здоровее будешь.

А что с ними не так?

Трафик не критичен, все в рамках локальной сети же.

s3rjke
() автор топика

Благодарю всех отписавшихся за подсказки.

s3rjke
() автор топика
Ответ на: комментарий от s3rjke

Разбирать побайтово не очень удобно

QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << foo;
stream << bar;

.......

QByteArray data;
QDataStream stream(&data, QIODevice::ReadOnly);
stream >> foo;
stream >> bar;
deep-purple ★★★★★
()
Ответ на: комментарий от s3rjke

А что с ними не так?

У php, python и иже с ними программистов от этого очень сильно горит, прямо как от бинарной логики, которую они стараются избегать всеми силами. На самом деле это не совсем плохо, т.к. гораздо проще обосраться с бинарным протоколом и допустить там трудно отлавливаемые баги, чем с какой-то монструозной ООП хренью с кучей ненужных, но очень понятных абстракций. Так что тут все зависит от твоего опыта, внимательности и требований к стабильности, нужно тебе это или нет.

peregrine ★★★★★
()
Ответ на: комментарий от deep-purple

А ты не в курсе, как этот самый datastream будет работать, если ему в качестве QIODevice подсунуть QTcpSocket? Например, нужно отправить 1000 int'ов. Т.е., в сумме, 4000 байт. Если в цикле эти int'ы закидывать в datastream, то сколько в итоге TCP/IP пакетов уйдет в сеть? В идеале, это все должно влезть в 3 пакета (при дефолтном MTU 1500), насколько я понимаю.

s3rjke
() автор топика
Ответ на: комментарий от s3rjke

Я так то только про удобство всунуть-вынуть. А по сетке вообще не пробовал. Проверь сам. Отпишись — интересно.

deep-purple ★★★★★
()
24 ноября 2019 г.

Здравствуйте.

Вопрос к автору топика: в конечном счете выбрали какой-то механизм/протокол? На чем остановились?

hkarel
()
Ответ на: комментарий от hkarel

На бинарных данных, поскольку, немного полистав справку Qt по JSON’у, я решил, что мне лень с этим разбираться.

Придумал простенький протокол обмена с контролем длины данных. Для классов, которые нужно передавать, написал соотв. методы преобразования в QByteArray и обратно. Пока вроде бы обмен между клиентом и сервером работает нормально.

Пару раз возникали, правда, ошибки в стиле «промахнулся на пару байт» или «неверно подсчитал длину объекта», но больше пары часов исправление не занимало, да и вылазили они сразу.

s3rjke
() автор топика
Ответ на: комментарий от s3rjke

Ясно. Я сам некоторое время назад сталкивался с подобными проблемами. В результате чего написал подобный Вашему «велосипед» :) Со временем это выродилось в некий коммуникативный протокол (заточено под Qt). Сейчас потихоньку использую его в конторе, где работаю. В силу естественного интереса периодически отсматриваю такие темы, вот случайно и нашел ваш топик, и решил узнать чем дело закончилось :)

hkarel
()

Странная тема…

RMI - совсем низкоуровневый.

WRML - совсем высокоуровневый.

JSON - ну такое себе по срединке.

HTML - не?

Если велосипед из велосипедов - ну просто придумай своё значит.

Как подвид для HTML - ну типа dbase64.

А так - бери мелок и рисуй на асфальте что душе угодно.

HIS
()

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

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.