Каков наилучший способ реализации кроссплатформенного, многопоточного сервера на C / C ++? - PullRequest
10 голосов
/ 20 декабря 2008

Часть команды разработчиков, с которой я работаю, получила задачу написать сервер для интеграции с нашим продуктом. У нас есть несколько низкоуровневых сенсорных устройств, которые предоставляют C SDK, и мы хотим поделиться ими по сети для использования людьми, собирающими данные. Звучит просто, правда? Кто-то подключит сенсорное устройство к своей машине в одной части здания и запустит наш сервер, тем самым предоставив доступ к устройствам в остальной части сети. Затем клиент подключится к этому серверу через наше приложение и получит показания датчика с устройства.

Я создал простой, независимый от языка сетевой протокол и эталонную реализацию на Java. Проблема заключается в создании реализации, которая будет работать с нашими устройствами, которые предоставляют только SDK, написанный на C. Мы думали сделать следующее:

  1. Создание потоков опроса, которые собирают и хранят самые последние показания с каждого подключенного устройства.
  2. Используйте многопоточный сервер, чтобы раскрутить каждое входящее соединение с рабочим потоком.
  3. Когда рабочий поток получает запрос на считывание датчика, самое последнее значение, собранное потоком опроса, отправляется обратно клиенту.

Это много потоков, особенно в C. Итак, для обзора, общие требования:

  • Работает на компьютерах с Windows XP / Vista, Linux и OS X
  • Написано на C или C ++, для взаимодействия с C SDK у нас есть
  • Принимает переменное количество одновременных подключений (рабочих потоков)
  • Должен использовать потоки, не разветвляться (не хочу иметь дело с другим уровнем IPC)

Кто-нибудь может предложить библиотеку и, желательно, пример кода, чтобы начать использовать?

Ответы [ 11 ]

13 голосов
/ 20 декабря 2008

Я использовал Boost.Thread & Boost.Asio для создания многопоточного сервера в системах Windows и Linux. Обучающие программы позволили легко начать работу.

9 голосов
/ 20 декабря 2008

Лучший способ написать такой сервер - это не написать один, а перестроить вашу систему, чтобы она не требовалась, и / или повторно использовать уже существующие компоненты. Потому что:

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

Это также может разделить всю машину с остальной частью сети, если ваш код имеет уязвимость (что, вероятно, так и будет, поскольку вы пишете ее на C ++ с нуля и изобретаете новый протокол).

Итак, сделай это наоборот. Установите простой клиент на компьютере, на котором установлено сенсорное оборудование, затем запускайте его постоянно или периодически и отправляйте (отправляйте) результаты на центральный сервер. Центральный сервер может быть даже стандартным веб-сервером. Или это может быть база данных. (Обратите внимание, что оба они уже написаны - не нужно изобретать велосипед; -)

Тогда ваше приложение работает так же, как вы сейчас себе представляете, однако оно собирает данные из базы данных, а не с датчиков. Однако часть, работающая на машине с датчиком, превратилась из многопоточного пользовательского сервера в кошмар, к хорошему маленькому однопоточному клиенту командной строки, который создает только исходящие соединения и который можно запустить из cron (или эквивалент в windows).

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

edit: ответы ceretullis и pukku предлагают хороший вариант использования многоадресной рассылки - см. этот ответ и комментарии

7 голосов
/ 20 декабря 2008

ACE Дугласа Шмидта (Adaptive Communication Environment) - это зрелая, очень портативная платформа с открытым исходным кодом для создания высокопроизводительных многопоточных серверов. Он в основном предназначен для телекоммуникационных приложений, но был использован для разнообразных проектов . Он также поставляется с брокером объектных запросов с именем TAO (если вы в CORBA )

Одно из требований к известности платформы - это то, что она поддерживает множество моделей потоков (пул потоков, поток на запрос, асинхронные потоки + и т. Д.), Поэтому вы можете использовать управление потоками таким образом, который оптимален для вашего приложения. На самом деле это самая интересная особенность системы - функциональность серверной инфраструктуры выходит из коробки. Большинство других библиотек, которые я видел здесь, по-прежнему требуют, чтобы вы реализовали большую часть этих функций самостоятельно.

Существует немало электронных документов , а также нескольких книг , написанных об этом. Это не самая теплая и пушистая из систем, и она действительно создана для скорости, а не комфорта - но мы используем C ++. Вы, вероятно, обнаружите, что гораздо меньше усилий для того, чтобы разобраться с ACE, чем пытаться восстановить функциональность и отладить всю синхронизацию.

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

4 голосов
/ 20 декабря 2008

Не совсем ответ на ваш вопрос, но, похоже, вы на самом деле лучше знакомы с Java, чем с C / C ++, почему бы не продолжить ссылочную реализацию и подключиться к SDK с помощью Собственный интерфейс Java . (Я никогда не использовал его, но подумал, что это будет полезно именно для таких ситуаций.)

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

