想定外の使い方で見つかった症状

このロビーは、1つのネットワークにつき1台のPCから遊ぶ使い方を想定している。動作検証で、人数合わせのために同じルーターの内側から2台のPCを同時に使ったところ、片方だけが部屋に参加できない現象が起きた。想定外の使い方ではあるが、原因を調べる過程で、P2P型のゲームが家庭用ルーターの中でどう動くかがよく分かる事例だったので、記録として残す。

症状は次のとおりである。

  • ロビーには入れる。部屋の一覧も見える。
  • 部屋に参加しようとすると、「プレイヤー○○に接続できませんでした。ファイアウォールの設定が正しくない可能性があります」という趣旨のメッセージが出て、参加できない。
  • 失敗するのは、同じルーターの内側で、後から接続した側のPCだけである。先に接続していたPCは、同じ相手と問題なく通信できている。
  • 同じPCが、別の時間帯には同じ相手の部屋へ問題なく参加できている。つまり、PCやOSの不具合ではなく、そのときの状況で成否が変わる。

用語の整理

以降の説明で使う用語を、先に定義しておく。

  • ホストと参加者: ホストは、部屋を作った側である。参加者は、その部屋に入ろうとする側である。
  • P2P(Peer to Peer): サーバーを経由せず、ホストと参加者のPCどうしが、直接通信する方式である。このゲームの対戦中のデータは、この方式で流れる。これはゲームブラウザの記事でも説明している。
  • IPアドレス: ネットワーク上の機器を識別する、住所にあたる番号である。公開IP(グローバルIP)は、インターネット上から見える住所で、家庭では通常、ルーターが1つだけ持つ。プライベートIPは、家庭内のネットワーク(LAN)の中でだけ通用する住所で、LAN内の各PCに1つずつ割り当てられる。
  • ポート: 1台の機器の中で、通信を受け取るソフトを区別する番号である。IPアドレスが建物の住所なら、ポートは部屋番号にあたる。
  • UDP: 通信方式の1つである。接続の確立の手順を踏まず、パケット(通信の1単位となるデータのかたまり)を送りっぱなしにする。反応の速さが求められるゲームの対戦通信で、よく使われる。
  • ルーターとNAT: 家庭のルーターは、複数のPCが1つの公開IPを共有してインターネットに出られるように、NAT(Network Address Translation、アドレス変換)を行う。NATは、LAN内のアドレスと、公開側のアドレスを、通信ごとに付け替える仕組みである。
  • 通信表(NAT変換表): ルーターが、NATの付け替えの対応を記録している表である。「LAN内のどのPCの、どのポートが、外側のどの相手の、どのポートと通信しているか」と、その通信に割り当てた公開側のポート(公開ポート)が対応づけて記録される。外から届いた通信は、この表に記録のある返事だけが、LAN内に通される。
  • 送信元ポート: 通信を送り出す側が使う、ポート番号である。
  • 固定ポート: ゲームが、毎回同じ番号で使うポートである。
  • NAT越え(穴あけ): 通信表に記録がないと外から通信を通さないルーターの間で、直接通信を成立させる方法である。サーバーが仲介して、両者が互いの相手に向けて同時に通信を送り出すことで、両方のルーターの通信表に記録を作る。
  • ポート転送: ルーターに設定する、特定のポートあての通信を、LAN内の決まった1台のPCへ通す設定である。UPnPは、PC上のソフトが、この設定をルーターへ自動で依頼する仕組みである。
  • ファイアウォール: 許可していない通信を遮断する仕組みである。PCのOSにも、ルーターにも備わっている。
  • ログ: サーバーが残す動作の記録である。

参加時に何が起きるか

ホストと参加者は、そのままでは互いの通信を届けられない。ルーターが、通信表に記録のない外からの通信を通さないためである。そこで、サーバーが仲介して、NAT越えを行う。

次の図は、参加者の視点で、参加を要求してから、直接通信できるようになるまでの流れを示している。矢印1本が、通信または通知の1回にあたる。

  1. 参加の要求: 参加者が、サーバーに「この部屋に参加したい」と要求する。
  2. 連絡: サーバーが、ホストに「参加者が来ている」と連絡する。
  3. 応答: ホストが、サーバーに応答して、仲介の準備ができたことを知らせる。
  4. 通知(参加者へ): サーバーが、参加者に「ホストの公開IPと公開ポート」を通知する。
  5. 通知(ホストへ): サーバーが、ホストに「参加者の公開IPと公開ポート」を通知する。
  6. 送り出し(参加者から): 参加者が、通知されたホストの住所へ、通信を送り出す。
  7. 送り出し(ホストから): ホストが、通知された参加者の住所へ、通信を送り出す。手順6と、ほぼ同時に行う。

