5. エラー処理

P2P 通信でのエラーをハンドリングするには二種類の方法でエラーを検知する必要があります。 NetZ::GetFatalError()で取得できるNEXライブラリ処理中に発生するエラーと、SystemError::GetLast()で取得できるP2PのAPI呼び出し時のエラーです。

前者の NetZ::GetFatalError()で取得できるエラーは、アプリケーションでエラーコードを取得して画面に表示する必要があります。 後者の SystemError::GetLast()で取得できるエラーは、基本的には開発段階で取り除かれるべきもので、アプリケーションが続行不能なときに限りエラーコードを取得して画面に表示してください。

5.1. NEXライブラリ内部のP2P処理中のエラーハンドリング・エラーコードの表示[必須]

このエラーはアプリケーション側で必ずハンドリングしていただき、エラーコードを画面に表示する必要があります。 NetZインスタンスが生成されているときに、NEXライブラリ内部の処理で発生する P2P 通信のエラーは NetZ::GetFatalError()で取得できます。

アプリケーションは以下の条件で、毎フレームに1回の頻度で NetZ::GetFatalError()でエラーが発生していないか必ずチェックしてください。 NEXのAPI呼び出し毎などにチェックする必要はありません。

  • NetZインスタンスが生成されいるとき
  • NetZインスタンスの破棄中( NetZ::Terminate()、delete中)でないとき

NetZ::GetFatalError() で返る qResult を QFAILED でチェックするか、false と比較して、失敗を示すqResultかどうかを判定してください。

NetZ::GetFatalError() のハンドリング方法、コード例を以下に示します。

  • 失敗を示す qResultNetZ::GetFatalError で返った場合
    • 復帰不能な何らかのエラーがNEXライブラリ内で発生しています。
    • エラーを保存して速やかにNetZインスタンスを破棄し、ErrorCodeConverter::ConvertToNetworkErrorCodeにてエラーコードを取得して必ず画面に表示してください。
  • 成功を示す qResultNetZ::GetFatalError で返った場合
    • アプリケーションで特別なハンドリングを行う必要はありません。
Code 5.1 P2P 通信中の復帰不能なエラーのハンドリング例
// ・NetZインスタンスが生成されているとき
// ・NetZインスタンスの破棄中でないとき
// 毎フレームの頻度でエラーチェック
NetZ* pNetZ = NetZ::GetInstance();
if (pNetZ)
{
    qResult fatal = pNetZ->GetFatalError();
    if ( QFAILED(fatal.GetReturnCode()) ) // または if ( fatal == false ) でも可
    {
        // 何らかのフェイタルエラーが発生

        // エラーコード取得
        qUnsignedInt32 errcode = ErrorCodeConverter::ConvertToNetworkErrorCode(fatal);


        // 速やかにNetZを破棄する


        // エラーコードを画面に表示する

    }
}
Table 5.1 NetZ::GetFatalError で発生する可能性のあるエラー
NetZ::GetFatalErrorの返り値 エラー内容
QSUCCESS(Core, Success) エラーが発生していないときに返る成功を示す値
QERROR(DOCore, InvalidState) DuplicatedObjectが不正なステートになった
QERROR(DOCore, FaultRecoveryFatal) DuplicatedObjectのフォルトリカバリー処理の開始に失敗した
QERROR(DOCore, FaultRecoveryJobProcessFailed) DuplicatedObjectのフォルトリカバリー処理中に失敗した
QERROR(DOCore, StationInconsistency) ステーション数がセッションマスターと違う状態が続きました
QERROR(Transport, ReliableSendBufferFullFatal) リライアブル通信に使用するバッファが足りなくなった
QERROR(Transport, NoBuffer) ダイレクトストリームにおける受信バッファの上限を超えた受信があった

5.1.1. リライアブル用のバッファが足りなくなった場合の対処

NetZ::GetFatalError で取得できるエラーは、現状アプリケーションでの回避方法はありません。 ただし開発段階においては、リライアブル送信を行いすぎると 通信環境が悪くなくても QERROR(Transport, ReliableSendBufferFullFatal) が発生します。 その場合は以下のような手法で回避してください。

  • リライアブル通信の頻度を下げる
  • リライアブル通信をアンリライアブル通信に切り替える(必要であればアンリライブル通信で独自に到達確認を行なってください)

補足

DirectStream::Send は、余裕をもってシステムの限界よりも先に QERROR(Transport, ReliableSendBuffer) を返すだけで、NetZのフェイタルエラーは発生しません。

NetZ::GetFatalError でQERROR(Transport, ReliableSendBufferFullFatal)が返るのは、 DOによる高頻度なリライアブル通信と、NEX内部でのリライアブル通信が疑われますが、 NEX内部での通信量はバッファに比べて十分小さいので、DOによるリライアブル通信頻度か、通信環境が問題である可能性が高いです。

5.2. SystemError::GetLast()呼び出しによるエラーハンドリング・エラーコードの表示[ゲーム進行不能になる場合は必須]

このエラーはゲーム進行が不能になる場合にアプリケーション側でハンドリングしていただき、エラーコードを画面に表示する必要があります。

NEX APIには、関数の実行や非同期処理のリクエストに失敗したときにfalseやNULLポインタを返すものがあります。 このとき、エラー内容を取得できるように設計されたNEX APIは、そのエラーを SystemError::GetLast()で取得することができます。 なお、このエラーはアプリケーションのプログラミングエラーが疑われるもので、基本的には開発段階で取り除かれるべきものです。

SystemError::GetLast()で取得できるエラーは、これらの関数を呼び出したスレッドで発生したエラーに限定されます。つまり、別スレッドで発生したエラーは取得できません。

エラーを分かりやすく解釈できる文字列を SystemError::GetErrorString()で取得できますのでデバッグにお役立てください。 この文字列を製品版で画面表示する必要はありません。

Code 5.2 Session::JoinSessionでエラーが発生したときのハンドリング例
CallContext cc;
if (Session::JoinSession(&cc, lstURL) == false)
{
    // JoinSessionがAPI呼び出しで失敗

    // Session::JoinSessionでセットされたエラーを取得
    qUnsignedInt32 last = SystemError::GetLast();

    // デバッグ用
    qChar msg[128];
    SystemError::GetErrorString(last, msg, 128);
    SAMPLE_LOG("JoinSession is failed. System last error:%u, Error:%ls", last, msg);

    // エラーコードを画面に表示する
    qUnsignedInt32 errcode = ErrorCodeConverter::ConvertToNetworkErrorCode(last);
}