您的位置:首页 > 理论基础 > 计算机网络

使用DirectPlay进行网络互联(3)

2016-06-20 00:00 344 查看
销毁玩家

当玩家断开连接时,服务器端会收到消息 DPN_MSGID_DESTROY_PLAYER,这时需要将消息缓冲区转换为DPNMSG_DESTROY_PLAYER类型。

The DPNMSG_DESTROY_PLAYER structure contains information for the DPN_MSGID_DESTROY_PLAYER system message.

typedef struct _DPNMSG_DESTROY_PLAYER{
DWORD dwSize;
DPNID dpnidPlayer;
PVOID pvPlayerContext;
DWORD dwReason;
} DPNMSG_DESTROY_PLAYER, *PDPNMSG_DESTROY_PLAYER;


dwSize

Size of this structure.

dpnidPlayer

DPNID of the player deleted from the session.

pvPlayerContext

Player context value.

dwReason

One of the following flags indicating why the player was destroyed.

DPNDESTROYPLAYERREASON_NORMAL

The player is being deleted for normal reasons.

DPNDESTROYPLAYERREASON_CONNECTIONLOST

The player is being deleted because the connection was lost.

DPNDESTROYPLAYERREASON_SESSIONTERMINATED

The player is being deleted because the session was terminated.

DPNDESTROYPLAYERREASON_HOSTDESTROYEDPLAYER

The player is being deleted because the host called
IDirectPlay8Peer::DestroyPeer.

Return Values

Return DPN_OK.

Remarks

In client/server mode, this message is received only by the server. In peer-to-peer mode, all players receive this message.

When the server closes a session, it receives a DPN_MSGID_DESTROY_PLAYER message for all connected players. Because the server knows that it is disconnecting, this is normal behavior, and the dwReason member of the associated structure is set to DPNDESTROYPLAYERREASON_NORMAL. The DPNDESTROYPLAYERREASON_SESSIONTERMINATED value is only set for unexpected disconnections.

You might receive DPN_MSGID_CREATE_PLAYER and DPN_MSGID_DESTROY_PLAYER messages on different threads. However, you will not receive a DPN_MSGID_DESTROY_PLAYER message before your callback function has returned from receiving a DPN_MSGID_CREATE_PLAYER message.

要强制一个玩家断开连接,使用IDirectPlay8Server::DestroyClient函数即可。

Deletes a client from the session.

HRESULT DestroyClient(
const DPNID dpnidClient,
const VOID *const pDestroyInfo,
const DWORD dwDestroyInfoSize,
const DWORD dwFlags
);


Parameters

dpnidClient

[in] Variable of type
DPNID that specifies the identifier of the client to delete.

pDestroyInfo

[in] Pointer that describes additional delete data information.

dwDestroyInfoSize

[in] Variable of type
DWORD that specifies the size of the data in the
pDestroyInfo parameter.

dwFlags

[in] Reserved. Must be 0.

Return Values

Returns S_OK if successful, or one of the following error values.

DPNERR_INVALIDPARAM
DPNERR_INVALIDPLAYER
DPNERR_NOTHOST
以下代码示例了如何处理消息DPN_MSGID_DESTROY_PLAYER。

//
application variables

struct
PLAYER_INFO
{
DPNID player_id;
//
DirectPlay Player ID

char
name[
26
];
//
Player Name

PLAYER_INFO() { player_id
=

0
; }
};

//
window handles, class.

HWND g_hwnd;

char
g_class_name[]
=

"
ServerClass
"
;

//
application GUID

GUID g_app_guid
=
{
0xababbe60
,
0x1ac0
,
0x11d5
, {
0x90
,
0x89
,
0x44
,
0x45
,
0x53
,
0x54
,
0x0
,
0x1
} };

IDirectPlay8Server
*
g_dp_server;
//
DirectPlay Server

DPN_SERVICE_PROVIDER_INFO
*
g_adapter_list;
//
adapters

DWORD g_num_adapters;
//
number of adapters

PLAYER_INFO g_player_info[
256
];
//
player information

BOOL g_is_hosting;
//
flag indicates whether host started or not

//
----------------------------------------------------------------------------------------

//
Callback function that receives all messages from the client, and receives indications

//
of session changes from the IDirectPlay8Client interface.

//
----------------------------------------------------------------------------------------