手順6と7で、双方のルーターに、互いの相手との通信の記録(通信表の記録)ができる。以降、相手からの通信は、その記録に対する返事として通されるので、直接通信できるようになる。

参加者の視点参加者PC + ルーター(自分)サーバー仲介役ホストPC + ルーター① 参加の要求② 連絡(参加者が来ている)③ 応答(準備ができた)④ 通知:ホストの公開IPとポート⑤ 通知:参加者の公開IPとポートここから先は、サーバーを通らない⑥ 参加者からホストへ、通信を送り出す⑦ ホストから参加者へ、通信を送り出す(⑥とほぼ同時)相手に伝えられる住所は「公開IP + 固定のポート」である
参加者の視点での、参加の流れ。矢印1本が、通信または通知の1回にあたる。⑥と⑦は別々の矢印で、互いの相手へ同時に送り出す。

ここで重要なのは、④と⑤で通知される住所のポートが、ゲームの固定ポートである点である。つまり、参加者もホストも、公開IPと固定ポートの組み合わせを、相手に名乗っている。この前提が、次に説明する衝突の原因になる。

2台がぶつかる理由

ルーターの通信表は、同じ相手との通信に対して、同じ公開ポートを、2台のPCへ同時に割り当てることができない。

同じルーターの内側で、2台が同じ固定ポートを使い、同じ相手と通信しようとすると、次のことが起きると考えられる。以下は、後から接続したPC(PC-B)の視点で読む。

  1. 先に接続したPC(以下、PC-A)が、相手との通信で、公開ポートを確保する。通信表に「PC-Aと相手」の記録ができる。
  2. PC-Bが、同じ相手へ、同じ固定ポートで送り出そうとする(前節の⑥にあたる)。ルーターは、同じ公開ポートを、PC-Bには使えないので、別のポートに付け替えるか、既存の記録に合わせる。
  3. 相手は、サーバーに教えられた固定ポートあてに通信を送る(前節の⑦にあたる)。この通信は、通信表の記録に従って、PC-Aに届く。付け替えられたPC-Bの公開ポートには、送られない。
  4. PC-Bには、相手からの通信が届かない。PC-Bは、参加できずにエラーとなる。

さらに、固定ポートあての通信を特定のPCへ転送するポート転送が、ルーターに設定されていると、その転送先のPCが優先されやすくなる。機種によって挙動が異なるため、この点は断定できない。

サーバーから見ると、この衝突は見えない。サーバーの仕事は、前節の④と⑤で、両者に相手の住所を伝えるところまでで、両者が要求を出せば、仲介そのものは完了する。衝突が起きるのは、その後の、PCと相手の間の通信だからである。

後から接続したPC-Bの視点同じルーターの内側(LAN)PC-A先に接続PC-B(自分)後から接続ルーター通信表相手のPCホストなど教えられた固定ポートへ送る通信表では先に通信していたPC-Aの記録PC-Bには届かず、「プレイヤーに接続できません」となる
相手からの通信は、通信表の記録に従ってPC-Aに届き、後から接続したPC-Bには届かない。

サーバーのログで見えたこと

検証中のサーバーログ(個人が特定できる情報は伏せている)から、次のことが読み取れた。

  • 仲介の段階は、多くの試行で完了している。 約15分の間に、後から接続した側(PC-B)の参加の試行は7回あった。そのうち、5回は結果の報告が届き、すべて失敗を示していた。3回は両側が失敗を報告し、2回は相手側だけが失敗を報告した。残りの2回は、PC-B側からの要求が確認できず、仲介が完了しなかった。
  • 失敗が続いたのは、PC-Aがすでに同じ相手と通信していた時間帯と一致する。 それより前の時間帯には、PC-Bは同じ相手との通信に成功していた。
  • サーバーに届いた要求の送信元ポートが、固定値から、別の値に変わっていた。 2台がほぼ同時にサーバーへ要求した場面で、後から要求した側の送信元ポートが、固定値ではない値になっていた。ルーターが、ぶつかった一方のポートを付け替えていることを示す、直接の記録である。この場面自体は、仲介が成功している。
  • NATの種類として報告された値は、成功時も失敗時も同じだった。 NATの種類とは、ルーターが公開ポートを割り当てる方式の分類で、分類によっては、NAT越えが成立しにくくなる。今回は、失敗を、この分類の違いでは説明できない。
  • 失敗した時間帯だけ、結果の報告に含まれる、接続に関する値が、成功時と異なっていた。 ただし、その値の意味は確認できていない。
  • ルーターのUPnPは有効だったが、UPnPで動的に登録されたポート転送は1件もなかった。 NAT越えの手順は、UPnPを使わないため、今回の失敗にUPnPは関与していないと判断した。

