Microsoft DirectX 9.0 SDK Update (Summer 2003)

Basic NAT Issues for Peer Clients


The best Network Address Translation (NAT) compatibility for joining a session with the IDirectPlay8Peer interface is when the NAT device supports Microsoft® DirectPlay®'s use of Universal Plug and Play (UPnP). Peer clients should keep enumerations active and avoid relying on DPNSVR to improve support for NAT devices without UPnP compatibility.

Keeping EnumHosts Active While Connecting

Most NAT devices create virtual connections each time an internal client starts sending packets to a new external address and port, such as a new IDirectPlay8Peer::EnumHosts operation. These virtual connections will sometimes be destroyed as soon as the IDirectPlay8Peer::EnumHosts operation completes. If your client application enumerates hosts before connecting, you should neither cancel the IDirectPlay8Peer::EnumHosts operation nor allow it to time out so that the call to IDirectPlay8Peer::Connect can reuse the virtual connection. This requirement can be met by specifying INFINITE for the dwEnumCount parameter as shown in the following example.
hr = pDP8Peer->EnumHosts(&dpnad,
                         pDP8AddressHost,
                         pDP8AddressDevice,
                         NULL,
                         0,
                         INFINITE,
                         0,
                         0,
                         NULL,
                         &dpnhEnumHosts,
                         0);

DirectPlay will automatically cancel all outstanding IDirectPlay8Peer::EnumHosts operations for you when the connect operation completes successfully. If the connect fails, the enumerations will continue running. You can then cancel them using the IDirectPlay8Peer::CancelAsyncOperation method.

To ensure that the connect operation uses the same virtual connection as the enumeration, the host and device address objects specified to IDirectPlay8Peer::Connect should be the same as those included in the corresponding DPN_MSGID_ENUM_HOSTS_RESPONSE message. For examples of how to add a reference or duplicate these addresses, see the DirectPlay C++ Samples.

DPNSVR

The application sends enumeration queries to the DPNSVR port when the DPNA_KEY_PORT component is not specified in the IDirectPlay8Address host object passed to IDirectPlay8Peer::EnumHosts. However the hosts reply from their respective game ports. Some NAT devices expect replies to come from the port to which the client originally sent, and might therefore drop the enumeration response. If your application uses DPNSVR to help with session discovery, it should also attempt to enumerate the game port directly.

Enumerating the Host's Game Port Directly

The host can retrieve its addresses using IDirectPlay8Peer::GetLocalHostAddresses and pass it in string format to the client using IDirectPlay8Address::GetURLA through a match-making service. The client can then convert the string back into an address using the IDirectPlay8Address::BuildFromURLA method as shown in the following example.

IDirectPlay8Address *pDP8AddressHost = NULL;

hr = CoCreateInstance(CLSID_DirectPlay8Address, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Address, NULL, (PVOID*) (&pDP8AddressHost));

hr = pDP8AddressHost->BuildFromURLA(szHostAddress);

This address object can then be passed to IDirectPlay8Peer::EnumHosts or IDirectPlay8Peer::Connect.

Enumerating or Connecting From a Particular Port

Applications should almost always let DirectPlay choose the local port or ports when enumerating or connecting. However, the local ports selected by DirectPlay to connect to the original host are the same ones used to listen for new players once the host migrates. If your application uses host migration but does not pass updated addresses for the new host by a DirectPlay Lobby or other match-making service, you might want to allow the user to select a local port. 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::EnumHosts or IDirectPlay8Peer::Connect for the device address parameter.

Automatic UPnP Port Forwarding

When the client has Microsoft 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 client, 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::EnumHosts or IDirectPlay8Peer::Connect, 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::EnumHosts or IDirectPlay8Peer::Connect will fail with DPNERR_INVALIDDEVICEADDRESS. This might happen when another instance of the application behind the same NAT is already connected. 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. This is shown in the following example.

DWORD dwTraversalMode = DPNA_TRAVERSALMODE_PORTRECOMMENDED;

hr = pDP8AddressDevice->AddComponent(DPNA_KEY_TRAVERSALMODE, &dwTraversalMode, sizeof(dwTraversalMode), DPNA_DATATYPE_DWORD);

Disabling Automatic Traversal

Some users know that the client is not behind a UPnP NAT device and Windows Internet Connection Firewall is not enabled. Others might want to manually control any mappings made for the client. You can decrease the time required by IDirectPlay8Peer::EnumHosts or IDirectPlay8Peer::Connect 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);



© 2003 Microsoft Corporation. All rights reserved.