HRESULT WINAPI Net_Msg_Handle(PVOID user_context, DWORD message_id, PVOID msg_buffer)
{

DPNMSG_DESTROY_PLAYER
*
destroy_player;
//
contains information for the DPN_MSGID_DESTROY_PLAYER system message

DPN_PLAYER_INFO
*
dpn_player_info;
//
describes static player informaion

DPN_BUFFER_DESC buffer_desc;
//
used dy DirectPlay for generic buffer information

DPNHANDLE async_handle;
PLAYER_INFO
*
player_info;

int
index;
DWORD size;

char
message[
512
];
HRESULT rv;

switch
(message_id)
{

//
Microsoft DirectPlay generates the DPN_MSGID_DESTROY_PLAYER message when a player leaves a peer-to-peer

//
or client/server session.

case
DPN_MSGID_DESTROY_PLAYER:
destroy_player
=
(DPNMSG_DESTROY_PLAYER
*
) msg_buffer;

//
make sure it is not the host

if
((player_info
=
(PLAYER_INFO
*
) destroy_player
->
pvPlayerContext)
==
NULL)

break
;

//
remove the player from list

player_info
->
player_id
=

0
;

//
An application sends an LB_FINDSTRING message to find the first string in a list box that begins

//
with the specified string.

//

//
The return value is the index of the matching item, or LB_ERR if the search was unsuccessful.

//

//
wParam:

//
Specifies the zero-based index of the item before the first item to be searched.

//
When the search reaches the bottom of the list box, it continues searching from the top of the

//
list box back to the item specified by the wParam parameter. If wParam is –1, the entire list

//
box is searched from the beginning.

//

//
lParam:

//
Pointer to the null-terminated string that contains the string for which to search.

//
The search is case independent, so this string can contain any combination of uppercase and

//
lowercase letters.

index
=
(
int
) SendMessage(GetDlgItem(g_hwnd, IDC_USERS), LB_FINDSTRING,
-
1
, (LPARAM)player_info
->
name);

if
(index
!=
LB_ERR)

//
An application sends an LB_DELETESTRING message to delete a string in a list box.

//

//
wParam:

//
Specifies the zero-based index of the string to be deleted.

//
lParam:

//
This parameter is not used.

SendMessage(GetDlgItem(g_hwnd, IDC_USERS), LB_DELETESTRING, index,
0
);

//
send message to remaining players notifying player left

sprintf(message,
"
%s quit.
"
, player_info
->
name);
Send_Text_Msg(DPNID_ALL_PLAYERS_GROUP, message);

break
;
}

//
return S_OK to signify the message was handled OK.

return
S_OK;
}

接收数据

实际的游戏数据采取应用程序指定消息的形式,但总是封装在DPN_MSGID_RECEIVE消息类型中,使用DPNMSG_RECEIVE结构体。

The DPNMSG_RECEIVE structure contains information for the DPN_MSGID_RECEIVE system message.

typedef struct {
DWORD dwSize;
DPNID dpnidSender;
PVOID pvPlayerContext;
PBYTE pReceiveData;
DWORD dwReceiveDataSize;
DPNHANDLE hBufferHandle;
} DPNMSG_RECEIVE, *PDPNMSG_RECEIVE;


dwSize

Size of this structure.

dpnidSender

DPNID of the player that sent the message.

pvPlayerContext

Player context value of the player that sent the message.

pReceiveData

PBYTE pointer to the message data buffer. This buffer is normally only valid while the
DPN_MSGID_RECEIVE message is being processed by the callback message handler.

dwReceiveDataSize

Size of the data, in bytes, of the
pReceiveData member.

hBufferHandle

Buffer handle for the
pReceiveData member. If you have returned DPNSUCCESS_PENDING , pass this value to
ReturnBuffer to notify Microsoft® DirectPlay® to free the buffer.

Return Values

Return DPNSUCCESS_PENDING to transfer ownership of the data buffer to your application. Otherwise, return DPN_OK.

Remarks

Because you should not spend large amounts of time processing messages, you should copy the data, and process the message. Alternatively, you can return DPNSUCCESS_PENDING from the callback message handler. Doing so transfers ownership of the buffer to the application. If you return DPNSUCCESS_PENDING, you must call IDirectPlay8Peer::ReturnBuffer, IDirectPlay8Client::ReturnBuffer, or IDirectPlay8Server::ReturnBuffer when you are finished with the buffer. Pass the method the value you receive in the hBufferHandle member to identify the buffer. If you fail to call ReturnBuffer, you will create a memory leak.

以下代码示例了如何处理消息DPN_MSGID_RECEIVE。

IDirectPlay8Server
*
g_dp_server;
//
DirectPlay Server

//
----------------------------------------------------------------------------------------

//
Callback function that receives all messages from the client, and receives indications

//
of session changes from the IDirectPlay8Client interface.

//
----------------------------------------------------------------------------------------

HRESULT WINAPI Net_Msg_Handle(PVOID user_context, DWORD message_id, PVOID msg_buffer)
{
DPNMSG_RECEIVE
*
receive_data;
//
contains information for the DPN_MSGID_RECEIVE system message

DPN_BUFFER_DESC buffer_desc;
//
used dy DirectPlay for generic buffer information

DPNHANDLE async_handle;

switch
(message_id)
{

//
Microsoft DirectPlay generates the DPN_MSGID_RECEIVE message when a message has been processed by

//
the receiver.

case
DPN_MSGID_RECEIVE:
receive_data
=
(DPNMSG_RECEIVE
*
) msg_buffer;

//
forward message to all player except host

buffer_desc.dwBufferSize
=
receive_data
->
dwReceiveDataSize;
buffer_desc.pBufferData
=
receive_data
->
pReceiveData;

g_dp_server
->
SendTo(DPNID_ALL_PLAYERS_GROUP,
&
buffer_desc,
1
,
0
, NULL,
&
async_handle, DPNSEND_NOLOOPBACK);

break
;
}

//
return S_OK to signify the message was handled OK.

return
S_OK;
}

