Вопрос о модели программирования сокетов сервера

За последние пару месяцев я работал над некоторыми реализациями сокетов на С++ и Java. Я написал небольшой сервер на Java, который обрабатывал бы и обрабатывал входные данные из флэш-приложения, размещенного на веб-сайте, и мне удалось успешно написать сервер, который обрабатывает входные данные от двухмерного игрового клиента с несколькими игроками на С++. Я использовал TCP в одном проекте, а UDP - в другом. Теперь у меня есть некоторые вопросы, которые я действительно не мог найти в сети, и я надеюсь, что некоторые из экспертов могут мне помочь.:)

Скажем, я хотел бы построить сервер на С++, который будет обрабатывать входные данные из тысяч автономных и/или веб-приложений, как мне тогда спроектировать мой сервер? До сих пор я обычно создаю новый и уникальный поток для каждого пользователя, который подключается, но я сомневаюсь, что это путь.

Также, как определить расположение пакетов, отправленных по сети; данные обычно отправляются по сети в двоичном или текстовом состоянии? Как вы обрабатываете сериализованные объекты при отправке данных на разные носители (например, сервер С++ для Flash-приложения)?

И, наконец, есть ли простая в использовании библиотека, которая обычно используется, которая поддерживает переносимость (например, разработка на машине Windows и развертывание в ящике Linux), кроме boost asio.

Спасибо.

Ответ 1

Похоже, у вас здесь пара вопросов. Я сделаю все возможное, чтобы ответить на то, что я вижу.

1. Как мне обрабатывать потоки на моем сетевом сервере?

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

Если вы действительно хотите сделать что-то правильно, у вас может быть конфигурируемый/динамический пул потоков, который будет перерабатывать рабочие потоки по мере их освобождения. Таким образом, вы можете установить максимальный размер пула потоков. Затем ваш сервер будет работать с размером пула... а затем делать дальнейшие запросы до тех пор, пока не будет доступен рабочий поток.

2. Как мне форматировать данные в моих пакетах?

Если вы не разрабатываете совершенно новый протокол... это не то, о чем вам действительно нужно беспокоиться. Если вы не имеете дело с потоковыми медиа (или другим приложением, в котором допустима потеря/повреждение пакетов), вы, вероятно, не будете использовать UDP для этого приложения. TCP/IP, вероятно, будет вашим лучшим выбором... и это будет определять дизайн пакетов для вас.

3. Какой формат я использую для сериализации?

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

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

Вы должны выбрать то, что лучше всего соответствует вашим потребностям.

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

Ответ 2

Что касается проблем с дизайном сервера, я бы сказал, что вы правы: хотя ONE-THREAD-PER-SOCKET - простой и легкий подход, это не тот способ, поскольку он не будет масштабироваться, как и другие шаблоны проектирования серверов.

Мне лично нравится подход COMMUNICATION-THREADS/WORKER-THREADS, где пул динамического числа рабочих потоков обрабатывает всю работу, созданную потоками производителя.

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

Я нашел Сетевое программирование UNIX Ричардом Стивенсом и удивительным источником для такого рода подходов к сетевому программированию. И, несмотря на его название, он будет очень полезен и в окнах.

Что касается компоновки пакетов (для этого вам следует задать другой вопрос, поскольку, на мой взгляд, это совершенно другой вопрос), при выборе метода TEXT vs. BINARY существуют компромиссы.

ТЕКСТ (т.е. XML), вероятно, проще разбирать и документировать и более просто в общем, в то время как протокол BINARY должен давать вам лучшую производительность с точки зрения скорости обработки и размера сетевых пакетов, но вам придется иметь дело с более сложные вопросы, такие как ENDIANNES слов и тому подобное.

Надеюсь, что это поможет.

Ответ 3

Хотя предыдущие ответы обеспечивают хорошее направление, просто для полноты, я хотел бы указать, что потоки не являются абсолютным требованием для большой производительности сервера сокетов. Некоторые примеры здесь. Существует также множество подходов к масштабируемости: пулы потоков, предварительно распашные процессы, пулы серверов и т.д.

Ответ 4

1) И последнее, есть ли простая в использовании библиотека, которая обычно используется, которая поддерживает переносимость (например, разработка на машине Windows и развертывание в ящике Linux), кроме boost asio.