検証できていないこと

この記事の説明は、ログとルーター設定に最も整合する説明であり、証明したものではない。次の点は、行っていない。

  • 順番を入れ替える(PC-Bを先に接続する)、1台だけで接続する、ポート転送の設定を変えるといった、条件を変えた比較実験
  • 失敗した時点での、PC側でのパケットの記録
  • ルーターの通信表そのものの確認

「ファイアウォール」のメッセージは当てにならない

クライアントには、ファイアウォールの設定が原因であるかのようなメッセージが出る。しかし、このケースの原因は、PCのファイアウォールではない。相手との直接通信に失敗したときに、原因を区別せず表示される汎用のメッセージだと考えられる。PCのファイアウォールを疑って設定を変えても、同じ症状が続く。

症状から見る切り分け

技術者向けに、切り分けの観点をまとめる。

  • 同じネットワークで、他のPCが同じゲームを動かしていないか。 他のPCのCiv4を終了して、1台だけで再試行する。これで参加できれば、今回のケースである可能性が高い。
  • (サーバー管理者向け)サーバーのログで、公開IPが同じで、プライベートIPが異なる参加者が、同時期に2組いないか。 同じ相手との仲介が、続けて失敗していれば、今回の現象を疑う。
  • PC側で、対戦用のUDPポートの受信を記録して、相手からのパケットが届くかを見る。 何も届かなければ、ルーターで止まっているか、別のPCに届いている。PCのファイアウォールが原因なら、パケットは届くのに、アプリケーションが受け取れない形になる。
  • ポート転送の設定と、その転送先を確認する。 特定のPCに向けた転送があると、他のPCは、受け取る側になれない。
  • 別のネットワーク(スマートフォンのテザリングなど)から試す。 ただし、携帯回線は、公開IPを他の利用者と共有している場合があり、別の制約が加わる。切り分けの結果は、慎重に解釈する。

対処

  • 1台ずつ接続する。 最も確実である。
  • 別のネットワークから、もう1台を接続する。 同じルーターの内側にいなければ、衝突は起きない。
  • ポート転送を、使うPCに合わせる。 転送先を、その時に使うPCに切り替える。同時に使えるのは、そのうちの1台だけである。
  • UPnPを有効にしても、この問題は解決しない。 多くの家庭用ルーターでは、同じ公開ポートを2台に割り当てられないためである。
  • ゲーム側で、対戦用のポートを変更できれば、2台の衝突は避けられる。 ただし、変更の可否は、確認できていない。

サーバー側では直せない

衝突が起きるのは、利用者のルーターの中である。サーバーができるのは、相手の公開アドレスを伝えるところまでで、ルーターの通信表には関与できない。サーバーを改良しても、この制約は取り除けない。

なお、ホストと参加者が、同じルーターの内側にいる場合は、今回とは別の仕組みが働く。サーバーは、両者の公開IPが同じであることを見て、LAN内で直接つなぐ方式を選ぶ。その詳細はゲームブラウザの記事にまとめている。この場合の動作は、今回は検証していない。

まとめ

  • 同じルーターの内側から、2台のPCが同時に、同じ相手へ、固定のポートで接続すると、後から接続した側が失敗することがある。
  • 原因は、ルーターの通信表で、同じ公開ポートと同じ相手の組み合わせを、2台に割り当てられないことと考えられる。
  • サーバーの仲介は成功するため、サーバー側からは異常が見えにくい。クライアントに出るファイアウォールのメッセージも、原因の手がかりにならない。
  • 対処は、1台ずつ接続するか、別のネットワークから接続することである。
  • 想定外の使い方であり、条件を変えた比較実験は行っていない。上記は、現時点で最も整合する説明として記録している。