发送服务器端消息

如果网络不传输数据,它就一无是处。要让服务器端对象发送数据到一个已经连接的客户端,需要使用SendTo函数,该函数发送数据到单个玩家,或一次发送到所有玩家,或发送到属于某一特定组的所有玩家。

Transmits data to a client or group within the session. The message can be sent synchronously or asynchronously.

HRESULT SendTo(
const DPNID dpnid,
const DPN_BUFFER_DESC *const pBufferDesc,
const DWORD cBufferDesc,
const DWORD dwTimeOut,
void *const pvAsyncContext,
DPNHANDLE *const phAsyncHandle,
const DWORD dwFlags
);


Parameters

dpnid

[in] Identifier of the client or group to receive data. Set this parameter to DPNID_ALL_PLAYERS_GROUP to send a message to all players in the session.

pBufferDesc

[in] Pointer to a
DPN_BUFFER_DESC structure that describes the data to send.

cBufferDesc

[in] Number of
DPN_BUFFER_DESC structures pointed to by
pBufferDesc. There can be only one buffer in this version of Microsoft® DirectPlay® .

dwTimeOut

[in] Number of milliseconds to wait for the message to send. If the message has not been sent by the
dwTimeOut value, it is deleted from the send queue. If you set this parameter to 0, the message remains in the send queue until it is sent or until the link is dropped.

pvAsyncContext

[in] Pointer to the user-supplied context, which is returned in the
pvUserContext member of the
DPN_MSGID_SEND_COMPLETE system message.

phAsyncHandle

[out] A
DPNHANDLE.
When the method returns,
phAsyncHandle will point to a handle that you can pass to
IDirectPlay8Server::CancelAsyncOperation to cancel the operation. This parameter must be set to NULL if you set the DPNSEND_SYNC flag in
dwFlags.

dwFlags

[in] Flags that describe send behavior. You can set one or more of the following flags.

DPNSEND_SYNC

Process the
SendTo request synchronously.

DPNSEND_NOCOPY

Use the data in the
DPN_BUFFER_DESC structure and do not make an internal copy. This may be a more efficient method of sending data. However, it is less robust because the sender might be able to modify the message before the receiver has processed it. This flag cannot be used with DPNSEND_NOCOMPLETE.

DPNSEND_NOCOMPLETE

Do not send the
DPN_MSGID_SEND_COMPLETE structure to the message handler. This flag may not be used with DPNSEND_NOCOPY or DPNSEND_GUARANTEED. Additionally, when using this flag
pvAsyncContext must be NULL.

DPNSEND_COMPLETEONPROCESS

Send the
DPN_MSGID_SEND_COMPLETE to the message handler when this message has been delivered to the target and the target's message handler returns from indicating its reception. There is additional internal message overhead when this flag is set, and the message transmission process may become significantly slower. If you set this flag, DPNSEND_GUARANTEED must also be set.

DPNSEND_GUARANTEED

Send the message by a guaranteed method of delivery.

DPNSEND_PRIORITY_HIGH

Sets the priority of the message to high. This flag cannot be used with DPNSEND_PRIORITY_LOW.

DPNSEND_PRIORITY_LOW

Sets the priority of the message to low. This flag cannot be used with DPNSEND_PRIORITY_HIGH.

DPNSEND_NOLOOPBACK

Suppress the
DPN_MSGID_RECEIVE system message to your message handler when you are sending to a group that includes the local player. For example, this flag is useful if you are broadcasting to the entire session.

DPNSEND_NONSEQUENTIAL

If this flag is set, the target application will receive the messages in the order that they arrive at the user's computer. If this flag is not set, messages are delivered sequentially, and will be received by the target application in the order that they were sent. Doing so may require buffering incoming messages until missing messages arrive.

Return Values

Returns S_OK if this method is processed synchronously and is successful. By default, this method is run asynchronously and generally returns DPNSUCCESS_PENDING or one of the following error values.

DPNERR_CONNECTIONLOST
DPNERR_INVALIDFLAGS
DPNERR_INVALIDPARAM
DPNERR_INVALIDPLAYER
DPNERR_TIMEDOUT

Remarks

This method generates a DPN_MSGID_RECEIVE system message in the receiver's message handler. The data is contained in the pReceiveData member of the associated structure.

Messages can have one of three priorities: low, normal, and high. To specify a low or high priority for the message set the appropriate flag in dwFlags. If neither of the priority flags is set, the message will have normal priority. See Basic Networking for a discussion of send priorities.

When the SendTo request is completed, a DPN_MSGID_SEND_COMPLETE system message is posted to the sender's message handler. The success or failure of the request is contained in the hResultCode member of the associated structure. You can suppress the send completion by setting the DPNSEND_NOCOMPLETE flag in dwflags.

