![]() |
|
Модераторы: xvr |
![]()
|
|
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
Здравствуйте!
Хочу создать игровой сервер. Есть не малый опыт программирования на С/С++. Никогда не писал сетевые программы, и тем более многопользовательские сервера. Читаю это: http://rapidshare.com/files/287978995/Shon..._Linux.pdf.html Купил http://www.ozon.ru/context/detail/id/3961491/, еще заказал http://www.ozon.ru/context/detail/id/2355792/ Попробовал написать простой клиент и сервер по кодам из книжки, работает. Меня больше интересует, как обрабатывается множество соединений одновременно? То что нужно синхронизировать доступ к общим данным, понимаю, полагаю использовать pthread(http://ru.wikipedia.org/wiki/POSIX_Threads). С принципами многопоточности/синхронизации знаком, использовал не мало. В книжке(http://rapidshare.com/files/287978995/Shon_Uolton_-_Sozdanie_setevih_prilojenii_v_srede_Linux.pdf.html) в качестве примера приведен пример сервера использующего accept(). Но, как я выяснил, вычитывая статьи из инета, это очень старый способ, есть нечто более новое, poll, и еще по новее epoll. Так вот epoll описывается в этой(http://www.ozon.ru/context/detail/id/3961491/) книжке. Но только вызовы. Никакой информации о проектировании сервера с использованием epoll нет. Вот собственно и сама просьба, наставьте на путь истинный. Подскажите, что нужно делать, и как? Как проектировать сервер, что нужно знать для проектирования, ... и еще много вопросов о которых я еще не знаю. Всем откликнувшимся, много благодарен! Это сообщение отредактировал(а) niXman - 3.10.2009, 05:01 --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
архитектура
1) поток 1 а) дергает accept() б) если в а) получили сокет, создаем (берем из кэша) дескриптор клиента и помещаем его в очередь в) переход на а) 2) поток 2 выполняет бесконечный цикл а) выполняет list_replace_init на очереди клиентских дескрипторов б) циклически просматривает матрицу элементов выполнения в) если поле состояния элемента - COMPLETE, освобождаем все данные, связанные с ним, берем дескриптор клиента (если он есть), связываем его с элементом выполнения, переводим его в состояние READY; если состояние элемента - FREE, значит он никем не используется, выполняем аналогичные действия кроме освобождения данных (освобождать нечего) 3) потоки 3...N, где N пропорционально числу процессоров а) циклически просматривают свою часть матрицы, в соответствии с состоянием конкретного элемента выполнения, выполняются специфичные действия для него, в состоянии READY кроме всего прочего добавляем (epoll_ctl()) сокет клиента в дескриптор epoll текущего потока б) после выполнения цикла, дергает epoll_wait() в) обрабатываем события, возвращенные в б), в соответствии с состоянием каждого элемента выполнения (к примеру, если у нас SMTP сервер , текущее состояние элемента выполнения SMTP_RCPT и получили событие EPOLLIN, то читаем из сокета данные; если в полученном буфере находим "\r\n", переходим в состояние SMTP_RCPT_RESPONSE; далее в а) на следующей итерации будет отправлен ответ на RCPT команду SMTP клиента) Добавлено через 2 минуты и 56 секунд соответственно на этой схеме запросто реализуется взаимодействие с внешними менеджерами типа менеджера БД т. е если нам надо послать запрос в базу, формируем запрос и добавляем его в очередь менеджера, при этом состояние элемента выполнения сохраняем (сокет и пр. _важные_ для восстановления данные), элемент выполнения освобождаем для возможного другого клиента -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
--------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
niXman,
не обязательно я просто привел одну из схем распараллеливания для достижения масштабируемости и модульности при минимуме использования механизмов синхронизации вполне возможно совместить 1) и 2) причем как в main, так и в отдельном потоке (и даже процессе) все зависит от конечной цели -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
MAKCim, Ок. Сейчас пишу свое видение, более детальное. Выложу. Проверите.(если не затруднит)
--------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
1. количество потоков в ОС ограниченно. Наверное будет правильней, чтоб одни поток обслуживал не одного клиента. В этом случае меньше расходов на синхронизацию. 2. что подразумевается по словом "процессоров" ? --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
объекты, которые могут независимо выполнять код
ну, а я что сказал? ;) если процессоров в системе, допустим, 4, то резонно создать 8 потоков: по одному на каждый процессор + 1 дополнительный с учетом эффективной реализации шедулера в ОС -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
Все, понял. А я подумал что речь идет о процессах(или потоках). Добавлено через 4 минуты и 41 секунду
Т.е. по потоку на ядро? Если да, то как можно создать поток именно на определенном ядре? Я думал что ОС сама принимает решение об этом. Это что такое? --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
это и подразумевалось, в смысле, что если тупо создать 4 потока, то ОС в большинстве случаев распределит их по имкющимся процессорам равномерно тем не менее есть linux-specific API функция sched_setaffinity это значит, что если планировщик задач ядра ОС реализован грамотно, то выбор процесса и переключение контекста на него осуществляются с минимальным оверхедом, в этом случае увеличение нагрузки на один процессор (увеличение числа потокв) до некоторого предела даст лучший результат, нежели концентрация большего количества работы в одном потоке -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
А если потребуется 5000 клиентов? ОС наверняка столько потоков не позволит создать. --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
niXman,
где я говорил, что нужно 5000 потоков для 5000 клиентов? я говорил о _пропорциональном_ числу процессоров числе потоков в большинстве случаев это 2x, т. е. 1 проц. - 2 потока, 2 проца - 4 потока и т. д. вы читали мой первый пост вообще? -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
--------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
Имеется ввиду это? : http://crona-dev.net/index.php/mcdatatypes/mistructlisthead http://rflinux.blogspot.com/2009/05/linux.html Т.е. это просто список, или он чем-то принципиально отличается? Что подразумевается под "матрица элементов выполнения" ? Это сообщение отредактировал(а) niXman - 6.10.2009, 00:28 --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
да использование по началу не совсем очевидно ;) здесь элементы не добавляются в список, а уже являются членами списка пример
массив массивов каждому из потоков 3...N соответствует свой индекс в нем => каждый из потоков работает со своей частью матрицы -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
При подклучении <linux/list.h> получаю такие ошибки:
Дело в том, что каталог linux есть в /usr/include, но в нем нет файла list.h. По этому я подключил каталог /usr/src/linux-headers-2.6.28-15/include Я правильно делаю? Или файл list.h должен находится в /usr/include/linux ? Добавлено через 49 секунд OS: ubuntu-9.10 --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
нет ;) linux/list.h - это часть ядра со всеми вытекающими вот адаптированная для userspace имплементация Присоединённый файл ( Кол-во скачиваний: 5 )
list.rar 2,87 Kb-------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
MAKCim, Скажите, можно ли как-то определить кол-во процессоров/ядер?
Я думаю что нужно определить их кол-во ядер, и создать кол-во потоков обслуживающих клиентские подключения, столько же. --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
но это не сецифицировано в posix -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| sdukshis |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 92 Регистрация: 23.3.2009 Репутация: нет Всего: 1 |
Могу высказать свое скромное мнение по этому поводу.
Возможно подойдет архитектура nginx. 1 главный процесс и несколько рабочих процессов (или потоков). Главный процесс принимает новое соединение и передают его одному из рабочих (в случае использования потоков это логично делать через глобальные структуры данных, например списки списков, при использовании дочерних процессов скорее подойдет дублирование дескрипторов и именованные каналы). Каждый из рабочих процессов работает с несколькими клиентами. В этом случае наиболее удачным по производительности решением будет использование мультиплексирования на основе poll или pselect (если разработка ведется под конкретных платформу, то лучше применять специализированные методы Linux - epoll, FreeBSD - kqueue). Остается логичный вопрос по поводу количества рабочих процессов, но на мой взгляд формула: кол-во ядер CPU == кол-ву рабочих процессов, не очень удачна, поскольку не учитывает блокировки процессов при файловом вводе-выводе. Точное количество конечно надо определять экспериментально, поэтому этот параметр должен задаваться извне, а не быть зашитым в программу. |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
тогда уж лучше юзать libevent и забыть о select/poll/epoll/kqueue/... ;)
-------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
Дык..
Я пишу, используя epoll, а выясняется что лучше libevent. Так что мне использовать? --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
niXman
libevent кроссплатформенная библиотека если нужна кроссплатформенность, лучше использовать ее, т. к. select и poll не столь эффективны это не более чем комментарий к посту sdukshis -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
MAKCim, Да, я уже посмотрел. Это просто обертка над API функциями.
Продолжаю кодить... Добавлено через 2 минуты и 38 секунд Да, кстати. Провел эксперимент по распределению потоков на ядра. Все правильно, если создать 4 потока, то ОС правильно распределит, по одному на ядро. --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| alx49 |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 3 Регистрация: 20.10.2009 Репутация: нет Всего: нет |
А есть ли резон писать свою сетевую часть? Советую обратить взор на boost::asio (и на boost::thread к слову о потоках и количестве ядер, там есть специальня функция, говорящая сколько потоков можно эффективно заюзать)
В доках boost'а есть хороший мануал, что с этой библиотекой делать, и как это делать эффективно. |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
Я не пишу подсистему ввода-вывода. Прочитайте второй пост. Не хочу использовать по некоторым причинам.
Можно по подробнее? Интересно взглянуть на реализацию. Это сообщение отредактировал(а) niXman - 20.10.2009, 19:05 --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| nerezus |
|
|||
![]() Вселенский отказник ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3330 Регистрация: 15.6.2005 Репутация: нет Всего: 43 |
Тем более что если пользователем будет запрошено ресурсоемкое действие, это заморозит всех пользователей на этом же потоке. |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
это смотря какие руки и голова ;) -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| nerezus |
|
|||
![]() Вселенский отказник ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3330 Регистрация: 15.6.2005 Репутация: нет Всего: 43 |
MAKCim, и как можно этого избежать?
|
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
--------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
niXman,
nerezus, есть такая штука как неблокируемый режим работы любой запрос любого ресурса можно разбить на ряд мелких, для выполнения которых не нужно блокировать процесс/поток ожидание ответа реализуется посредством опроса через определенные интервалы времени если ресурс не удаленный, то используя асинхронный обработчик, передаваемый вместе с запросом, мы можем изменить состояние дескриптора без опроса естественно, такое решение более сложое, чем тупо создание потоков в неограниченном количестве -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| nerezus |
|
|||
![]() Вселенский отказник ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3330 Регистрация: 15.6.2005 Репутация: нет Всего: 43 |
|
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
менеджер БД должен работать в отдельном потоке все обращения к нему асинхронные, запросы кладутся в очередь менеджер проверяет очередь и выполняет их (в целях ускорения юзает multiple statements) -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
MAKCim, Спасибо, хорошая идея.
--------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 1 Всего: 0 |
А ведь и вправду, долгие операции будут тормозить поток, и все клиенты в этом же потоке, будут ждать завершения.
К примеру, загрузка карты. Пока один клиент загружает, все остальные ждут, и так с каждым В таких случаях, наверное нужно чтоб продолжительные операции выполнялись в отдельном потоке. При том, кол-во этих "отдельных" потоков, нужно определять исходя из конкретной задачи. У кого-то есть какие-то соображения по этому поводу? Может кто-то знает подобный opensource проект, чтоб посмотреть как это реализовано? --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
| alx49 |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 3 Регистрация: 20.10.2009 Репутация: нет Всего: нет |
Я когда писал свой сервак, делал специальный watchdog, который в случае, если все рабочие потоки долго заняты, добавлял в пул еще потоки. У меня правда потоки обрабатывали эвенты, тоесть клиент жеско к потоку не привязывался.
|
|||
|
||||
| MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 84 Всего: 207 |
niXman,
надо разделять ввод-вывод и логику использование неблокируемого ввода-вывода увеличит скорость реакции при большой нагрузке (большом количестве клиентов) однако от тормозов на логике это не избавит пока внешние менеджеры обрабатывают большие по времени выполнения запросы, клиенту можно выдать сообщение типа идет работа и т. д в противном случае (без неблокируемого ввода-вывода) клиент тупо будет ждать, "не понимая" что происходит иными словами, интерактивность хреновая ;) -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
| Loki |
|
||||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 244 Регистрация: 9.7.2006 Репутация: нет Всего: нет |
хм, а если у меня как-бы потоки создаются в потоке? например есть куча серверных сокетов, на каждый сокет, свой поток и в них в асинхроне я создаю потоки по мере необходимости(для получения данных), потом они терминируются и отдают данные в главный поток(т.е. я использую aio) и в нем же происходит mysql_query(), только вот я не проверяю стейты предидущих данных, т.е. очередей как-бы нет и проблем пока не возникло, хотя вот если добавить фильтрацию, тогда некоторые данные будут тормозиться и тогда наверно придется сооружать очередь, только пока хз как но mutex не прокатит. поток то один.
это у вас наверно epoll был? а если просто потоки приотерезировать то наверно mutex'ами |
||||
|
|||||
![]()
|
| Правила форума "С/С++: Программирование под Unix/Linux" | |
|
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |