Какую модель на основе сокетов следует использовать для множества одновременных подключений?

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

Я читал о c10k, но что мне использовать, если, например, мой сервер позволяет одновременно только 5000 клиентов? Блокирующие сокеты, многопоточные (один поток на клиента)? Неблокирующие сокеты, без резьбы? цикл, обрабатывающий всех клиентов всего за несколько потоков? IOCP?

Каковы преимущества и недостатки, вкратце? Я уже гуглил, но не нашел конкретной статьи про все эти модели.

Или есть более простой способ справиться с проблемой c10k?

Платформа: WindowsXP


person Patryk Czachurski    schedule 10.11.2010    source источник
comment
Просто дружеское замечание: нет смысла добавлять квази-теги в квадратных скобках к заголовку вашего вопроса, если вы все равно добавите их как теги. См. meta.stackexchange.com/questions/10647/   -  person suszterpatt    schedule 11.11.2010


Ответы (4)


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

Недавно я собрал некоторые из причин, по которым предпочитаю IOCP другим моделям, доступным в Windows, и они доступны здесь, в моем блоге здесь.

Часто «сложность» приводится как причина избегать IOCP, но, честно говоря, если вы делаете что-то кроме «потока на соединение», код все равно будет достаточно сложным. У меня есть бесплатный набор кода C++ с примерами серверов, который вы Вы можете взглянуть на него, он выполняет всю работу IOCP за вас, а примеры серверов являются хорошей отправной точкой для вашего собственного кода. Вы можете получить его по адресу здесь.

В качестве альтернативы у меня также есть бесплатная (для некоммерческого использования) подключаемая серверная платформа, которая может вас заинтересовать. С WASP вы просто пишете dll с определенными точками входа, а я делаю всю работу по сети за вас... См. здесь для загрузки и здесь для руководства .

Какую бы модель вы ни выбрали, САМАЯ важная вещь, ИМХО, заключается в том, чтобы начать тестирование производительности и нагрузки с 0 ДНЯ. Таким образом, вы ЗНАЕТЕ, сколько одновременных подключений вы можете поддерживать на целевом оборудовании, и вы можете быстро обнаружить неподходящие проектные решения. которые вызывают это падение. Параллелизм вашего сервера легко растратить. См. здесь для получения дополнительной информации.

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

person Len Holgate    schedule 11.11.2010

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

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

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

Затем вы можете решить, должны ли вы гарантировать порядок пакетов и насколько важен каждый отправленный пакет, поскольку тогда вам нужно будет смотреть на TPC или UDP. (http://www.skullbox.net/tcpudp.php)

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

Затем вы можете посмотреть select() (http://www.lowtek.com/sockets/select.html), что позволило бы меньшему количеству потоков обрабатывать нагрузку, но если вы выполняете потоковую передачу, это может быть проблемой, поскольку выделенный поток имеет больше смысла.

Итак, как решить, что лучше, зависит от того, какой тип игры вы пишете, поскольку оба могут быть полезны в зависимости от сценария.

person James Black    schedule 11.11.2010
comment
Когда вы используете selects, вы фактически опрашиваете сокет. Не имеет значения, может ли один поток одновременно выполнять выборку из 16 (или любых других) сокетов, он все равно опрашивает (хотя и несколько более эффективно). Один или несколько (или все) сокетов, включенных в выборку, не будут готовы к обслуживанию, и для каждого из них вы потеряете (драгоценное) процессорное время. IOCP сигнализируют о таких событиях, как WriteFile завершила запись или ReadFile завершила чтение данных. продолжение - person Olof Forshell; 08.12.2010
comment
(продолжение) Если вы являетесь сервером, первое событие означает, что вы можете выдать ReadFile для обработки следующего клиентского запроса, второе — что есть клиентский запрос, который необходимо обслужить, за которым следует ответ, выданный порту с помощью WriteFile. Вообще ничего не тратится. - person Olof Forshell; 08.12.2010
comment
@Olof Forshell. Моя цель использования select() заключалась в том, чтобы уменьшить количество задействованных потоков, поскольку это более масштабируемо, чем наличие одного потока/соединения, но не является лучшим во всех ситуациях. - person James Black; 09.12.2010

Вы читали эту статью?

Я провел аналогичное исследование и нашел библиотеку eNet. Я считаю, что он использует Select() вместо любого из новых методов, но есть много пользователей, которые указали, что они получили очень хорошие результаты с ним. Это нетрудно изменить, если вам абсолютно необходимо столько одновременных подключений.

person Jay    schedule 10.11.2010

Вы должны использовать IOCP и ввод-вывод сокетов (любой простой ввод-вывод) с помощью ReadFile и WriteFile.

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

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

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

person Olof Forshell    schedule 07.12.2010
comment
Существует параметр тайм-аута при ожидании завершения ввода-вывода. Когда вы истекаете (т. е. нет завершения обслуживания), вы можете запустить свои активные сеансы/соединения и выполнить уборку, например, закрыть сеансы, которые не ответили в течение определенного периода времени. Если вы предвидите, что тайм-аут не произойдет, потому что свободного времени никогда не будет, вы можете создать очень маленький поток, который просыпается, может быть, раз в секунду и отправляет фиктивное время, сигнализирующее о завершении ввода-вывода, для выполнения хозяйственных операций. Поток, который делает так мало, не повлияет на производительность в целом. - person Olof Forshell; 08.12.2010