Send completions are typically posted on the source computer as soon as the message is sent. In other words, a send completion does not necessarily mean that the message has been processed on the target. It may still be in a queue. If you want to be certain that the message has been processed by the target, set the DPNSEND_COMPLETEONPROCESS flag in dwFlags. This flag ensures that the send completion will not be sent until the target's message handler has processed the message, and returned.

Note Do not assume that resources such as the data buffer will remain valid until the method has returned. If you call this method asynchronously, the DPN_MSGID_SEND_COMPLETE message may be received and processed by your message handler before the call has returned. If your message handler deallocates or otherwise invalidates a resource such as the data buffer, that resource may become invalid at any time after the method has been called.

下面这个函数发送消息数据到指定的玩家ID组。

//
--------------------------------------------------------------------------------

//
Send text to all clients which specified by player_id.

//
--------------------------------------------------------------------------------

void
Send_Text_Msg(DPNID player_id,
char
*
text)
{
DPNHANDLE async_handle;
DPN_BUFFER_DESC buffer_desc;

if
(g_dp_server
==
NULL)

return
;

//
build a data structure

buffer_desc.dwBufferSize
=
(DWORD) (strlen(text)
+

1
);
buffer_desc.pBufferData
=
(BYTE
*
) text;

//
Send message (async method - reason for handle)

//

//
Transmits data to a client or group within the session.

//
The message can be sent synchronously or asynchronously.

g_dp_server
->
SendTo(player_id,
&
buffer_desc,
1
,
0
, NULL,
&
async_handle, DPNSEND_NOLOOPBACK);
}

结束主持会话

一旦服务器端完成了工作,就是停止会话的时候了,停止会话也就是停止所有传输并销毁所有玩家,这可以通过IDirectPlay8Server::Close来完成。因为该函数采用同步方式工作,所以在所有传输完成以及所有连接关闭之前不会返回,这就保证了无须有任何顾虑,就能关闭应用程序。

Closes the open connection to a session.

HRESULT Close(
const DWORD dwFlags
);


Parameters

dwFlags

[in] Reserved. Must be 0.

Return Values

Returns S_OK if successful, or the following error value.

DPNERR_UNINITIALIZED

Remarks

This method must be called on any object successfully initialized with IDirectPlay8Server::Initialize.

This method is a counterpart to IDirectPlay8Server::Host. It closes all active network connections hosted by the server. This method is synchronous, and will not return until the server has processed all DPN_MSGID_DESTROY_PLAYER messages. This feature guarantees that when Close returns, you can safely shut down the server application.

Calling Close will cancel all outstanding operations, including data sent as guaranteed. To make sure all messages are sent, wait for all outstanding SendTo calls to complete before calling Close.

以下给出一个完整的服务器端代码示例:

点击下载源码和工程

/*
**************************************************************************************
PURPOSE:
Server Network Demo
**************************************************************************************
*/

#include
<
windows.h
>

#include
<
stdio.h
>

#include
<
dplay8.h
>

#include
<
dpaddr.h
>

#include
"
resource.h
"

#pragma comment(lib,
"
dxguid.lib
"
)
#pragma comment(lib,
"
dplayx.lib
"
)

#pragma warning(disable :
4996
)

#define
Safe_Release(p) if((p)) (p)->Release();

//
application variables

struct
PLAYER_INFO
{
DPNID player_id;
//
DirectPlay Player ID

char
name[
26
];
//
Player Name

PLAYER_INFO() { player_id
=

0
; }
};

//
window handles, class.

HWND g_hwnd;

char
g_class_name[]
=

"
ServerClass
"
;

//
application GUID

GUID g_app_guid
=
{
0xababbe60
,
0x1ac0
,
0x11d5
, {
0x90
,
0x89
,
0x44
,
0x45
,
0x53
,
0x54
,
0x0
,
0x1
} };

IDirectPlay8Server
*
g_dp_server;
//
DirectPlay Server

DPN_SERVICE_PROVIDER_INFO
*
g_adapter_list;
//
adapters

DWORD g_num_adapters;
//
number of adapters

PLAYER_INFO g_player_info[
256
];
//
player information

BOOL g_is_hosting;
//
flag indicates whether host started or not

//
--------------------------------------------------------------------------------

//
Send text to all clients which specified by player_id.

//
--------------------------------------------------------------------------------

void
Send_Text_Msg(DPNID player_id,
char
*
text)
{
DPNHANDLE async_handle;
DPN_BUFFER_DESC buffer_desc;

if
(g_dp_server
==
NULL)

return
;

//
build a data structure

buffer_desc.dwBufferSize
=
(DWORD) (strlen(text)
+

1
);
buffer_desc.pBufferData
=
(BYTE
*
) text;

//
Send message (async method - reason for handle)

//

//
Transmits data to a client or group within the session.

//
The message can be sent synchronously or asynchronously.

g_dp_server
->
SendTo(player_id,
&
buffer_desc,
1
,
0
, NULL,
&
async_handle, DPNSEND_NOLOOPBACK);
}

