| 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 | < Назад | Оглавление | Далее > |