Microsoft DirectX 9.0 SDK Update (Summer 2003) |
Advanced NAT Techniques for Servers
Microsoft® DirectPlay® cannot provide 100 percent connectivity for hosts behind Network Address Translation (NAT) devices that do not support Universal Plug and Play (UPnP). However, your application might be able to improve support for some of these cases using more elaborate methods. The techniques described here require additional development effort as well as external server resources.
NAT Resolver
NAT devices create an implicit port mapping when an internal computer sends a packet to an external computer. Some NAT devices allow any external computer to use this port mapping to send to the internal computer, instead of only forwarding replies sent by the original external target. These are sometimes referred to as "loose NATs." DirectPlay can take advantage of this behavior using the IDirectPlay8NATResolver interface and address components to allow hosting behind these devices.
You must start by implementing an IDirectPlay8NATResolver server that will be accessible from the Internet. Then your game application can add the DPNA_KEY_NAT_RESOLVER component to the device address passed to IDirectPlay8Server::Host. The component data is a string containing the Internet Protocol (IP) address and port for the NAT resolver to use, separated by a colon. For example, the following code specifies to use a server located on port 5678 at the address 123.123.123.123.
WCHAR * wszNATResolver = L"123.123.123.123:5678"; DWORD dwNATResolverSize = (wcslen(wszNATResolver) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER, wszNATResolver, dwNATResolverSize, DPNA_DATATYPE_STRING);
You can also specify a hostname instead of a numerical IP address as shown in the following example.
WCHAR * wszNATResolver = L"resolver.mydomain.com:5678"; DWORD dwNATResolverSize = (wcslen(wszNATResolver) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER, wszNATResolver, dwNATResolverSize, DPNA_DATATYPE_STRING);
For robustness, you might want to have more than one resolving servers to try. You can specify multiple addresses separated by commas as shown in the following example.
WCHAR * wszNATResolvers = L"123.123.123.123:5678,backupresolver.mydomain.com:6789"; DWORD dwNATResolverSize = (wcslen(wszNATResolvers) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER, wszNATResolvers, dwNATResolverSize, DPNA_DATATYPE_STRING);
Each resolver is tried simultaneously for speed, and the first response is used. If no server responds, the IDirectPlay8Server::Host call still succeeds.
Because hosting these resolving servers requires resources, you might want to prevent arbitrary players from using the resolver. This can be achieved with the DPNA_KEY_NAT_RESOLVER_USER_STRING address component. This value is passed directly to the resolver for verification in the DPN_MSGID_NAT_RESOLVER_QUERY message, and it can choose to respond or ignore as appropriate. The following example shows how to do this.
WCHAR * wszPassword = L"MyPassword"; DWORD dwPasswordSize = (wcslen(wszPassword) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER_USER_STRING, wszPassword, dwPasswordSize, DPNA_DATATYPE_STRING);
The host's NAT device generates a port mapping for the NAT resolver query. If the NAT resolver elects to respond to the query, DirectPlay will send the mapping's public address back to the querying host. The host will then have it included in the addresses returned by the IDirectPlay8Server::GetLocalHostAddresses method.
External clients use the mapping address when connecting to the internal host. However the NAT device will expire the mapping if it is inactive, which will prevent external clients from using it. The actual expiration timeout varies by NAT device. Generally, the external clients are expected to begin connecting within 30 seconds of the IDirectPlay8Server::Host call completing.
An example usage of NAT resolver address components can be found in the NATPeer sample.