//
----------------------------------------------------------------------------------------

//
Callback function that receives all messages from the client, and receives indications

//
of session changes from the IDirectPlay8Client interface.

//
----------------------------------------------------------------------------------------

HRESULT WINAPI Net_Msg_Handle(PVOID user_context, DWORD message_id, PVOID msg_buffer)
{
DPNMSG_CREATE_PLAYER
*
create_player;
//
contains information for the DPN_MSGID_CREATE_PLAYER system message

DPNMSG_DESTROY_PLAYER
*
destroy_player;
//
contains information for the DPN_MSGID_DESTROY_PLAYER system message

DPNMSG_RECEIVE
*
receive_data;
//
contains information for the DPN_MSGID_RECEIVE system message

DPN_PLAYER_INFO
*
dpn_player_info;
//
describes static player informaion

DPN_BUFFER_DESC buffer_desc;
//
used dy DirectPlay for generic buffer information

DPNHANDLE async_handle;
PLAYER_INFO
*
player_info;

int
index;
DWORD size;

char
message[
512
];
HRESULT rv;

switch
(message_id)
{

//
Microsoft DirectPlay generates the DPN_MSGID_CREATE_PLAYER message when a player is added to a

//
peer-to-peer or client/server session.

case
DPN_MSGID_CREATE_PLAYER:
create_player
=
(DPNMSG_CREATE_PLAYER
*
) msg_buffer;

//
get player name and save it

size
=

0
;
dpn_player_info
=
NULL;

//
Retrieves the client information set for the specified client

rv
=
g_dp_server
->
GetClientInfo(create_player
->
dpnidPlayer, dpn_player_info,
&
size,
0
);

if
(FAILED(rv)
&&
rv
!=
DPNERR_BUFFERTOOSMALL)
{

//
skip this if this is a host player

if
(rv
==
DPNERR_INVALIDPLAYER)

break
;

return
E_FAIL;
}

if
((dpn_player_info
=
(DPN_PLAYER_INFO
*
)
new
BYTE[size])
==
NULL)

return
E_FAIL;

ZeroMemory(dpn_player_info, size);
dpn_player_info
->
dwSize
=

sizeof
(DPN_PLAYER_INFO);

//
retrieves the client information set again

if
(FAILED(g_dp_server
->
GetClientInfo(create_player
->
dpnidPlayer, dpn_player_info,
&
size,
0
)))
{
delete[] dpn_player_info;

return
E_FAIL;
}

//
Find an empty player structure to use

index
=

-
1
;

for
(
int
i
=

0
; i
<

256
; i
++
)
{

if
(g_player_info[i].player_id
==

0
)
{
index
=
i;

break
;
}
}

if
(index
==

-
1
)
{
delete[] dpn_player_info;

return
E_FAIL;
}

//
set player context pointer

create_player
->
pvPlayerContext
=
(
void
*
)
&
g_player_info[index];

//
save player ID

g_player_info[index].player_id
=
create_player
->
dpnidPlayer;

wcstombs(g_player_info[index].name, dpn_player_info
->
pwszName,
256
);

//
add player to list

SendMessage(GetDlgItem(g_hwnd, IDC_USERS), LB_ADDSTRING,
0
, (LPARAM) g_player_info[index].name);

//
send a message to all players notifying someone joined

sprintf(message,
"
%s joined!
"
, g_player_info[index].name);
Send_Text_Msg(DPNID_ALL_PLAYERS_GROUP, message);

delete[] dpn_player_info;

break
;

//
Microsoft DirectPlay generates the DPN_MSGID_DESTROY_PLAYER message when a player leaves a peer-to-peer

//
or client/server session.

case
DPN_MSGID_DESTROY_PLAYER:
destroy_player
=
(DPNMSG_DESTROY_PLAYER
*
) msg_buffer;

//
make sure it is not the host

if
((player_info
=
(PLAYER_INFO
*
) destroy_player
->
pvPlayerContext)
==
NULL)

break
;

//
remove the player from list

player_info
->
player_id
=

0
;

//
An application sends an LB_FINDSTRING message to find the first string in a list box that begins

//
with the specified string.

//

//
The return value is the index of the matching item, or LB_ERR if the search was unsuccessful.

//

//
wParam:

//
Specifies the zero-based index of the item before the first item to be searched.

//
When the search reaches the bottom of the list box, it continues searching from the top of the

//
list box back to the item specified by the wParam parameter. If wParam is –1, the entire list

//
box is searched from the beginning.

//

//
lParam:

//
Pointer to the null-terminated string that contains the string for which to search.

//
The search is case independent, so this string can contain any combination of uppercase and

//
lowercase letters.

index
=
(
int
) SendMessage(GetDlgItem(g_hwnd, IDC_USERS), LB_FINDSTRING,
-
1
, (LPARAM)player_info
->
name);

if
(index
!=
LB_ERR)

//
An application sends an LB_DELETESTRING message to delete a string in a list box.

//

//
wParam:

//
Specifies the zero-based index of the string to be deleted.

//
lParam:

//
This parameter is not used.

SendMessage(GetDlgItem(g_hwnd, IDC_USERS), LB_DELETESTRING, index,
0
);

//
send message to remaining players notifying player left

sprintf(message,
"
%s quit.
"
, player_info
->
name);
Send_Text_Msg(DPNID_ALL_PLAYERS_GROUP, message);

break
;

//
Microsoft DirectPlay generates the DPN_MSGID_RECEIVE message when a message has been processed by

//
the receiver.

case
DPN_MSGID_RECEIVE:
receive_data
=
(DPNMSG_RECEIVE
*
) msg_buffer;

//
forward message to all player except host

buffer_desc.dwBufferSize
=
receive_data
->
dwReceiveDataSize;
buffer_desc.pBufferData
=
receive_data
->
pReceiveData;

g_dp_server
->
SendTo(DPNID_ALL_PLAYERS_GROUP,
&
buffer_desc,
1
,
0
, NULL,
&
async_handle, DPNSEND_NOLOOPBACK);

break
;
}

//
return S_OK to signify the message was handled OK.

return
S_OK;
}