Еще одна альтернатива - это библиотека ACE. Он очень зрелый (был с начала 90-х годов) и широко развернут. Краткое описание того, как оно сравнивается с Boost ASIO доступно на Riverace веб-сайт здесь. Имейте в виду, что ACE пришлось долгое время поддерживать большое количество устаревших платформ, поэтому он не использует современные функции С++, например Boost ASIO.

2) Предположим, что я хотел бы построить сервер на С++, который обрабатывал бы входные данные из тысяч автономных и/или веб-приложений, как мне тогда проектировать мой сервер? До сих пор я обычно создавал новый и уникальный поток для каждого подключаемого пользователя, но я сомневаюсь, что это путь.

Существует ряд широко используемых подходов, включая, но не ограничиваясь: thread-per-connection (описываемый вами подход) и пул потоков (описанный подход Justin). У каждого есть свои плюсы и минусы. Многие рассматривают компромиссы. Хорошей отправной точкой могут быть ссылки на шаблон пула потоков страница Википедии.

Dan Kegel "Проблема C10K "В веб-странице есть много полезных заметок об улучшении масштабируемости.

3) Также, как определить расположение пакетов, отправленных по сети; данные обычно отправляются по сети в двоичном или текстовом состоянии? Как вы обрабатываете сериализованные объекты при отправке данных на другой носитель (например, сервер С++ для флэш-приложения)?

Я согласен с другими, что отправка двоичных данных, как правило, будет наиболее эффективной. Библиотека boost serialization может использоваться для маршализации данных в двоичную форму (а также текст). Зрелые двоичные форматы включают XDR и CDR. CDR - это формат, используемый CORBA, например. Компания ZeroC определяет ICE кодирование, которое должно быть намного более эффективным, чем CDR.

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

Тем не менее, существует много middleware, который уже предоставляет законченное решение для большинства ваших потребностей. Например, OpenSplice и OpenDDS являются обеими реализациями OMG Служба распространения данных стандарт. DDS фокусируется на эффективном распределении данных, таких как модель публикации-подписки, а не на удаленный вызов функций. Я больше знаком с технологиями, определенными OMG, но я уверен, что существуют другие реализации промежуточного программного обеспечения, которые будут соответствовать вашим потребностям.

Ответ 5

вам все равно понадобится сокет для обработки каждого клиента, но идея будет заключаться в создании пула X сокетов (скажем, 50), а затем, когда вы приблизитесь (скажем, 90%) к потреблению всех этих сокетов, создайте еще один пул из X сокетов. В какой-то момент, после того, как клиенты подключились, отправили данные и отключились, некоторые из ваших сокетов будут доступны для использования, и вы можете использовать их (пулы сокетов google для этой информации)

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

re: сериализованный, извините, я не могу вам помочь, ни с библиотеками (я слишком встроен, чтобы использовать многие из них)

Ответ 6

О сокетах сервера и сериализации (маршалинг). Важнейшей проблемой является увеличение числа сокетов, которое можно читать и записывать в выбранном состоянии. Я не об ограничении в FD_SET. Это разрешимо просто. Я говорю о росте времени передачи сигналов и проблемных данных в нечитаемых сокетах при обработке данных, доступных в оцененном сокете. Таким образом, решение может быть даже вне границ SW и требует множественной модели процессора, когда роли процессоров ограничены: один считывает и записывает, N обрабатывает. В этом случае все доступные данные сокетов должны быть прочитаны, когда выбранный возвращен и отправлен на другие процессоры.

То же самое касается входящих данных.

О маршалинге. Из грубого двоичного формата предпочтительнее, поскольку производительность. В том, как XML в терминах UNICODE имеет ту же проблему. Но... товарищи, это не просто копирование длинного или целочисленного значения в поток сокетов. Но в этом случае даже htons, htonl может помочь (он отправляет/получает в формате NW, а ОС отвечает за преобразование данных). Но безопаснее отправлять данные после заголовка представления, где выставлен формат большинства/наименее значимых битов, порядок байтов и тип данных IEEE. Это работает, у меня не было случая, когда нет.

С уважением и большим успехом для всех. Саймон Кантор