4 голосов
/ 20 декабря 2008

Я бы использовал QT. Имеет поддержку кроссплатформенности. Хорошая документация:

Документация по QT Threading

Их механизм обмена сигналами / слотами также беспрепятственно работает между потоками.

3 голосов
/ 20 декабря 2008

Я также хотел бы порекомендовать Spread Toolkit , который (согласно веб-сайту проекта) представляет собой «набор инструментов с открытым исходным кодом, который обеспечивает высокопроизводительную службу обмена сообщениями, устойчивую к сбоям в локальных и глобальные сети ". Я использовал это пару раз в ситуациях, которые звучат очень похоже на вашу. По сути, он дает вам сервер, который frankodwyer предлагает .

Демон Spread (то есть сервер) не является многопоточным, но он действительно быстрый и хорошо масштабируется как минимум до сотен или тысяч клиентов. Кроме того, протокол обеспечивает надежную многоадресную IP-рассылку, которая (в многопользовательской среде) может дать вам (с точки зрения производительности) определенное преимущество перед всем, что реализовано с использованием только двухточечных соединений TCP или UDP. (Но: не пытайтесь реализовать надежную многоадресную IP-рассылку самостоятельно ... по-видимому, проект Spread выпустил ряд кандидатских и магистерских диссертаций в качестве побочного продукта - или это набор инструментов, который является побочным продуктом, в то время как основной акцент всегда было академическое исследование? Я не знаю ...).

Поскольку Spread имеет клиентские API C и Java (плюс Python), это звучит как очень хорошее решение вашей проблемы. У них есть две модели лицензирования; Первая альтернатива близка к BSD. И это, конечно, кроссплатформенность (как для клиента, так и для сервера).

Однако Spread (конечно) не сделает все за вас. Наиболее вероятно, что он не выполняет постоянство (то есть, если ваш клиент не в сети или иным образом не может получать сообщения, Spread не буферизует их, по крайней мере, за очень небольшое количество сообщений). Но, к счастью, на самом деле не так уж сложно поставить собственную реализацию персистентности поверх того, что гарантирует Spread (ну, я даже не знаю, важна ли для вас такая проблема или нет). Во-вторых, Spread ограничивает ваши сообщения до 100 килобайт каждое, но этот предел также довольно легко обойти, просто заставив отправителя нарезать большое сообщение на несколько меньших, а затем объединяя их в получателе.

2 голосов
/ 23 августа 2009

Я использую libevent для мультиплексирования сетевого ввода-вывода. Вы должны рассмотреть это как альтернативу Boost.Asio.

API libevent предоставляет механизм для выполнения функции обратного вызова, когда в дескрипторе файла происходит определенное событие или по истечении времени ожидания. В настоящее время libevent поддерживает / dev / poll, kqueue, порты событий, select, poll и epoll.

1 голос
/ 20 декабря 2008

Я согласен с frankodwyer, перевернуть протокол с модели с вытягиванием на модель с выталкиванием.

Пусть компьютер с подключенным датчиком будет транслировать показания по многоадресной UDP-передаче с частотой 100 Гц (или с тех пор, как это будет сделано для ваших датчиков) всякий раз, когда работает служба совместного использования. Затем напишите клиенты, которые читают многоадресные данные.

В качестве альтернативы вы можете использовать широковещательный UDP вместо многоадресной.

Кстати, именно так поступают GPS, лидар и другие датчики.

0 голосов
/ 20 декабря 2008

Я настоятельно рекомендую рассмотреть шаблон дизайна прототипа.

Я использовал этот шаблон для написания независимого от протокола сервера на C ++, который я использовал для всего, от HTTP Web Services до пользовательских проприетарных двоичных протоколов.

По сути, идея такова:

Сервер заботится о принятии () входящих подключений к определенному порту и создании потоков (или процессов) для обработки этих подключений.

Когда вы пытаетесь построить общий сервер, вы понимаете, что вы не можете действительно читать или записывать какие-либо данные, не делая предположений о протоколе ... Итак, хитрость заключается в использовании шаблона-прототипа.

Создайте класс «ConnectionHandlerBase» с помощью чистого метода «HandleConnection () = 0». Сделайте пользователей серверного класса подклассами этого класса с их собственной реализацией. Кроме того, этот класс реализует метод "Clone ()", который возвращает свою копию ... Таким образом, сервер может создавать его новые экземпляры без необходимости знать его тип ... Затем, когда вы получаете соединение, вызываете "Clone" () "в вашем прототипном экземпляре и вызовите поток обработки" HandleConnection () "для этого объекта.

При запуске приложения пользователь класса сервера должен вызвать что-то вроде этого:

"Server.AttachConnectionPrototype (& MyConnectionObject);"

0 голосов
/ 20 декабря 2008

Если вы хотите использовать C (а не C ++), библиотека NSPR может предоставить то, что вам нужно ...

...