Client and Server (DirectX)
Client and Server Client and Server
(DirectX) (DirectX)
Vishnu
Vishnu Kotrajaras Kotrajaras
Server scalability
• Your game can handle more players at a time (Over internet, most peer-to- peer can only handle about 6 players)
• All depend on server power
• Too many people-> just upgrade
server (client does not have to do
much configuration)
Firewalls
• Peer-to-peer needs many holes in the firewall, but firewalls allow only a few ports to open
• Client/server only needs one port open
Simplicity
• Clients only worry about themselves
• Server can be left with a simple interface
• Server can run on many OS
IDirectPlay8Server Interface
• Initialize COM with CoInitialize()
• Then it must be created with CoCreateInstance()
• The methods are like what you ’ ve seen for peers
IDirectPlay8Server functions (1)
• Initialize- sets up the interface to receive messages for connections
• Close- disconnects and closes the server from the current session
• Host- creates the session in which
clients can connect
IDirectPlay8Server functions (2)
• SendTo- transmits data to one or many players within the session
• EnumServiceProviders- enumerates service providers available to the server
• SetApplicationDesc- changes particular application settings for an existing session
• CreateGroup- creates a new group to which players can be added
• DestroyGroup
IDirectPlay8Server functions (3)
• AddPlayerToGroup
• RemovePlayerFromGroup
• GetGroupInfo
• GetClientInfo
• DestroyClient
• GetClientAddress- get DPlay address
IDirectPlay8Server functions (4)
• GetLocalHostAddresses- get DPlay address used to host the local server
• GetSendQueueInfo- monitors the send message queue for a particular client
• GetConnectionInfo- gets info about the connection with a particular client
• GetApplicationDesc
After CoCreateInstance() of server
• Init IDirectPlay8Server Message Handler (all messages will be sent here)
HRESULT Initialize(
PVOID Const pvUserContext, PFNDPNMESSAGEHANDLER pfn, DWORD dwFlags
);
Pointer to message handler
After Initialize()
• Set up device address (for the host)
• Set the address ’ service provider
• The code is very similar to the code for peers
hosting
1. Set up the server info with SetServerInfo()
2. Set up application description with the DPN_APPLICATION_DESC data
structure
3. Set up the address to handle things with AddComponent()
4. Call the Host() function
SetServerInfo()
HRESULT SetServerInfo(
const DPN_PLAYER_INFO *const pdpnPlayerInfo, PVOID const pvAsynContext,
DPNHANDLE *const phAsyncHandle, const DWORD dwFlags
)
Server info, e.g.name
User context (opt)
DPNSETSERVERINFO_SYNC ->process synchronously
//// Setup our player information
//DXUtil_ConvertGenericStringToWide( wszServerName, "DPChat Server" );
ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
dpPlayerInfo.pwszName = wszServerName;
// Set us up to be non-asynchronous if( FAILED( hReturn = g_pDPServer-
>SetServerInfo( &dpPlayerInfo, NULL, NULL, DPNSETSERVERINFO_SYNC ) ) ) {
MessageBox( hWindow, "Failed to SetServerInfo()", "Unknown Error", MB_ICONERROR );
return -1;
Set up
DPN_APPLICATION_DESC
• Use just like you used peers, but dwFlags change to
DPNSESSION_CLIENT_SERVER
// Setup the application description
DXUtil_ConvertGenericStringToWide( wszSessionName, "DPChat Session" );
ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
dnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
dnAppDesc.guidApplication = GUID_CHATSERVER;
dnAppDesc.pwszSessionName = wszSessionName;
dnAppDesc.dwMaxPlayers = MAX_PLAYERS;
dnAppDesc.dwFlags = DPNSESSION_CLIENT_SERVER;
Set up the address
• Need to add server’s host port number
• Clients communicate through here //// Add port number to host address //hReturn = g_pDeviceAddress-
>AddComponent(DPNA_KEY_PORT,&dwPort,sizeof(DWORD),DPNA_
DATATYPE_DWORD);
if( hReturn != S_OK ) {
MessageBox( hWindow, "Failed to AddComponent()",
"hrHostGame()", MB_ICONERROR );
return -1;
}
Tell the port number
Host()
HRESULT Host(
const DPN_APPLICATION_DESC *const pdnAppDesc, IDirectPlay8Address **const prgDeviceInfo,
const DWORD cDeviceInfo,
const DPN_SECURITY_DESC *const pdpSecurity,
const DPN_SECURITY_CREDENTIALS *const pdpCredentials, VOID *const pvPlayerContext,
const DWORD dwFlags );
The app.desc you created
Device you just added port number How many devices in the second parameter
Not used by DirectX8 Optional
user context Hosting flags
hReturn = g_pDPServer->Host(
&dnAppDesc,
&g_pDeviceAddress, 1,
NULL,
NULL, NULL, NULL );
if( FAILED( hReturn ) ) {
MessageBox( hWindow, "Failed to Host()", "DirectPlay Error", MB_ICONERROR );
return -1;
}
Managing Players
• When a player connects to your game, he passes you a creation message
• You need to add him to your table of
active players
HRESULT hrCreatePlayer( PVOID pvUserContext, PVOID pMsgBuffer )
{ HRESULT hReturn = S_OK;
PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
char strName[256];
char szOutput[256];
DWORD dwSize = 0;
DPN_PLAYER_INFO *pdpPlayerInfo = NULL;
int i;
// Get a Create Message pointer to the buffer pCreatePlayerMsg =
(PDPNMSG_CREATE_PLAYER)pMsgBuffer;
// Get the peer info and extract its name
hReturn = g_pDPServer->GetClientInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
if( FAILED(hReturn) && hReturn != DPNERR_BUFFERTOOSMALL ) { if( hReturn == DPNERR_INVALIDPLAYER ) {
vShowText(hLB_Output,"Adding Ourselves");
}hReturn = -1;
}else {
pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
ZeroMemory( pdpPlayerInfo, dwSize );
pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
hReturn = g_pDPServer->GetClientInfo( pCreatePlayerMsg-
>dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
if( FAILED(hReturn) ) {
GET SIZE
HRESULT GetClientInfo(
const DPNID dpnid, // belongs to the client that we want info
DPN_PLAYER_INFO *const pdpnPlayerInfo, //this will hold the soon- to- //be-retrieved player info DWORD *const pdwSize, //size of the information retrieved.
//If the buffer size is too small,
//DPNERR_BUFFERTOOSMALL is returned const DWORD dwFlags //DPNINFO_NAME or DPNINFO_DATA );
vShowText(hLB_Output,"Error Getting Client Info");
hReturn = -1;
}else {
EnterCriticalSection( &g_csModifyPlayer );
// Convert player name to ANSI
DXUtil_ConvertWideStringToGeneric( strName, pdpPlayerInfo-
>pwszName );
// Add player to list
for( i = 0 ; i < MAX_PLAYERS ; i++ ) { if( !PlayerInfo[i].bActive ) {
PlayerInfo[i].bActive = 1;
PlayerInfo[i].dpnidPlayer = pCreatePlayerMsg-
>dpnidPlayer;
strcpy(PlayerInfo[i].szPlayerName,strName);
break;
} }
// Check if no free slot found
vShowText(hLB_Output,"No free slots in game!");
}// Check if we are adding ourselves else if( pdpPlayerInfo->dwPlayerFlags &
DPNPLAYER_LOCAL ) {
g_dpnidLocalPlayer = pCreatePlayerMsg-
>dpnidPlayer;
sprintf(szOutput,"<Slot%d> Added Ourselves",i);
vShowText(hLB_Output,szOutput);
}else {
sprintf(szOutput,"<Slot%d><%s> Is In The Game",i,strName);
vShowText(hLB_Output,szOutput);
}
SAFE_DELETE_ARRAY( pdpPlayerInfo );
// Update the number of active players in a thread safe way if( i != MAX_PLAYERS )
InterlockedIncrement( &g_lNumberOfActivePlayers );
LeaveCriticalSection( &g_csModifyPlayer );
} }
return hReturn;
}
Destroy Player
• Must check for
DPN_MSGID_DESTROY_PLAYER
• CALL hrDestroyPlayer() to process it
HRESULT hrDestroyPlayer( PVOID pvUserContext, PVOID pMsgBuffer )
{ PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
HRESULT hReturn = S_OK;
int i;
char szOutput[256];
// Get a Destroy Message pointer to the buffer pDestroyPlayerMsg =
(PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
// Update the number of active players in a thread safe way InterlockedDecrement( &g_lNumberOfActivePlayers );
EnterCriticalSection( &g_csModifyPlayer );
Use this, so that we can find the player’s ID
for( i = 0 ; i < MAX_PLAYERS ; i++ ) { if( PlayerInfo[i].bActive ) {
if( PlayerInfo[i].dpnidPlayer ==
pDestroyPlayerMsg->dpnidPlayer ) {
PlayerInfo[i].bActive = 0;
sprintf(szOutput,"<Slot%d><%s> Left The Game",i,PlayerInfo[i].szPlayerName);
vShowText(hLB_Output,szOutput);
break;
} } }
LeaveCriticalSection( &g_csModifyPlayer );
return(hReturn);
}
Receiving messages from client
case DPN_MSGID_RECEIVE:
{ PDPNMSG_RECEIVE pReceiveMsg;
PacketGeneric *PGen;
pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
PGen = (PacketGeneric*)pReceiveMsg->pReceiveData;
// Ignore the packet if we sent it originally
if( pReceiveMsg->dpnidSender == g_dpnidLocalPlayer ) break;
// If it is a chat packet, send it out to all of the players In LoginDirectPlayMessageHandler
DWORD dwType;
DWORD dwSize;
void *packet;
PacketChat *ChatMsg;
// Convert the packet to a void stream ChatMsg = (PacketChat*)pReceiveMsg-
>pReceiveData;
packet = (VOID*)ChatMsg;
// Send the chat packet to all clients hrSendServerMessage(DPNID_ALL_PLAYERS_GROUP,P ACKET_TYPE_CHAT,packet);
// Output it on our screen
vShowText(hLB_Output,ChatMsg-
>szText);
}break;
}
hrSendServerMessage()
HRESULT hrSendServerMessage(
int player, //slot number that you want to send the message //to. Use DPNID_ALL_PLAYERS_GROUP to //send message to all clients
DWORD dwMessageType, //message type, defined in //DPChatServer.h
PVOID pMsgBuffer //void pointer to the contents of the //message. Void pointer allows any type //of data for this parameter
HRESULT hrSendServerMessage( int player, DWORD dwMessageType, PVOID pMsgBuffer )
{ DPNHANDLE hAsync;
DWORD dwLength;
DPN_BUFFER_DESC bufferDesc;
PacketGeneric *PGen;
// Cast to a generic packet to get the size PGen = (PacketGeneric*)pMsgBuffer;
dwLength = PGen->dwSize;
if( dwLength == 0 ) return(0);
bufferDesc.dwBufferSize = dwLength;
bufferDesc.pBufferData = (BYTE*)pMsgBuffer;
// Broadcast it if player set to zero if( player == DPNID_ALL_PLAYERS_GROUP )
g_pDPServer->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1, 0, NULL,
&hAsync, DPNSEND_NOLOOPBACK );
else g_pDPServer->SendTo( PlayerInfo[player].dpnidPlayer, &bufferDesc, 1, 0, NULL, &hAsync, 0 );
return S_OK;
}
Exit because we don’t want to send empty message
Set up buffer
SendTo()
HRESULT SendTo(
const DPNID dpnid, //client you want to send to const DPN_BUFFER_DESC *const pBufferDesc,
//this is the actual message data const DWORD cBufferDesc, //how many msg buffers to
//send with this call
const DWORD dwTimeOut, //time out, set to 0 if you want //to keep sending the lost message void *const pvAsyncContext, //NULL
DPNHANDLE *const phAsyncHandle, //can use it to cancel send //but if we set it to
//DPNSEND_SYNC, this value //must be null
const DWORD dwFlags // SEE next page
SendTo -> flags (1)
• DPNSEND_GUARANTEED – Guarantees message delivery
• DPNSEND_NOLOOPBACK
– Keeps the server from receiving its own messages
• DPNSEND_PRIORITY_LOW
– Queues the message with low priority
• DPNSEND_PRIORITY_HIGH
– Queues the message with high priority
SendTo -> flags (2)
• DPNSEND_NOCOPY
– Keeps DirectPlay from making a copy of the
DPN_BUFFER_DESC before sending the message. You cannot use DPNSEND_NOCOMPLETE with this
• DPNSEND_SYNC
– Process the send synchronously
• DPNSEND_NOCOMPLETE
– Keeps DPN_MSGID_SEND_COMPLETE message from being generated
– Cannot use DPNSEND_NOCOPY and
DPNSEND_GUARANTEED flags with this one, and the
SendTo -> flags (3)
• DPNSEND_NONSEQUENTIAL
– Lets messages arrive at the client’s computer in any possible order -> only use this flag for non-critical packets
• DPNSEND_COMPLETEONPROCESS – Causes DirectPlay to send a
DPN_MSGID_SEND_COMPLETE message upon completion of a message being received by the client. You have to set the
DPNSEND_GUARANTEED flag in conjunction with this flag
IDirectPlay8 Client interface
• Initialize and do things as before
HRESULT hrInitializeDirectPlay( HWND hWindow ) { HRESULT hReturn;
// Initialize COM
hReturn = CoInitialize( NULL );
if( FAILED(hReturn) ) {
MessageBox( hWindow, "Error Initializing COM", "DirectPlay Error", MB_ICONERROR );
return hReturn;
}
// Create IDirectPlay8Client Object
if( FAILED( hReturn = CoCreateInstance( CLSID_DirectPlay8Client, NULL,
CLSCTX_INPROC_SERVER, IID_IDirectPlay8Client, (LPVOID*) &g_pDP ) ) )
MessageBox( hWindow, "Can't Create DPlayClient", "DirectPlay Error", MB_ICONERROR );
// Init IDirectPlay8Client Message Handler
if( FAILED( hReturn = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) ) { MessageBox( hWindow, "Failed to Message Handler", "DirectPlay Error", MB_ICONERROR );
return -1;
}
// Create a device address
hReturn = CoCreateInstance( CLSID_DirectPlay8Address,
NULL,CLSCTX_INPROC_SERVER, IID_IDirectPlay8Address, (LPVOID*)
&g_pDeviceAddress );
if( FAILED(hReturn) ) {
MessageBox( hWindow, "Failed to Create Device", "CoCreateInstance()", MB_ICONERROR );
return -1;
}
// Set our service provider to TCP/IP
if( FAILED( hReturn = g_pDeviceAddress->SetSP( &CLSID_DP8SP_TCPIP ) ) ) {
// Create a host address
hReturn = CoCreateInstance( CLSID_DirectPlay8Address,
NULL,CLSCTX_INPROC_SERVER, IID_IDirectPlay8Address, (LPVOID*)
&g_pHostAddress );
if( FAILED(hReturn) ) {
MessageBox( hWindow, "Failed to Create Host Address()", "Invalid Param", MB_ICONERROR );
return -1;
}// Set the host address to TCP/IP
if( FAILED( hReturn = g_pHostAddress->SetSP( &CLSID_DP8SP_TCPIP ) ) ) { MessageBox( hWindow, "Failed to SetSP() for Host Address", "Invalid Param", MB_ICONERROR );
return -1;
}
// Create connection complete event for later use
g_hConnectCompleteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
vShowText(hLB_Output,"<<--TCP INITED-->>");
return S_OK;
}
Joining the server session
• A user click the join button -> msg is sent
• hrJoinGame() is called
LRESULT CALLBACK fnMessageProcessor ( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
{ char szMessage[256];
char szCompMessage[256];
char szMyName[32];
HRESULT hReturn;
void *packet;
PacketChat ChatMsg;
switch (iMsg)
{ case WM_COMMAND:
{ // Check for child window messages switch(LOWORD(wParam))
{ // Check if the user clicked the button case IDC_hBU_Join:
hrJoinGame( hWnd );
break;
} } …
} See next page
HRESULT hrJoinGame( HWND hWnd )
{ HRESULT hReturn = S_OK;
WCHAR wszHostName[256];
WCHAR wszClientName[256];
char szClientName[256];
char szIP[256];
char szPort[256];
DWORD dwPort;
DWORD dwLength = 256;
DPN_APPLICATION_DESC dpnAppDesc;
DPN_PLAYER_INFO dpPlayerInfo;
vShowText(hLB_Output,"Attempting Connection...");
// Set the Client info
GetWindowText(hEB_InputName,szClientName,36); // Get name from Window Edit Box
ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
dpPlayerInfo.pwszName = wszClientName;
// Make this a synchronous call
if( FAILED( hReturn = g_pDP->SetClientInfo( &dpPlayerInfo, NULL, NULL, DPNSETCLIENTINFO_SYNC ) ) ) {
vShowText(hLB_Output,"Failed to set client info");
return -1;
}
// Prepare the application description ZeroMemory( &dpnAppDesc, sizeof(
DPN_APPLICATION_DESC ) );
dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC );
dpnAppDesc.guidApplication = GUID_CHATSERVER;
Setup info for the player who joins
If your client application’s GUID does not match the server’s Then a connection cannot be made
HRESULT setClientInfo(
const DPN_PLAYER_INFO *const pdpnPlayerInfo,
//stores player name and other info about client PVOID const pvAsyncContext, //NULL
DPNHANDLE *const phAsyncHandle, // a handle that lets you cancel //this operation
const DWORD dwFlags //1 flag -> DPNSETCLIENTINFO_SYNC, //which tells the function to process //synchronously
);
// Get IP from edit box
GetWindowText(hEB_InputServerIP,szIP,32);
// Get Port from edit box
GetWindowText(hEB_InputServerPort,szPort,6);
// Convert the IP to a wide string
DXUtil_ConvertGenericStringToWide( wszHostName, szIP );
// Convert the port string to a DWORD dwPort = atol(szPort);
// Add host name to address hReturn = g_pHostAddress-
>AddComponent(DPNA_KEY_HOSTNAME,wszHostName,(wcslen(
wszHostName)+1)*sizeof(WCHAR),DPNA_DATATYPE_STRING);
if( hReturn != S_OK ) {
MessageBox( hWnd, "Failed to AddComponent()",
"hrJoinGame()", MB_ICONERROR );
return -1;
}
Created in hrInitializeDirectPlay()
// Add port number to address hReturn = g_pHostAddress-
>AddComponent(DPNA_KEY_PORT,&dwPort,sizeof(DWORD),D PNA_DATATYPE_DWORD);
if( hReturn != S_OK ) {
MessageBox( hWnd, "Failed to AddComponent()",
"hrJoinGame()", MB_ICONERROR );
return -1;
}// Connect to the session
hReturn = g_pDP->Connect( &dpnAppDesc, g_pHostAddress, g_pDeviceAddress, NULL,
NULL, NULL,
if( hReturn != E_PENDING && FAILED(hReturn) ) { vShowText(hLB_Output,"Failed to Connect");
return -1;
}
SetTimer( hWnd, TIMERID_CONNECT_COMPLETE, 100, NULL );
return(hReturn);
}
HRESULT Connect(
const DPN_APPLICATION_DESC *const pdnAppDesc, //the application description we set up earlier
IDirectPlay8Address *const pHostAddr, //host address interface IDirectPlay8Address *const pDeviceInfo, //device address for the client
//connection that you defined in //hrInitializeDirectPlay() const DPN_SECURITY_DESC *const pdnSecurity, //for future, therefore
//NULL
const DPN_SECURITY_CREDENTIALS *const pdnCredentials, //NULL const void *const pvUserConnectData, //send additional connect information to
//the server. If you specify this, the //server receives a
//DPN_MSGID_INDICATE_CONNECT //message
const DWORD dwUserConnectDataSize, //size of the 6thparameter void *const pvAsyncContext, //user created context to be passed when
//DPN_MSGID_CONNECT_COMPLETE message is //processed
DPHANDLE *const phAsyncHandle, //must be NULL if you set //DPNCONNECT_SYNC
Set up the connection timer
• Let it goes off every 0.1 second
• i.e. the system sends a WM_TIMER message to the main message loop every 0.1 second
• This saves the program from having to do a loop-intensive waiting
• The part of the code that handles
WM_TIMER is in fnMessageProcessor()
case WM_TIMER:
if( wParam == TIMERID_CONNECT_COMPLETE ) {
// Check if the message is telling us our connection is complete if( WAIT_OBJECT_0 == WaitForSingleObject(
g_hConnectCompleteEvent, 0 ) ) {
if( FAILED( g_hrConnectComplete ) ) {
vShowText(hLB_Output,"<<----CONNECTION IN-COMPLETE---->>");
} else {
vShowText(hLB_Output,"<<----CONNECTION COMPLETE---->>");
}
KillTimer( hWnd, TIMERID_CONNECT_COMPLETE );
} }
Check if this is the timer that we created
• In DirectPlayMessageHandler(),
DPN_MSGID_CONNECT_COMPLETE is received by clientr once the server has accepted the connection
• When you get this message, you should signal the g_hConnectCompleteEvent with the SetEvent()
– Before you do that it is best to set the return state of the connection to find out whether the server accepted or rejected the connection
case DPN_MSGID_CONNECT_COMPLETE:
{
PDPNMSG_CONNECT_COMPLETE pConnectCompleteMsg;
pConnectCompleteMsg = (PDPNMSG_CONNECT_COMPLETE)pMsgBuffer;
g_hrConnectComplete = pConnectCompleteMsg->hResultCode;
SetEvent( g_hConnectCompleteEvent );
break;
}
Sending messages to the server
• Look in fnMessageProcessor()
switch(HIWORD(wParam))
{ case EN_UPDATE:
// Get the text from the edit box
GetWindowText(hEB_InputField,szMessage,256);
// Check if they pressed enter
if( szMessage[strlen(szMessage)-1] == 10 ) { // Get rid of trailing garbage
szMessage[strlen(szMessage)-2] = '\0';
GetWindowText(hEB_InputName,szMyName,32);
sprintf(szCompMessage,"<%s> %s", szMyName, szMessage);
// clear input field
SetWindowText(hEB_InputField,"");
//// Send a chat packet to the server //ChatMsg.dwSize = sizeof(PacketChat);
ChatMsg.dwType = PACKET_TYPE_CHAT;
strcpy(ChatMsg.szText,szCompMessage);
// Convert the packet to a void stream packet = (VOID*)&ChatMsg;
HRESULT hrSendClientMessage( DWORD dwMessageType, PVOID pMsgBuffer )
{ DPNHANDLE hAsync;
DWORD dwLength;
DPN_BUFFER_DESC bufferDesc;
PacketGeneric *PGen;
// Cast to a generic packet to get the size PGen = (PacketGeneric*)pMsgBuffer;
dwLength = PGen->dwSize;
if( dwLength == 0 ) return(0);
bufferDesc.dwBufferSize = dwLength;
bufferDesc.pBufferData = (BYTE*)pMsgBuffer;
g_pDP->Send( &bufferDesc, 1, 0, NULL, &hAsync, 0 );
return S_OK;
} Use Send() instead of SendTo()
-client can only send to server, so no need to tell Who it sends to
IDirectPlay8Client::Send()
HRESULT Send(
const DPN_BUFFER_DESC *const pBufferDesc,
//the DPN_BUFFER_DESC data structure that contains the //message to send
const DWORD cBufferDesc, //number of buffers passed in //the first parameter (currently //you can only send 1)
const DWORD dwTimeOut, //milliseconds, if 0-> keep sending void *const pvAsyncContext, //NULL
DPNHANDLE *const phAsyncHandle, //if you use
//DPSEND_SYNC, you
Review
• Server
– Initialize COM
– Create IDirectPlay8Server interface – Create a device address
– Set TCP/IP as the service provider – Set the server information using
IDirectPlay8Server::SetServerInfo() – Initialize the application description
– Add the port number to the device address – Call the IDirectPlay8Server::Host()
• Client
– Initialize COM
– Create IDirectPlay8Client interface – Create a device address
– Set TCP/IP as the service provider – Create a host address
– Set TCP/IP as the service provider – Create a completion event
– Set the client information using IDirectPlay8Client::SetClientInfo() – Initialize the application description
– Add the host port number and IP address to the host address
– Call the IDirectPlay8Client::Connect()