?/TD> |
Microsoft DirectX 9.0 |
The best Network Address Translation (NAT) compatibility for hosting with the IDirectPlay8Peer interface is when the NAT device supports the use of Universal Plug and Play (UPnP) by Microsoft?DirectPlay? Peer hosts should avoid relying on DPNSVR and use a particular set of ports to improve support for NAT devices without UPnP compatibility.
The DPNSVR helper application is launched when IDirectPlay8Peer::Host is called with a DPN_APPLICATION_DESC structure that does not have the DPNSESSION_NODPNSVR flag set. The DPNSVR process listens for enumeration queries on a "well known" port, which is the same port that is assumed when the DPNA_KEY_PORT component is not specified in the IDirectPlay8Address host object passed to IDirectPlay8Peer::EnumHosts.
When DPNSVR receives an enumeration query, it is forwarded to all DPNSVR-enabled hosts on the local computer. Each host application then replies to the enumerator directly from its own port. However, some clients' NAT devices expect these replies to come from the port to which the client originally sent, and might drop this enumeration response. Therefore if your application uses DPNSVR to help with session discovery, it should also attempt to enumerate the game port directly. This requires that the client knows the game's addresses in advance. See Client Issues for more information on handling peer clients.
A host can determine the addresses on which it is listening by using the IDirectPlay8Peer::GetLocalHostAddresses method.
IDirectPlay8Address *pDP8AddressHost = NULL; DWORD dwNumAddresses = 1; hr = pDP8Peer->GetLocalHostAddresses(&pDP8AddressHost, &dwNumAddresses, DPNGETLOCALHOSTADDRESSES_COMBINED);
Specifying the DPNGETLOCALHOSTADDRESSES_COMBINED flag with the Transmission Control Protocol/Internet Protocol (TCP/IP) service provider returns a single address object that allows IDirectPlay8Peer::EnumHosts or IDirectPlay8Peer::Connect to attempt all of the host's addresses simultaneously. This address can be converted into a string for easy transmission through a match-making service by using the IDirectPlay8Address::GetURLA method as shown in the following example.
char *szHostAddress = NULL; DWORD dwNumHostAddressChars = 0; hr = pDP8AddressHost->GetURLA(NULL, &dwNumHostAddressChars); szHostAddress = LocalAlloc(LPTR, dwNumHostAddressChars * sizeof(char)); hr = pDP8AddressHost->GetURLA(szHostAddress, &dwNumHostAddressChars);
Once received, the client converts the string back into an object using IDirectPlay8Address::BuildFromURLA. See Client Issues for more information on handling peer clients.
Applications should generally let DirectPlay select a port when hosting. However, there are some NAT scenarios where the user might want to change the port on which the game is hosting. Also, if your application does not pass addresses using DirectPlay Lobby or other match-making service, it should have a default game port to use for direct discovery. This can be added to a device address using the DPNA_KEY_PORT component as shown in the following example.
DWORD dwUserSelectedPort; // value retrieved from user input if (dwUserSelectedPort != 0) { // User specified a port value; use it. hr = pDP8AddressDevice->AddComponent(DPNA_KEY_PORT, &dwUserSelectedPort, sizeof(dwUserSelectedPort), DPNA_DATATYPE_DWORD); } else { // Let DirectPlay select; don't add port component. }
This address object is then passed to IDirectPlay8Peer::Host as a device address on which to host.
When the host has the Windows Internet Connection Firewall enabled or is behind a UPnP NAT device, DirectPlay will attempt to enable port forwarding for your application automatically. This asks the device to accept all packets received from the Internet on a particular port and forward them to a particular address and port inside the private network.
If DirectPlay selected the local port for the host, then it will select an unused external port for the NAT device to forward. The actual public port number chosen will vary, and might not be the same as the local port.
If the DPNA_KEY_PORT component was set in the device address specified to IDirectPlay8Peer::Host, then DirectPlay will ask the NAT device to forward the same external port number. If that public port number is in use then the call to IDirectPlay8Peer::Host will fail with DPNERR_INVALIDDEVICEADDRESS. This can happen when another instance of the application is already hosting behind the same NAT.
You should design your application and match-making so that they do not require the same port to be used both locally and on the NAT device. You can then allow DirectPlay to try alternate external ports when the matching port is not available by using the DPNA_KEY_TRAVERSALMODE device address component as shown in the following example.
DWORD dwTraversalMode = DPNA_TRAVERSALMODE_PORTRECOMMENDED; hr = pDP8AddressDevice->AddComponent(DPNA_KEY_TRAVERSALMODE, &dwTraversalMode, sizeof(dwTraversalMode), DPNA_DATATYPE_DWORD);
Some users know that the hosting application is not behind a UPnP NAT device and the Windows Internet Connection Firewall is not enabled. Others might want to manually control any mappings made for the host. You can decrease the time required by IDirectPlay8Peer::Host and prevent automated traversal by setting the DPNA_KEY_TRAVERSALMODE component to DPNA_TRAVERSALMODE_NONE as shown in the following example.
DWORD dwTraversalMode = DPNA_TRAVERSALMODE_NONE; hr = pDP8AddressDevice->AddComponent(DPNA_KEY_TRAVERSALMODE, &dwTraversalMode, sizeof(dwTraversalMode), DPNA_DATATYPE_DWORD);