//
--------------------------------------------------------------------------------

//
Create DirectPlay Server and initialize it.

//
--------------------------------------------------------------------------------

BOOL Init_DirectPlay_Server()
{

//
create DirectPlay Server component

if
(FAILED(CoCreateInstance(CLSID_DirectPlay8Server, NULL, CLSCTX_INPROC, IID_IDirectPlay8Server,
(
void
**
)
&
g_dp_server)))

return
FALSE;

//
Assign a message handler to network component

//

//
Registers an entry point in the client's code that receives the messages from the IDirectPlay8Client

//
interface and from the server.

if
(FAILED(g_dp_server
->
Initialize(NULL, Net_Msg_Handle,
0
)))

return
FALSE;

return
TRUE;
}

//
--------------------------------------------------------------------------------

//
Enumerate all TCP/IP adapters.

//
--------------------------------------------------------------------------------

void
Enum_Adapters()
{

//
return if no server object or GUID

if
(g_dp_server
==
NULL)

return
;

//
get a handle of the list box

HWND adapters_ctrl
=
GetDlgItem(g_hwnd, IDC_ADAPTERS);

//
clear the list box

SendMessage(adapters_ctrl, LB_RESETCONTENT,
0
,
0
);

//
free prior adapter list

delete[] g_adapter_list;
g_adapter_list
=
NULL;

g_num_adapters
=

0
;

DWORD adapter_list_size
=

0
;

//
query the required size of the data buffer

HRESULT rv
=
g_dp_server
->
EnumServiceProviders(
&
CLSID_DP8SP_TCPIP, NULL, g_adapter_list,
&
adapter_list_size,

&
g_num_adapters,
0
);

if
(rv
!=
DPNERR_BUFFERTOOSMALL)

return
;

//
allocate a buffer

if
((g_adapter_list
=
(DPN_SERVICE_PROVIDER_INFO
*
)
new
BYTE[adapter_list_size])
==
NULL)

return
;

//
enumerate again

if
(SUCCEEDED(g_dp_server
->
EnumServiceProviders(
&
CLSID_DP8SP_TCPIP, NULL, g_adapter_list,
&
adapter_list_size,

&
g_num_adapters,
0
)))
{

char
adapter_name[
1024
];

//
enumeration is complete, scan through entries.

DPN_SERVICE_PROVIDER_INFO
*
adapter_ptr
=
g_adapter_list;

for
(DWORD i
=

0
; i
<
g_num_adapters; i
++
)
{

//
convert wide string into multi-byte string

wcstombs(adapter_name, adapter_ptr
->
pwszName,
1024
);

//
add the adapter name int listbox

SendMessage(adapters_ctrl, CB_ADDSTRING,
0
, (LPARAM)adapter_name);

//
go to next servicec provider

adapter_ptr
++
;
}
}

//
Select first adapter

//

//
An application sends a CB_SETCURSEL message to select a string in the list of a combo box.

//
If necessary, the list scrolls the string into view. The text in the edit control of the combo box

//
changes to reflect the new selection, and any previous selection in the list is removed.

//

//
wParam:

//
Specifies the zero-based index of the string to select. If this parameter is –1, any current selection

//
in the list is removed and the edit control is cleared.

//

//
lParam:

//
This parameter is not used.

SendMessage(adapters_ctrl, CB_SETCURSEL,
0
,
0
);
}

//
--------------------------------------------------------------------------------

//
Release all resource which allocated for DirectPlay.

//
--------------------------------------------------------------------------------

