netlib.narod.ru | < Назад | Оглавление | Далее > |
В предыдущей главе вы увидели, как просто использовать DirectPlay. Теперь вы узнаете, как работать с DirectPlay, используя сетевое ядро. Сетевое ядро содержит три класса: cNetworkAdapter, cNetworkServer и cNetworkClient.
Вы используете cNetworkAdapter для перечисления установленных в вашей системе TCP/IP-устройств. Чтобы установить соединение вам необходимо знать GUID устройства, и его получение является целью класса cNetworkAdapter. Вот как выглядит объявление класса:
class cNetworkAdapter { protected: DPN_SERVICE_PROVIDER_INFO *m_AdapterList; // Список адаптеров unsigned long m_NumAdapters; // Количество адаптеров // Пустой обработчик сетевых сообщений - необходим static HRESULT WINAPI NetMsgHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer) { return S_OK; } public: cNetworkAdapter(); // Конструктор ~cNetworkAdapter(); // Деструктор BOOL Init(); // Инициализация объекта класса BOOL Shutdown(); // Завершение работы объекта // (освобождение памяти) long GetNumAdapters(); // Получение количества // установленных адаптеров // Сохранение имени адаптера в буфере // (Num от 0 до количества адаптеров минус 1) BOOL GetName(unsigned long Num, char *Buf); // Возвращает указатель на GUID адаптера // (Num от 0 до количества адаптеров минус 1) GUID *GetGUID(unsigned long Num); };
Использовать класс cNetworkAdapter просто: вызовите функцию Init, запросите количество установленных адаптеров, а затем начинайте извлекать имена адаптеров и их GUID. Когда завершите работу с объектом, вызовите Shutdown для освобождения внутренних ресурсов класса.
Я вернусь к использованию класса cNetworkAdapter в следующих двух подразделах. А сейчас давайте перейдем к классу cNetworkServer.
На серверной стороне сети вы имеете дело с классом cNetworkServer, который позволяет инициализировать серверные объекты DirectPlay, открыть игровую сессию и обрабатывать входящие и исходящие сетевые сообщения. В данном разделе вы увидите, как я оборачиваю операции серверной стороны в представленный ниже класс cNetworkServer:
class cNetworkServer { protected: IDirectPlay8Server *m_pDPServer; // Объект сервера BOOL m_Connected; // Флаг запуска узла // Имя и пароль сессии (хранятся как ASCII-символы) char m_SessionName[MAX_PATH]; char m_SessionPassword[MAX_PATH]; long m_Port; // Используемый порт long m_MaxPlayers; // Максимально допустимое // количество игроков long m_NumPlayers; // Текущее количество игроков // Обработчик сетевых сообщений static HRESULT WINAPI NetworkMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer); // Перегружаемые функции для различных // сетевых сообщений virtual BOOL AddPlayerToGroup( DPNMSG_ADD_PLAYER_TO_GROUP *Msg) { return TRUE; } virtual BOOL AsyncOpComplete( DPNMSG_ASYNC_OP_COMPLETE *Msg) { return TRUE; } virtual BOOL ClientInfo( DPNMSG_CLIENT_INFO *Msg) { return TRUE; } virtual BOOL ConnectComplete( DPNMSG_CONNECT_COMPLETE *Msg) { return TRUE; } virtual BOOL CreateGroup( DPNMSG_CREATE_GROUP *Msg) { return TRUE; } virtual BOOL CreatePlayer( DPNMSG_CREATE_PLAYER *Msg) { return TRUE; } virtual BOOL DestroyGroup( DPNMSG_DESTROY_GROUP *Msg) { return TRUE; } virtual BOOL DestroyPlayer( DPNMSG_DESTROY_PLAYER *Msg) { return TRUE; } virtual BOOL EnumHostsQuery( DPNMSG_ENUM_HOSTS_QUERY *Msg) { return TRUE; } virtual BOOL EnumHostsResponse( DPNMSG_ENUM_HOSTS_RESPONSE *Msg) { return TRUE; } virtual BOOL GroupInfo( DPNMSG_GROUP_INFO *Msg) { return TRUE; } virtual BOOL HostMigrate( DPNMSG_HOST_MIGRATE *Msg) { return TRUE; } virtual BOOL IndicateConnect( DPNMSG_INDICATE_CONNECT *Msg) { return TRUE; } virtual BOOL IndicatedConnectAborted( DPNMSG_INDICATED_CONNECT_ABORTED *Msg) { return TRUE; } virtual BOOL PeerInfo( DPNMSG_PEER_INFO *Msg) { return TRUE; } virtual BOOL Receive( DPNMSG_RECEIVE *Msg) { return TRUE; } virtual BOOL RemovePlayerFromGroup( DPNMSG_REMOVE_PLAYER_FROM_GROUP *Msg) { return TRUE; } virtual BOOL ReturnBuffer( DPNMSG_RETURN_BUFFER *Msg) { return TRUE; } virtual BOOL SendComplete( DPNMSG_SEND_COMPLETE *Msg) { return TRUE; } virtual BOOL ServerInfo( DPNMSG_SERVER_INFO *Msg) { return TRUE; } virtual BOOL TerminateSession( DPNMSG_TERMINATE_SESSION *Msg) { return TRUE; } public: cNetworkServer(); // Конструктор ~cNetworkServer(); // Деструктор IDirectPlay8Server *GetServerCOM(); // Возвращает объект сервера BOOL Init(); // Инициализирует сетевой сервер BOOL Shutdown(); // Выключает сетевой сервер // Начинает сессию на узле BOOL Host(GUID *guidAdapter, long Port, char *SessionName, char *Password = NULL, long MaxPlayers = 0); BOOL Disconnect(); // Завершение сессии BOOL IsConnected(); // Проверяет, запущена ли сессия // Передача необработанных данных или текстовой строки BOOL Send(DPNID dpnidPlayer, void *Data, unsigned long Size, unsigned long Flags=0); BOOL SendText(DPNID dpnidPlayer, char *Text, unsigned long Flags=0); // Принудительное отключение игрока BOOL DisconnectPlayer(long PlayerId); // Получение IP-адреса игрока или сервера в указанный буфер BOOL GetIP(char *IPAddress, unsigned long PlayerId = 0); // Получение имени игрока BOOL GetName(char *Name, unsigned long PlayerId); // Получение номера используемого порта long GetPort(); // Иолучение имени сессии и пароля BOOL GetSessionName(char *Buf); BOOL GetSessionPassword(char *Buf); // Получение максимально возможного и текущего // количества игроков long GetMaxPlayers(); long GetNumPlayers(); };
Для того, чтобы использовать класс cNetworkServer (точно так же как и класс cNetworkClient, о котором мы поговорим в следующем разделе), вы наследуете собственный класс, используя cNetworkServer в качестве базового. Это необходимо потому что требуется перегрузка обработчиков сообщений вашими собственными функциями. В классе cNetworkServer представлены все сетевые сообщения, так что наследуемый класс не пропустит важной информации.
Чтобы начать сессию вам необходим GUID адаптера, имя сессии, необязательный пароль и максимально возможное количество игроков (0 означает, что ограничений нет). При вызове cNetworkServer::Host DirectPlay инициализирует подключения и возвращает управление вам. После этого вы можете начать ожидание входящих сообщений. Ваша работа — закачивать входящие сообщения и поступать с ними так, как считаете нужным. Создаваемые вами обработчики сообщений должны возвращать TRUE, если сообщение успешно обработано, и FALSE, если произошла ошибка.
В качестве примера здесь приведен экземпляр класса cNetworkServer, который отображает текст входящего сообщения и отсылает точно такое же сообщение назад отправителю (используя гарантированную доставку):
// Создаем класс-наследник class cServer : public cNetworkServer { private: BOOL Receive(DPNMSG_RECEIVE *Msg); }; BOOL cServer::Receive(DPNMSG_RECEIVE *Msg) { // Отображаем сообщение MessageBox(NULL, Msg->pReceivedData, "Incoming Message", MB_OK); // Отправляем его обратно Send(Msg->dpnidSender, Msg->pReceiveData, Msg->dwReceivedDataSize, DPNSEND_GUARANTEED); return TRUE; } int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) { cServer Server; cNetworkAdapter Adapter; GUID *guidAdapter; // Выбираем первый сетевой адаптер Adapter.Init(); guidAdapter = Adapter.GetGUID(0); // 0 = первый адаптер Server.Init(); Server.Host(guidAdapter, 12345, "TextSession"); // Ждем нажатия ESC while(!(GetAsyncKeyState(VK_ESC) & 0x80)); Server.Disconnect(); Server.Shutdown(); // Освобождаем список адаптеров Adapter.Shutdown(); return TRUE; }
Не беспокойтесь, если вам показалось, что приведенный пример показывает слишком мало; вы ближе познакомитесь с сетевыми компонентами в главе 15, «Сетевой режим для многопользовательской игры».
Пришло время взглянуть на последний сетевой класс, cNetworkClient, который работает на клиентской стороне сети:
class cNetworkClient { protected: IDirectPlay8Client *m_pDPClient; // Объект клиента DP BOOL m_Connected; // Флаг подключения char m_IPAddress[MAX_PATH]; // IP-адрес long m_Port; // Порт подключения char m_Name[MAX_PATH]; // Имя клиента // Имя и пароль сессии (пересылается серверу) char m_SessionName[MAX_PATH]; char m_SessionPassword[MAX_PATH]; // Функция обработки сетевых сообщений static HRESULT WINAPI NetworkMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer); // Далее идут перегружаемые обработчики сетевых // сообщений (такие же, как и в cNetworkServer). // Здесь они не приводятся для экономии места public: cNetworkClient(); // Конструктор ~cNetworkClient(); // Деструктор // Возвращает объект клиента DirectPlay IDirectPlay8Client *GetClientCOM(); BOOL Init(); // Инициализация сетевого клиента BOOL Shutdown(); // Выключение клиента // Подключение к удаленному серверу с использованием // указанного адаптера, IP, порта, имени игрока, // имени сессии, и необязательного пароля BOOL Connect(GUID *guidAdapter, char *IP, long Port, char *PlayerName, char *SessionName, char *SessionPassword = NULL); BOOL Disconnect(); // Отключение от сессии BOOL IsConnected(); // Возвращает TRUE при наличии подключения // Функции передачи необработанных данных и текста BOOL Send(void *Data, unsigned long Size, unsigned long Flags=0); BOOL SendText(char *Text, unsigned long Flags=0); BOOL GetIP(char *IPAddress); // Получение IP-адреса в буфер long GetPort(); // Возвращает номер порта // подключения BOOL GetName(char *Name); // Возвращает имя BOOL GetSessionName(char *Buf); // Возвращает имя сессии BOOL GetSessionPassword(char *Buf); // Возвращает пароль };
Работа с cNetworkClient похожа на использование cNetworkServer, за исключением подключения к сети. Чтобы установить подключение, используя cNetworkClient::Connect, вы должны сперва выбрать сетевой адаптер, используя объект класса cNetworkAdapter, как показано в следующем примере:
// Создаем класс-наследник class cClient : public cNetworkClient { private: BOOL Receive(DPNMSG_RECEIVE *Msg); }; BOOL cClient::Receive(DPNMSG_RECEIVE *Msg) { MessageBox(NULL, Msg->pReceiveData, "Incoming Message", MB_OK); return TRUE; } int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) { cClient Client; cNetworkAdapter Adapter; GUID *guidAdapter; // Выбираем первый сетевой адаптер Adapter.Init(); guidAdapter = Adapter.GetGUID(0); // 0 = первый адаптер // Инициализируем клиента и подключаемся к IP=123.123.123.123 // используя порт 12345, сессия = TextSession без пароля. Client.Init(); Client.Connect(guidAdapter, "123.123.123.123", 12345, "MyName", "TextSession"); // Ждем установки соединения, // или пока пользователь не нажмет ESC. while(Client.IsConnected() == FALSE) { if(GetAsyncKeyState(VK_ESC) & 0x80) { Client.Disconnect(); Client.Shutdown(); return TRUE; } } // Подключились, начинаем прикладную обработку // Отправляем текстовое сообщение Client.SendText("Hello there!"); // Ждем нажатия ESC while(!(GetAsyncKeyState(VK_ESC) & 0x80)); // Разрыв соединения и выключение Client.Disconnect(); Client.Shutdown(); // Освобождение списка адаптеров Adapter.Shutdown(); return TRUE; }
И снова пример минимален; все сетевое ядро в действии вы увидите в главе 15. Сейчас можете посмотреть на объявление класса и на код реализации, находящийся на прилагаемом к книге компакт-диске.
netlib.narod.ru | < Назад | Оглавление | Далее > |