サーバーでの基本的な NAT の問題
サーバーでの基本的な NAT の問題
IDirectPlay8Server インターフェイスを使ってホスティングするための、ネットワーク アドレス変換 (NAT) の互換性が最適となるのは、NAT デバイスが Microsoft® DirectPlay® によるユニバーサル プラグ アンド プレイ (UPnP) の使用をサポートしている場合である。UPnP の互換性のない NAT デバイスに対するサポートを向上させるために、サーバーは、DPNSVR に依存せずに特定のポートのセットを使う必要がある。
DPNSVR
DPNSESSION_NODPNSVR フラグを設定していない DPN_APPLICATION_DESC 構造体を使って IDirectPlay8Server::Host を呼び出すと、DPNSVR ヘルパー アプリケーションが起動される。DPNSVR プロセスは、"既知の" ポートで列挙クエリーを聴取する。既知のポートとは、IDirectPlay8Client::EnumHosts に渡す IDirectPlay8Address ホスト オブジェクトに DPNA_KEY_PORT コンポーネントが指定されないときに想定されるのと同じポートである。
DPNSVR が列挙クエリーを受け取ると、ローカル コンピュータで DPNSVR を有効にしているすべてのホストにそのクエリーが転送される。次に、各ホスト アプリケーションは、それぞれの固有のポートから直接列挙に応答する。しかし、クライアントの NAT デバイスによっては、クライアントのもともとの送信先ポートから応答を予期しているものがあり、そのために列挙応答をドロップすることがある。そのため、アプリケーションでセッションの発見のために DPNSVR を使う場合、アプリケーションはゲーム ポートを直接列挙しようとしなければならない。そのためには、クライアントがゲームのアドレスを事前に知っている必要がある。ピア クライアントの処理の詳細については、「クライアントの問題」を参照すること。
ホスト アドレスの判断
ホストは、IDirectPlay8Server::GetLocalHostAddresses メソッドを使って、自身が聴取しているアドレスを判断できる。
IDirectPlay8Address *pDP8AddressHost = NULL;
DWORD dwNumAddresses = 1;
hr = pDP8Server->GetLocalHostAddresses(&pDP8AddressHost, &dwNumAddresses, DPNGETLOCALHOSTADDRESSES_COMBINED);
Transmission Control Protocol/Internet Protocol (TCP/IP) サービス プロバイダで DPNGETLOCALHOSTADDRESSES_COMBINED フラグを指定すると、1 つのアドレス オブジェクトが返される。このオブジェクトにより、IDirectPlay8Client::EnumHosts またはIDirectPlay8Client::Connect は、ホストのすべてのアドレスを同時に試すことができる。次の例のように、このアドレスは IDirectPlay8Address::GetURLA メソッドを使って、マッチ メーキング サービスを通じて簡単に転送できるように、文字列に変換できる。
char *szHostAddress = NULL;
DWORD dwNumHostAddressChars = 0;
hr = pDP8AddressHost->GetURLA(NULL, &dwNumHostAddressChars);
szHostAddress = LocalAlloc(LPTR, dwNumHostAddressChars * sizeof(char));
hr = pDP8AddressHost->GetURLA(szHostAddress, &dwNumHostAddressChars);
クライアントは、アドレスを受信すると、IDirectPlay8Address::BuildFromURLA を使って文字列をオブジェクトに変換し直す。ピア クライアントの処理の詳細については、「クライアントの問題」を参照すること。
特定のポートの使用
通常、アプリケーションは、ホスティングの際に DirectPlay にポートを選択させる必要がある。しかし、NAT の状況によっては、ゲームをホスティングするポートをユーザーが変更したいこともある。また、アプリケーションが DirectPlay のロビー またはその他のマッチ メーキング サービスを使ってアドレスを渡さない場合、直接的な発見のために使う、デフォルトのゲーム ポートが必要である。次の例のように、DPNA_KEY_PORT コンポーネントを使って、ポートをデバイス アドレスに追加できる。
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.
}
次に、このアドレス オブジェクトは、ホスティングするデバイス アドレスとして、IDirectPlay8Server::Host に渡される。
UPnP ポートの自動転送
ホストで Microsoft Windows® Internet Connection Firewall を有効にしているか、クライアントが UPnP NAT デバイスの対象になっている場合、DirectPlay はアプリケーションに対してポート転送を自動的に有効にしようとする。このとき、特定のポートでインターネットから受信するすべてのパケットを受け入れ、プライベート ネットワーク内の特定のアドレスとポートに転送するようにデバイスに要求する。
DirectPlay がホスト用のローカル ポートを選択した場合、DirectPlay は NAT デバイスが転送するための未使用の外部ポートを選択する。実際に選択されるパブリック ポートは異なり、ローカル ポートと同じではないことがある。
IDirectPlay8Server::Host に指定したデバイス アドレスに DPNA_KEY_PORT コンポーネントが設定されている場合、DirectPlay は、NAT デバイスに対して同じ外部ポート番号を転送するように要求する。そのパブリック ポート番号が使われている場合、IDirectPlay8Server::Host への呼び出しは失敗し、DPNERR_INVALIDDEVICEADDRESS が返される。この状況が発生する可能性のあるのは、同じ NAT の対象であるアプリケーションの別のインスタンスが、既にホスティングを実行している場合である。
アプリケーションとマッチ メーキングは、ローカルに同じポートを使うと共に、NAT デバイス上で同じポートを使うことを要求しないように設計する必要がある。次に、DPNA_KEY_TRAVERSALMODE デバイス アドレス コンポーネントを使って、一致するポートが利用できないときに、DirectPlay が別の外部ポートを試すようにできる。その例を次に示す。
DWORD dwTraversalMode = DPNA_TRAVERSALMODE_PORTRECOMMENDED;
hr = pDP8AddressDevice->AddComponent(DPNA_KEY_TRAVERSALMODE, &dwTraversalMode, sizeof(dwTraversalMode), DPNA_DATATYPE_DWORD);
自動トラバースの無効化
ホスト アプリケーションが UPnP NAT デバイスの対象ではなく、Windows Internet Connection Firewall が有効になっていないことを知っているユーザーもいる。一方、ホストに対して行われたマッピングを手動で制御したいユーザーもいる。次の例のように、DPNA_KEY_TRAVERSALMODE コンポーネントを DPNA_TRAVERSALMODE_NONE に設定すると、IDirectPlay8Server::Host が必要とする時間を減らし、自動トラバースを行わないようにできる。
DWORD dwTraversalMode = DPNA_TRAVERSALMODE_NONE;
hr = pDP8AddressDevice->AddComponent(DPNA_KEY_TRAVERSALMODE, &dwTraversalMode, sizeof(dwTraversalMode), DPNA_DATATYPE_DWORD);