void
Release_DirectPlay()
{

//
release client service provider list memory

delete[] g_adapter_list;
g_adapter_list
=
NULL;

g_num_adapters
=

0
;

//
release client component

if
(g_dp_server
!=
NULL)
{

//
Closes the open connection to a session. This method must be called on any object that is successfully

//
initialized with a call to the IDirectPlay8Client::Initialize method.

g_dp_server
->
Close(
0
);

g_dp_server
->
Release();

g_dp_server
=
NULL;
}
}

//
--------------------------------------------------------------------------------

//
Start hosting a server which GUID specified by adapter_guid.

//
--------------------------------------------------------------------------------

BOOL Start_Session(GUID
*
adapter_guid)
{

//
Make sure there's an adapter

if
(adapter_guid
==
NULL)

return
FALSE;

//
Need to re-assign a network handler as quitting a previous session to clears it.

//
Close the connection first before assigning a new network handler.

//

//
Closes the open connnection to a session.

g_dp_server
->
Close(
0
);

//
Initialize DirectPlay Server

if
(FAILED(g_dp_server
->
Initialize(NULL, Net_Msg_Handle,
0
)))

return
FALSE;

IDirectPlay8Address
*
dp_address;

//
Create an address object and fill it with information

if
(FAILED(CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC, IID_IDirectPlay8Address,
(
void
**
)
&
dp_address)))

return
FALSE;

//
Set the protocol to TCP/IP

//

//
Sets the service provider GUID in the address object.

//
If a service provider is specified for this address, it is overwrittern by this call.

if
(FAILED(dp_address
->
SetSP(
&
CLSID_DP8SP_TCPIP)))
{
dp_address
->
Release();

return
FALSE;
}

//
Set the port

DWORD port
=

21234
;

//
Adds a component to the address.

//
If the component is part of the address, it is replaced by the new value in this call.

//
Set port

if
(FAILED(dp_address
->
AddComponent(DPNA_KEY_PORT,
&
port,
sizeof
(DWORD), DPNA_DATATYPE_DWORD)))
{
dp_address
->
Release();

return
FALSE;
}

//
Set the adapter

if
(FAILED(dp_address
->
AddComponent(DPNA_KEY_DEVICE, adapter_guid,
sizeof
(GUID), DPNA_DATATYPE_GUID)))
{
dp_address
->
Release();

return
FALSE;
}

DPN_APPLICATION_DESC app_desc;
//
Describes the settings for a Microsoft DirectPlay application

//
Setup the application description structure

ZeroMemory(
&
app_desc,
sizeof
(DPN_APPLICATION_DESC));

app_desc.dwSize
=

sizeof
(DPN_APPLICATION_DESC);
app_desc.dwFlags
=
DPNSESSION_CLIENT_SERVER;
app_desc.guidApplication
=
g_app_guid;
app_desc.pwszSessionName
=
L
"
SercverSession
"
;
app_desc.dwMaxPlayers
=

256
;

//
Start hosting

//

//
Creates a new client/server session, hosted by the local computer.

if
(FAILED(g_dp_server
->
Host(
&
app_desc,
&
dp_address,
1
, NULL, NULL, NULL,
0
)))
{
dp_address
->
Release();

return
FALSE;
}

//
Release the address component

dp_address
->
Release();

//
Disables combo-box control.

//

//
Enables or disables mouse and keyboard input to the specified window or control.

//
When input is disabled, the window does not receive input such as mouse clicks and key presses.

//
When input is enabled, the window receives all input.

EnableWindow(GetDlgItem(g_hwnd, IDC_ADAPTERS), FALSE);

//
Setup the dialog information

SetWindowText(GetDlgItem(g_hwnd, IDC_STARTSTOP),
"
Stop
"
);

g_is_hosting
=
TRUE;

return
TRUE;
}

//
--------------------------------------------------------------------------------

//
Stoping hosting server.

//
--------------------------------------------------------------------------------

void
Stop_Session()
{

//
Close the connection

if
(g_dp_server)
g_dp_server
->
Close(
0
);

//
Enables combo-box control

EnableWindow(GetDlgItem(g_hwnd, IDC_ADAPTERS), TRUE);

//
Setup the dialog information

SetWindowText(GetDlgItem(g_hwnd, IDC_STARTSTOP),
"
Start
"
);

g_is_hosting
=
FALSE;
}

//
--------------------------------------------------------------------------------

//
Window procedure.

//
--------------------------------------------------------------------------------

long
WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
unsigned
int
selected;

char
name[
256
];
DPN_SERVICE_PROVIDER_INFO
*
adapter_ptr;

