maaGames
@maaGames
Погроммирую программы

Как передавать много сообщений через Socket?

Использую библиотеку Poco ( pocoproject.org )

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

Для отправки/получения использую SocketOutputStream/SocketInputStream и записываю/читаю данные функциями write/read. Чтобы не пытаться одновременно писать в сокет из разных потоков, создание SocketOutputStream окружено Poco:Mutex. (так же пробовал omp critical для эксперимента)

Если сообщения отсылаются редко (например, пошагово в отладчике), то всё работает правильно, независимо от размера передаваемых данных. В реальной же программе сообщения отсылаются очень часто и что-то портится. В начале каждого блока данных я записываю "магическое число", чтобы убедиться, что это правильные данные. И часто получается, что данные в SocketInputStream оказываются неправильные. Для каждого сообщения создаётся новый объект SocketOutputStream, по идее, при удалении объекта делается flush и данные должны отсылаться сразу же.

Выглядит всё так, что к данным первого блока дописана часть данных второго блока. Например, было два объекта по 10 байт, пришло 15 байт, первый объект считал свои 10 байт и "хвост" удалился. И при следующем срабатывании poll() считались оставшиеся 5 байт и в их начале уже нет "магического числа" и всё пропало... Это кажется очевидным, но ведь я специально использую классы SocketOutputStream, чтобы избавиться от ручного управления буферами, хвоставми и т.д. и т.п.

Собственно, вопрос:
Как в Poco правильно передавать и получать большие объёмы разноразмерных данных (сонтимассивов в секунду, размером в десятки байт и (редко) штучные массивы размером сотни мегабайт)? Которые могут отсылаться асинхронно из нескольких потоков.
  • Вопрос задан
  • 1078 просмотров
Решения вопроса 1
tsarevfs
@tsarevfs Куратор тега C++
C++ developer
Без кода сказать можно мало что. Если есть возможность залить на гитхаб, то шансов стало бы больше. Есть 3 подозрительных момента:
Для каждого сообщения создаётся новый объект SocketOutputStream

А нужно ли это? Почему бы не использовать этот stream все время пока живет сокет?
создание SocketOutputStream окружено Poco:Mutex

Попадает ли непосредственно запись в стрим и flush(деструктор) под этот mutex?
использую классы SocketOutputStream, чтобы избавиться от ручного управления буферами, хвоставми и т.д. и т.п.

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

По отладке сетевого кода могу посоветовать логирование вместо отладчика.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@res2001
Developer, ex-admin
Для передачи вы видимо используете TCP. В ТСР нет разделения на сообщения, все данные передаются в одном потоке в том порядке, в котором были отправлены. Т.е. вполне реальна ситуация, когда вы посылаете например 2 сообщения по 10 байт каждое, а читает 3 раза по 5, 10, 5 байт за раз. Таким образом разделять на сообщения вы должны на принимающей стороне самостоятельно.
РОСО тут не причем - эменно так работает протокол ТРС. Вместо TCP можете использовать UDP - тут будете принимать именно сообщениями. Но есть сложность с дефрагментацией - если размер сообщения больше MTU, то сообщение будет фрагментировано при передаче и не факт, что куски дойдут и соберуться. Ну и еще UDP не гарантирует доставку, т.е. сообщения могут теряться и ни получатель ни отправитель об этом не узнает.
Так же можно использовать протокол SCTP, он так же как UDP работает с сообщениями, но при этом гарантирует доставку. SCTP входит в стандартный стек TCP/IP. Есть ли его поддержка в POCO я не в курсе.
Ответ написан
Комментировать
mayton2019
@mayton2019
Bigdata Engineer
Автор пытается построить свой прикладной протокол поверх сокетов. Этого не надо делать т.к такие протоколы уже созданы. Ключевые слова: jms, mq, apache-mq, kafka, rabbitmq, ibmmq.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Похожие вопросы