リレー通信について説明します。
PiaSession には、メッシュ内のステーションと直通の通信経路を確立できなかった場合に(インターネットマッチメイク時の NAT トラバーサル失敗など、ステーション同士の接続は失敗することがあります)、通信経路を確立できた別のステーションを介してデータの送受信を可能にする「リレー通信機能」があります。リレー通信機能を使用するネットワークトポロジーを Session クラスのインスタンス生成時に指定することで有効になります。各ネットワークトポロジーの特徴については 7.1. PiaSession の概念 を参照してください。
リレー通信機能説明用に 図 7-8. リレー通信接続イメージ図 を例示します。
Route A や Route B、Route D がリレー通信時の経路になります。Route C のような 2 ステーション以上を経由するリレー経路は選択されません。
直接の通信経路が無いステーション間で別のステーションを介してデータの送受信を可能とする接続形態を「リレー接続」と表現します。このとき、どのステーションを仲介先とするか選択することを「経路選択」と表現します。経路選択はステーションの参加や離脱が発生した時にホストが行い、選択した経路情報をクライアントに送信します。経路選択や経路情報の生成、解析は、common::Scheduler::Dispatch() 内で行われます。
例として 図 7-8. リレー通信接続イメージ図 では、Station2 と Station4 の間に直通の通信経路がありません。この場合に Station2 - Station4 の通信経路としては Route A = Station1 経由、 Route B = Station3 経由、 Route C = Station1 & 3 経由、などといった複数の選択肢が存在することになります。このような複数の選択肢の中から以下のリレー経路選定条件を考慮して通信経路を選定します。
リレー接続処理によって一度選定した通信経路は基本的には変更されませんが、他のステーションがメッシュへ参加もしくはメッシュから離脱した場合にメッシュ内全体の通信経路を最適化するために変更されることがあります。
リレー依頼先ステーションが切断した場合、自動的に代替経路が探索されます。探索は短時間(理想的環境では RTT ぐらいの時間)で反映されますが、その間、上記リレー経路選定条件は一時的に無視され、接続の維持が優先されます。ホストはリレー経路選定条件を満たすように新しいリレー依頼先ステーションを探索、決定し、クライアントに新しい経路情報を通知します。クライアントはホストからの経路情報を受信し次第、それに従った通信を開始します。ただし、ホストがリレー経路選定条件を満たす新しいリレー依頼先ステーションを見つけられない場合があります。この場合、一時的に代替経路として選択したリレー依頼先ステーションがリレー経路選定条件を満たしていなかったとしても、接続の維持を優先し、代替経路を使用し続けます。
直通の通信経路が無いステーション間でデータ送受信を行う場合に別のステーションを経由して送受信することを「リレー送受信」もしくは「転送」と表現します。
アプリケーションが Pia の送受信用 API (transport::UnreliableProtocol::Send/Receive など)を呼び出す場合には、直通の通信経路の有無に関わらず(経由地点の存在を意識することなく)最終宛先を指定するだけで送信可能です。
転送依頼されたステーションは、自分が宛先ではないデータを受信すると、宛先のアドレスに転送します。この処理は、同じ common::Scheduler::Dispatch() 内で行われます。
例として 図 7-8. リレー通信接続イメージ図 では、Station2 と Station4 の間に直通の通信経路がありません。このとき Station2 から Station4 へ送信する場合でも、アプリケーションは最終宛先である Station4 を指定すれば送信可能です。Pia 内部ではリレー接続処理によって選定された通信経路(例えば Route A)を使用してリレー送信が行われます。受信側となる Station4 では、アプリケーションは送信元が(Station1 ではなく)Station2 であるという情報が取得できます。
また、図 7-8. リレー通信接続イメージ図 では、Station2 と Station5 の間にも直通の通信経路がありません。仮に、Station2 と Station4 の通信経路が Route A、Station2 と Station5 の通信経路が Route D だったとします。このとき transport::UnreliableProtocol::SendToAll のように Station2 が Station4 と Station5 に同じデータを送信する場合、Pia 内では宛先として Station4 と Station5 の両方を設定した 1 つのデータが Station1 に送信されます。Station1 は、そのデータを Station4 と Station5 に転送します。このように Pia のリレー通信では重複した送信データは自動的に 1 つに統合され、消費する通信帯域が大きく削減されます。
ステーション同士の全ての組み合わせの経路について接続状態が書き込まれたテーブルを「リレー経路テーブル」と呼びます。リレー経路テーブルをトレース出力するには、common::TraceFlagTransportRelay のトレースフラグをオンにします。リレー経路テーブルは、更新が発生した際に、図 7-9. リレー経路テーブルのトレース のような表形式でトレース出力されます。
この表で上から i 行目(0 <= i <= 7)、左から j 列目(0 <= j <= 7)の値を k[i][j] とします。k[i][j] は、ステーション i (StationIndex == i)とステーション j (StationIndex == j)の接続状態を表します。
i != j のとき、各ステーション間の接続状態は以下のように区別できます。
・k[i][j] が "-" のとき:ステーション i とステーション j は切断されています。
・k[i][j] == j のとき:ステーション i とステーション j は直接接続されています。
・k[i][j] != i かつ k[i][j] != j のとき:ステーション i とステーション j は、ステーション k[i][j] を経由してリレー接続されています。
リレー接続している経路には、数値の右側に"*"が付いています。
この例では、ステーション 0 ~ ステーション 3 がメッシュに参加しており、ステーション 2 とステーション 3 はステーション 1 を経由してリレー接続されています(k[2][3] == 1、k[3][2] == 1)。その他の経路は全て直接接続されています。
リレー通信機能を有効にすることで、ステーション同士の接続に失敗してもメッシュへの参加に成功することができ、特に多数台接続時にはメッシュ参加失敗確率を大きく下げれます。ただし、次のようなケースの場合、メッシュへの参加に失敗します。
リレー通信経路の選定はホストが行うため、メッシュ参加処理で最初の 1 台であるホストとの接続に失敗した場合、メッシュへの参加に失敗します。この場合にメッシュ参加の非同期処理の結果 Result (session::Session::GetJoinSessionResult で取得可能)はリレー通信機能無効時の失敗結果と同等の Result となります。
ただし、ランダムマッチメイク使用時には、P2P 接続に失敗しやすい回線(EDMなど)で接続しているステーションはマッチメイクセッション作成遅延によってホストになりにくいようになっているため(詳しくは、NEX プログラミングマニュアルの「オートマッチメイク時の新規マッチメイクセッションの作成遅延」を参照してください)、自ステーションが EDM 回線でなければ通常参加成功します。また、自ステーションが EDM 回線であったとしても最初の 1 台となるホストとの P2P 接続には高い確率で成功します。
メッシュ内のすでにリレー通信を行っているステーションの割合が多い時など、上記の転送依頼数上限値を超える経路しか見つからなかった場合、メッシュへの参加に失敗します。この場合にメッシュ参加非同期処理の結果 Result (session::Session::GetJoinSessionResult で取得可能)は ResultRelayFailedRelayNumLimit になります。
メッシュ内のステーション同士の通信遅延が大きい時など、上記の通信遅延上限値を超える経路しか見つからなかった場合、メッシュへの参加に失敗します。この場合にメッシュ参加非同期処理の結果 Result (session::Session::GetJoinSessionResult で取得可能)は ResultRelayFailedRttLimit になります。
メッシュ内で通信可能な経路が特殊な状況で、新規参加ステーションと他ステーションの間で選択可能なリレー経路が 1 ステーション以下にならない場合は、メッシュへの参加に失敗します。この場合にメッシュ参加非同期処理の結果 Result (session::Session::GetJoinSessionResult で取得可能)は ResultRelayFailedNoCandidate になります。
リレー接続処理関連でメッシュ参加失敗となる場合は、前述の複数の制限に同時にかかっている場合があります。その場合でも処理結果 Result はかかった制限の内の 1 つを表すものになります。
リレー通信のデバッグ用機能として、接続経路エミュレーション機能があります。
接続経路エミュレーション機能を使用することで、任意のステーション間がリレー接続になるように指定できます。
Session クラスのインスタンス生成前に、接続経路エミュレーション機能が有効になるようにデバッグ機能設定(session::Session::SetDebugSetting)を呼び出す必要があります。
// DebugSetting 構造体の設定
nn::pia::session::Session::DebugSetting debugSetting; // 接続経路エミュレーション設定のフラグを有効に指定 debugSetting.isRelayEmulationEnabled = true; // DebugSetting 構造体の設定 終了 result = nn::pia::session::Session::SetDebugSetting(debugSetting); if (result.IsFailure()) { // エラー処理 // 適切なタイミングで呼び出している限り、失敗しません } // 上記の処理成功後に Session::CreateInstance を呼び出して Session クラスのインスタンスを生成することで // 接続経路エミュレーション機能が有効な状態になります。 |
任意の StationIndex を2つ指定することで、指定した StationIndex が割り当てられた2つのステーション間がリレー接続になります。
メッシュに接続していないタイミングでのみ、指定内容を変更できます。同じメッシュに参加するステーションは、全員同じ指定をする必要があります。
// 接続前であるとします
// STATION_INDEX_2 と STATION_INDEX_3 の間および StationIndex_2 と StationIndex_4 の間がリレー接続になるように設定するための引数です。 nn::pia::StationIndex targetIndexA = nn::pia::StationIndex_2; nn::pia::StationIndex targetIndexB = nn::pia::StationIndex_3; nn::pia::StationIndex targetIndexC = nn::pia::StationIndex_4; result = nn::pia::session::Session::GetInstance()->RelayEmulation(targetIndexA, targetIndexB); if (result.IsFailure()) { // エラー処理 // 適切なタイミング、引数で呼び出している限り、失敗しません } // 同時に別のステーション間の接続経路についてエミュレーション設定をすることも可能です。 result = nn::pia::session::Session::GetInstance()->RelayEmulation(targetIndexA, targetIndexC); if (result.IsFailure()) { // エラー処理 // 適切なタイミング、引数で呼び出している限り、失敗しません } // 接続するステーション全員が上記設定を行った後に接続処理を行うと、StationIndex_2 と StationIndex_3 の間および StationIndex_2 と StationIndex_4 の間がリレー接続になります。 |