switch
(msg)
{

case
WM_COMMAND:

//
The WM_COMMAND message is sent when the user selects a command item from a menu, when a control sends a

//
notification message to its parent window, or when an accelerator keystroke is translated.

//

//
wParam:

//
The high-order word specifies the notification code if the message is from a control.

//
If the message is from an accelerator, this value is 1. If the message is from a menu, this value is zero.

//
The low-order word specifies the identifier of the menu item, control, or accelerator.

//

//
lParam:

//
Handle to the control sending the message if the message is from a control.

//
Otherwise, this parameter is NULL.

switch
(LOWORD(wParam))
{

case
IDC_STARTSTOP:

if
(
!
g_is_hosting)
{

//
Get adapter to use

if
((selected
=
(
int
) SendMessage(GetDlgItem(hwnd, IDC_ADAPTERS), CB_GETCURSEL,
0
,
0
))
==
LB_ERR)

//
invalid selected item

break
;

//
Make sure it's valid and start session

if
(selected
<
g_num_adapters)
{

//
pointer adapter pointer to selected position

adapter_ptr
=
g_adapter_list;
adapter_ptr
+=
selected;

if
(
!
Start_Session(
&
adapter_ptr
->
guid))
MessageBox(hwnd,
"
Unable to start server!
"
,
"
Error
"
, MB_OK
|
MB_ICONEXCLAMATION);
}
}

else

Stop_Session();

break
;

case
IDC_DISCONNECT:

//
Make sure we're hosting

if
(g_is_hosting)
{

//
Get user from list to disconnect

selected
=
(
int
) SendMessage(GetDlgItem(hwnd, IDC_USERS), LB_GETCURSEL,
0
,
0
);

if
(selected
!=
LB_ERR)
{

//
Find the player in list

//

//
An application sends an LB_GETTEXT message to retrieve a string from a list box.

//

//
wParam:

//
Specifies the zero-based index of the string to retrieve.

//

//
lParam:

//
Pointer to the buffer that will receive the string; it is type LPTSTR which is subsequently

//
cast to an LPARAM. The buffer must have sufficient space for the string and a terminating

//
null character. An LB_GETTEXTLEN message can be sent before the LB_GETTEXT message to

//
retrieve the length, in TCHARs, of the string.

SendMessage(GetDlgItem(hwnd, IDC_USERS), LB_GETTEXT, selected, (LPARAM)name);

for
(unsigned
int
i
=

0
; i
<

256
; i
++
)
{

if
(g_player_info[i].player_id
!=

0

&&

!
strcmp(g_player_info[i].name, name))
{

//
Disconnect them

//

//
Deletes a client from the session

g_dp_server
->
DestroyClient(g_player_info[i].player_id, NULL,
0
,
0
);

break
;
}
}
}
}

break
;
}
//
end - case WM_COMMAND:

break
;

case
WM_DESTROY:
Stop_Session();
Release_DirectPlay();
PostQuitMessage(
0
);

break
;

default
:

return
(
long
) DefWindowProc(hwnd, msg, wParam, lParam);
}

return

0
;
}

//
--------------------------------------------------------------------------------

//
Main function, routine entry.

//
--------------------------------------------------------------------------------

int
WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line,
int
cmd_show)
{
WNDCLASS win_class;
MSG msg;

//
create window class and register it

win_class.style
=
CS_HREDRAW
|
CS_VREDRAW;
win_class.lpfnWndProc
=
Window_Proc;
win_class.cbClsExtra
=

0
;
win_class.cbWndExtra
=
DLGWINDOWEXTRA;
win_class.hInstance
=
inst;
win_class.hIcon
=
LoadIcon(inst, IDI_APPLICATION);
win_class.hCursor
=
LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground
=
(HBRUSH) (COLOR_BTNFACE
+

1
);
win_class.lpszMenuName
=
NULL;
win_class.lpszClassName
=
g_class_name;

if
(
!
RegisterClass(
&
win_class))

return
FALSE;

//
create the main window

g_hwnd
=
CreateDialog(inst, MAKEINTRESOURCE(IDD_SERVER),
0
, NULL);

//
initialize COM

//

//
initialize the COM library on the current thread and identifies the concurrency model as single-thread

//
apartment (STA).

CoInitialize(
0
);

//
Initialzie DirectPlay and enumerate service providers.

if
(
!
Init_DirectPlay_Server())
{
MessageBox(NULL,
"
Error initializing DirectPlay.
"
,
"
ERROR
"
, MB_OK
|
MB_ICONEXCLAMATION);

goto
exit;
}

//
enumerate all TCP/IP adapters

Enum_Adapters();

//
Make sure there's an adapter to use

if
(g_num_adapters
==

0
)
{
MessageBox(g_hwnd,
"
There is no TCP/IP adapters to use!
"
,
"
ERROR
"
, MB_OK
|
MB_ICONEXCLAMATION);

goto
exit;
}

ShowWindow(g_hwnd, cmd_show);
UpdateWindow(g_hwnd);

//
start message pump, waiting for signal to quit.

ZeroMemory(
&
msg,
sizeof
(MSG));

while
(msg.message
!=
WM_QUIT)
{

if
(PeekMessage(
&
msg, NULL,
0
,
0
, PM_REMOVE))
{
TranslateMessage(
&
msg);
DispatchMessage(
&
msg);
}
}

exit:
UnregisterClass(g_class_name, inst);

//
release COM system

//

//
Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other

//
resources that the thread maintains, and forces all RPC connections on the thread to close.

CoUninitialize();

return
(
int
) msg.wParam;
}

运行截图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: