Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > [BCB2009] Многопоточночть. Online игра.


Автор: MuForum 9.8.2009, 18:26
Доброе время суток.
Работаю на "Borland C++ Builder 2009".
Мне необходимо написать программу на подобие прокси-сервера для Online игры. (MMORPG)
Необходимо создавать для одного пользователя два потока:
1. Клиент <> моя программа
2. моя программа <> Сервер
Программа должна поддерживать ~1000 пользователей. То есть, это 2000 потоков + 1 прослушивающий(Listen) = Итого ~2001 потоков.
Работаю я с асинхронными функциями передачи данных: WSA*()

После многодневного чтения форумов я понял, что для моей задачи мне необходимо организовать пул потоков/Pool threads.
- Так же я понял, что кол-во поток в пуле должен быть: кол-во процессоров + 1, а не так как писал Рихтер, что кол-во процессоров * 2.

Собственно система передачи данных между клиентами у меня уже реализована и работает на ура.
- Почему я создал данную тему?
- Потому, что раньше кол-во игроков не достигало больше 120 пользователей, а как только это число резко выросло, я столкнулся с тем, что по достижению какого-то кол-во пользователей, новый пользователи подключится не могут, а старые продолжают играть. Во время подключения в таком случае пользователей получает ошибку номер 10053 (WSAGetLastError()).

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

Прочитал статью на RSDN: http://www.rsdn.ru/article/baseserv/threadpool.xml;
Прочитал статью "Джеффри РИХТЕР "Windows для профессионалов"": http://wm-help.net/books-online/book/59464/59464-4.html;
- Многое прояснилось. Какую-то часть даже понял =)

В общем меня заинтересовала реализации через: "Встроенная поддержка пула потоков". ( QueueUserWorkItem() )
- Только вот как применить эту функцию на практике не могу понять. Искал в гугле примеры, но кроме частных особо нечего и не встретишь.


P.S. -> Прошу помощи в реализации. То есть, может кто-то подкинет название какой-то книги, либо примерчик(нормальный). 

Автор: Romikgy 9.8.2009, 18:35
Цитата(MuForum @  9.8.2009,  17:26 Найти цитируемый пост)
Borland C++ Builder 2009

используя это я бы восплользовался Indy там есть уже прокси ....

Автор: MuForum 9.8.2009, 18:40
Цитата(Romikgy @ 9.8.2009,  18:35)
Цитата(MuForum @  9.8.2009,  17:26 Найти цитируемый пост)
Borland C++ Builder 2009

используя это я бы восплользовался Indy там есть уже прокси ....

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


# Добавлено: Когда подключается новый пользователь, я инициализирую класс "CS_Item", и помещаю его в список.
Код

class CS_Item
{
    public:
        unsigned long ID; // Идентификатор пользователя;
        TS_CS *cs; // Класс для обмена данными между Клиентом и моей программой;
        TS_SG *sg; // Класс для обмена данными между Сервером и моей программой;
};

Далее я инициализирую класс "TS_CS" для обмена данными с клиентом.
Код

class TS_CS: public TThread
{
    private:
        SOCKET sListen; // Сокет клиента;
        SOCKET sSocket; // Сокет сервера;
        AnsiString sIPAddress;
        unsigned long sID;
        ....
    public:
        __fastcall TS_CS::TS_CS(bool CreateSuspended): TThread(CreateSuspended) {};
        virtual void __fastcall Execute(void);
        ...
};

После того, как пользователь заходит в игру и выбирает сервер на котором он хочет играть, я инициализирую класс "TS_SG" для обмена данными с сервером.
Код

class TS_SG: public TThread
{
    private:
        SOCKET sListen; // Сокет сервера;
        SOCKET sSocket; // Сокет клиента;
        AnsiString sIPAddress;
        unsigned short sPort;
        unsigned long sID;
        ...
    public:
        __fastcall TS_SG::TS_SG(bool CreateSuspended): TThread(CreateSuspended) {};
        virtual void __fastcall Execute(void);
        ...
};

Далее уже происходит обмен данными между клиентом и сервером через мою программу, через эти два потока.


P.S. -> Надеюсь изъяснился доступно.

Автор: Romikgy 9.8.2009, 21:31
Цитата(MuForum @  9.8.2009,  17:40 Найти цитируемый пост)
Мне необходимо немного другое ежели прокси-сервер

точнее ...

 и где вопросы ?

Автор: Alca 9.8.2009, 21:34
Цитата

используя это я бы восплользовался Indy там есть уже прокси ....

Там еще есть такое IdSchedulerOfThreadDefault (Indy 10).
Сразу скажу, что не юзал этот компонент.  smile 

Автор: Lazin 9.8.2009, 22:04
Цитата(MuForum @  9.8.2009,  18:26 Найти цитируемый пост)
QueueUserWorkItem

лучше использовать порты завершения

