6. セッション・ステーション
ステーションのひとつのグループを P2P セッションといい、以後、単にセッションと言った場合には、 P2P セッションを指します。 セッションの管理者のことを、セッションマスターといい、 P2P 通信の責任者であるホストと一致した端末となります。 セッションに参加している各端末をステーションと呼び、自端末のことをローカルステーションと呼びます。
インターネット通信時には、NetZ初期化前に P2P 通信で使用するポートの自動選択処理や NAT トラバーサル機能の初期化が必要です。 詳細は、 4. を参照してください。
6.1. セッション管理
6.1.1. セッションマスターの役割
セッションマスターは、セッション全体の情報を管理して、セッションへの参加受付や、セッション内で 同期が取られているセッションクロックの管理、セッション内で共有のDOの作成を行います。 セッションマスターであるか否かは、 Session::GetRole()で取得可能です。
最初のセッションマスターは、 Session::CreateSession()によりセッションを作成したステーションです。 このステーションがセッションを離れると、ステーションのリストで二番目のステーションを新しいセッションマスターとして選びます。
セッションマスターは、セッションの情報を管理し、変更ができます。 ローカルステーションのセッション情報は、Session::GetLocalSessionDescription()で取得できます。 セッションの情報は、 SessionDescriptionクラスで管理され、アプリケーションタイトル、セッション名、セッション ID、 ユーザー定義属性、セッションのライブラリバージョンとビルド番号などが含まれます。
6.1.2. セッションの作成
セッションは、 Session::CreateSession() を使用してセッションマスターによって作成されます。
セッションのステータスは、Session クラスを使用して制御されます。 このクラスはシングルトンなので、アクセスのグローバルポイントがあるインスタンスを 1 つのみ持ちます。 つまり、各NetZ インスタンスに 1 つの Session のみが作成されます。 Session クラスの主な機能は、セッションを作成可能および参加可能にし、セッションに関する特定の情報が設定され、検索されるようにすることです。 ステーションがセッションの一部になると、そのステーションに関する特定の情報が Station クラスを使用して検索できます。
ローカル通信のセッションを作る際、UDSネットワークのマスターが Session::CreateSession()を呼び出す必要があります。
6.1.3. セッションへの参加
すでに存在するセッションに参加するには、次の構文のいずれかを使用して Session::JoinSession()を呼び出します。
qBool Session::JoinSession(CallContext* pCallContext,
const StationURL& refStationURL); //ローカル通信用 非同期
qBool Session::JoinSession(const StationURL& refStationURL); //ローカル通信用 同期
qBool Session::JoinSession(CallContext* pCallContext,
const qList< StationURL > & lstURLs); //インターネット通信用 非同期
qBool Session::JoinSession(const qList< StationURL > & lstURLs); //インターネット通信用 同期
上記、refStationURL や、 lstURLs は、 nn::pia::local::UdsNetwork::GetMasterUrl()や MatchMakingClient::GetSessionURLs()で取得できるセッションの参加に使用するステーションの URL への参照です。 URLリストには参加先のグローバルIPやローカルIP情報が含まれているので、一つのみ取り出しても正しく動作できません。 ローカル通信用のメソッドを、インターネット通信時に利用すると、Assertによる強制終了もしくは、Joinに失敗します。
pCallContext は、呼び出しの CallContext へのポインタです。 定義されている場合、呼び出しは非同期的に行われます。 定義されていない場合は同期的に行われます。 呼び出しが同期である場合、システムは呼び出しが値またはエラーを返すまで、次のタスクの実行を待機します。 非同期の場合、呼び出しが完了するのを待機する間に他のタスクを実行できます。
Session::JoinSession()が終了する前に、すべての WellKnownDO とすべての IDGenerator コアオブジェクトが検出され、 これらのオブジェクトが UserDO の前に検出されることが保証されています。 また、 Session::JoinSession()内では UserDO が検出されないことが保証されています。 Session::JoinSession() を呼び出した後で Scheduler::Dispatch() を呼び出す場合にのみ最初の UserDO が検出されます。
NetZ のスレッドモデルの詳細については、 スレッドモードの設定 を参照してください。
6.1.4. セッションの離脱
Session オブジェクトを削除する唯一の方法は NetZ インスタンスを削除することです。 そのため、セッションから離脱するには、NetZ インスタンスを削除する必要があります。
NetZ インスタンスが破棄されると、ローカルステーションに存在するセッションを含めたすべての DO が削除されます。
6.1.5. LAN中セッションの検索と参加[デバッグ用]
P2P セッションを LAN で参加できるようにするため、NetZ には LANSessionDiscovery クラスが用意されています。 LANSessionDiscovery クラスを用いると同一 LAN に存在する P2P セッションを探すことができます。 このクラスはセッションマスターとなっている端末と同一の LAN で動作している端末でのみ利用可能です。 デバッグ用途以外での利用はできず、ローカル通信利用時には設定不要です。 LANSessionDiscovery::Enable()とすると、LAN上に作られた他のセッションを Session::QuerySessions()で参照できるようになります。
Session::QuerySessions()は Session::CreateSession()するか、Session::JoinSession()する前に呼び出される必要があります。
例えば、LANの P2P セッションを検索し、セッションの記述からのさまざまな情報を出力するには、次の通りです。
LANSessionDiscovery::Enable();
.....
std::list<SessionDescription> lstResult;
if (Session::QuerySessions(&lstResult,false)==0)
{
QLOG(EventLog::Error, NEX_T("No session found."));
}
else
{
std::list<SessionDescription>::iterator it=lstResult.begin();
while (it!=lstResult.end())
{
// Display relevant information from SessionDescription
QLOG(EventLog::Info, NEX_T("Session ID : ") << it->GetSessionID());
}
}
また、LANの P2P セッションを検索して、リストで一番最初のセッションに参加するコードは以下の通りです。
LANSessionDiscovery::Enable();
.....
std::list<SessionDescription> lstResult;
if ( Session::QuerySessions(&lstResult,false) == 0 )
{
QLOG(EventLog::Error, NEX_T("No session found."));
}
else
{
if (! Session::JoinSession(&lstResult.begin()) )
{
QLOG(EventLog::Error, NEX_T("Join Failed."));
}
}
6.2. ステーション
ステーションは、セッションが存在する限り存在するローカルステーションと、検出時に生成されて離脱時に削除されるピア(対向)ステーションの二つに分かれます。 それぞれ、固有のStationオブジェクトが生成されます。
Stationオブジェクトには、ステーション状態、 DOHandle、セッション内で固有のグローバルステーション識別子である StationIDなどが含まれます。 Stationオブジェクトが、ローカルステーションかピアステーションかを、Station::IsLocal() と Station::IsAPeer() を使用して調べることができます。 また、別のステーションと通信ができるかをStation::IsConnected()を使用して調べることができます。
6.2.1. ステーションの状態遷移
ステーションの現在の状態は Station::GetState() で取得でき、 Station::Unknown、 Station::JoiningSession、 Station::CreatingSession、 Station::Participating、 Station::Leaving、 Station::LeavingOnFault のいずれかで、 Figure 6.1 に示すように遷移します。
6.2.2. ステーションのセッション入退出検知
以下の関数を使ってステーションの参加・離脱タイミングを知ることができます。
API | 内容 |
---|---|
Station:: |
ステーションの参加、離脱の処理開始時に呼ばれるコールバックを設定します |
Station:: |
ステーションの参加、離脱の処理処理完了時に呼ばれるコールバックを設定します |
主な使用用途を以下に説明します。
- Station::RegisterOperationBeginCallback() で登録したコールバックが呼ばれたときの用途
- JoinStationOperation( StationOperation::GetType()がOperation::JoinStation)
- ステーションの参加処理開始を示します。
- JoinStationOperation::GetTargetStation() で取得できるステーションはまだ不完全です。
- この時点から、DirectStream のデータを受信する可能性があり、NEX内部のバッファに溜められて参照可能になりますが、参加処理完了後の受信データ参照を推奨します。
- LeaveStationOperation( StationOperation::GetType()がOperation::LeaveStation)
- ステーションの離脱処理の開始を示します。
- LeaveStationOperation::GetTargetStation() で取得できるステーションに関連する、アプリケーション独自データの削除を行う場合は、これ以降に行うと良いでしょう。
- コールバック引数 StationOperation pOperation から、 pOperation->GetTargetStation()->GetState() で、離脱理由が正常終了 (Station::Leaving)であるか、タイムアウトなどによるフォルト( Station::LeavingOnFault)処理によるものか判断できます。
- Station::RegisterOperationEndCallback() で登録したコールバックが呼ばれたときの用途
- JoinStationOperation( StationOperation::GetType()がOperation::JoinStation)
- ステーションの参加処理完了を示します。
- JoinStationOperation::GetTargetStation() で取得できるステーションはすでに参加処理が完了しているので、アプリケーション独自のデータを DirectStreamやRMCで送信したい場合には、これ以降に行ってください。
- タイミングによっては、このステーションからのパケットがこれより前に届くことがあることがあります。
- LeaveStationOperation( StationOperation::GetType()がOperation::LeaveStation)
- ステーションの離脱処理完了を示します。
- アプリケーション側での使用推奨例は特にありません。
サンプルコードについては AutoMatchサンプル を参照してください。
6.2.3. ステーションの消失検知
NEXは、ネットワークの障害や対向ステーションの電源OFFや無線LANのOFFなど、正常ではないステーションの消失を検知するためにキープアライブパケットによる ステーションの生存監視を行います。
生存監視の動作は以下の通りです。
- StreamSettings::GetKeepAliveTimeout() で取得できる時間(デフォルト値は1000msec) 連続して通常のパケットの送信がなかったステーションに対して、キープアライブパケットを StreamSettings::GetKeepAliveTimeout()の間隔で送信します。
- キープアライブパケットを受け取ったステーションは、キープアライブパケットの応答を送り返します。
- StreamSettings::GetMaxSilenceTime() で取得できる時間(デフォルト値は10,000msec)連続して一切のパケット受信がないステーションを、もはや接続していないと判断します。
ステーションの消失を検出した場合、 Station::RegisterOperationBeginCallback()で登録したコールバックで状態変化を検出でき、 消失したステーションの Station::GetState()が Station::LeavingOnFaultとなります。
6.2.4. ステーションのプロトコル情報
NEXのプロトコルの統計情報は、Station::GetEndPointInfoInterface()で、統計操作オブジェクトを取得できます。 通信開始後、もしくは、 EndPointInfoInterface::ResetEndPointInfo() 実行後から統計情報を、 EndPointInfoInterface::GetEndPointInfo() で取得できます。取得できる情報は、 EndPointInfo構造体を参照してください。 これら統計は、BundlingPolicy クラスによるDOのメッセージバンドリング後、StreamBundling クラスの設定によるパケットバンドリング前の統計情報です。 実際に出力されるデータは、パケットバンドリングによりまとめられる可能性があります。
リライアブル通信の送信用バッファのパケット数は、 EndPointInfoInterface::GetReliableBufferPacketCount()、 EndPointInfoInterface::GetReliableBufferPacketCounts() で取得できます。
RTTについては、Station::GetRTT() と Station::GetRTTAverage() により特定のメッセージの往復遅延時間と、 ローカルステーションと別のステーション間で送信されたメッセージの平均往復遅延時間を直接取得することができます。
6.2.5. ステーションのバージョン、ユーザー定義情報
Station::SetLocalApplicationVersion()により、アプリケーションのバージョンを設定可能です。 ここで設定された値は、Station::GetApplicationVersion()で取得できます。
また、Station::SetLocalIdentificationToken() を使用してユーザー定義の文字列を特定のステーションに関連付けることができます。 識別トークンを使用する方法の 1 つは、ステーションがセッションに参加しようとする際にステーションにセッション参加の権限があるかどうかを判断するため、 パスワードまたはその他の識別タグを渡すことです。 この場合、情報は接続先のステーションに渡されるため、そのステーションが参加リクエストに関する決定を下すことができます。 識別トークンは参加するステーションオブジェクトに設定され、その後 Station::GetIdentificationToken() でアクセスできます。 識別トークンの使用方法の例については、 14. を参照してください。