Автор: SVN74 9.8.2009, 22:17
Я с такой штучкой сталкивался, - правда у меня было на блокирующих сокетах и применял я CreateThread, - тоже при большом количестве народу отсекало некоторых...
Дело было: accept - > CreateThread, - когда accept передавал в поток (КОПИЮ) сокета, то проходит определенное время создания нового потока, а accept продолжает работать получая новый сокет... Получается - пока создастся новый поток, то копия сокета до него не доходит.
Я просто поставил задержку между созданием нового потока и созданием очередного сокета и все.... smile

Автор: Lazin 9.8.2009, 22:34
SVN74, порождать поток для каждого нового клиента - некошерно

Автор: SVN74 9.8.2009, 22:40
Цитата(Lazin @  9.8.2009,  22:34 Найти цитируемый пост)
порождать поток для каждого нового клиента - некошерно 

Оно то так, если стоит стандартная задача для всех клиентов, а если задач много типа ftp, gsm,video и тд.?

Автор: Lazin 10.8.2009, 08:09
Цитата(SVN74 @  9.8.2009,  22:40 Найти цитируемый пост)
Оно то так, если стоит стандартная задача для всех клиентов, а если задач много типа ftp, gsm,video и тд.?

это не важно, механизм IOCP, позволяет организовать диспетчеризацию разных событий

если вкратце, то выглядит оно так: ты создаешь порт завершения, связываешь объекты(файлы, сокеты) с ним, с каждым объектом(клиентом) может быть связан еще один объект(completion key), когда ты начинаешь операцию на сокете, она выполняется асинхронно(send/recv возвращают управление сразу, не дожидаясь, когда произойдет ввод/вывод) и с ней можно связать структуру OVERLAPPED(конечно это может быть не эта структура, а к примеру ее наследник, содержащий дополнительные поля). Дальше, когда операция завершается, ты можешь вызвать ф-ю GetQueuedCompletionStatus, и получить ключ завершения(completion key) и структуру OVERLAPPED(или то, что ты передал вместо нее), завершенной асинхронной операции. Эти данные однозначно идентифицируют клиента твоего сервера а так-же конкретную операцию ввода-вывода, которая завершилась для данного клиента.
Типичный пример использования - ты создаешь М потоков, и в каждом из низ выполняется цикл, в котором вызывается ф-я GetQueuedCompletionStatus, это похоже на обработку сообщений windows, только в несколько потоков. 
Порт завершения можно настроить так, что-бы на нем могли выполняться одновременно только К потоков(К < M). К примеру, у тебя в пулле потоков - 20 потоков, а на порте завершения может работать только 10 потоков одновременно. Допустим в начальный момент времени, все потоки в режиме ожидания (сидят в ф-ии GetQueuedCompletionStatus). После того, как на порте завершения произойдет какое-либо событие, у тебя в одном из потоков, ф-я GetQueuedCompletionStatus, вернет управление, после чего твой код обработает это событие и снова вызовет эту ф-ю и будет ждать следующего события. Если произойдет 10 событий, то у тебя будут работать все 10 потоков. Если в этот момент произойдет 11-е событие, оно будет помещено в очередь, до тех пор, пока один из 10 работающих потоков не перейдет в состояние ожидания, к примеру вызвав ф-ю GetqueuedcompletionStatus. Но в состояние ожидания поток может перейти, вызвав к примеру ф-ю WrteFile(синхронно), либо WaitForSingleObject. В общем, если один из 10 работающих потоков по какой-либо причине должен будет что-нибудь ждать, то система разбудит еще один поток из пулла, и передаст туда 11-е событие. В результате, даже если у тебя в коде обработчиков событий есть синхронный ввод/вывод, или ожидание всяких локов, процессор простаивать не будет.
В общем, механизм очень эффективен, для реализации высоко-нагруженного сервера лучше использовать именно его.
Статья Русиновича - http://technet.microsoft.com/ru-ru/sysinternals/bb963891(en-us).aspx

Автор: Alca 10.8.2009, 12:28
http://www.gamedev.ru/community/mmorpg/articles/?id=2522
http://r3code.livejournal.com/98391.html
http://forum.sources.ru/index.php?showtopic=229784&view=showall

Автор: Alca 10.8.2009, 12:56
http://books.google.ru/books?id=vay1dnm9QIcC&pg=PA450&lpg=PA450&dq=%D0%BF%D0%BE%D1%80%D1%82%D1%8B+%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D1%8F&source=bl&ots=mf4AwXwxZy&sig=KJhz1QzMUjVKwLldA8jHdfiM4NU&hl=ru&ei=_-x_SsDbLdiJsAa55KXxAg&sa=X&oi=book_result&ct=result&resnum=10#v=onepage&q=%D0%BF%D0%BE%D1%80%D1%82%D1%8B%20%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D1%8F&f=false

Автор: MuForum 10.8.2009, 14:17
Модератор: Сообщение скрыто.

Автор: MuForum 14.8.2009, 01:29
# for all - Нашел небольшую статью про "Порты завершения": http://www.gamedev.ru/community/mmorpg/articles/?id=2522;
- Может кому-то пригодится.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)