6.2. PiaTransport 基本機能

PiaTransport の基本的な機能を利用する場合の、手順や注意事項を以下に説明します。

基本的な処理手順は以下のようになります。

  1. 初期化処理
  2. ディスパッチ関数呼び出し
  3. 接続状態のチェック
  4. 送受信 API 呼び出し
  5. 終了処理

PiaInet を利用する場合は、PiaTransport の初期化処理を開始する前に PiaInet の初期化を実行します。逆に、PiaTransport の終了処理は、PiaInet の終了処理よりも先に実行します。

PiaLocal を利用する場合は、PiaTransport の初期化処理を開始する前に PiaLocal の初期化を実行します。逆に、PiaTransport の終了処理は、PiaLocal の終了処理よりも先に実行します。

以下、それぞれの処理について説明します。

初期化処理

初期化処理のサンプルコードを以下に示します。

コード 6-3. PiaTransport の初期化
void initializePiaTransport()
{
    // PiaTransport の初期化。
    nn::pia::Result result = nn::pia::transport::Initialize();
    PIA_ASSERT(result.IsSuccess());

    // セットアップ区間開始。
    result = nn::pia::transport::BeginSetup();
    PIA_ASSERT(result.IsSuccess());


    // Transport インスタンスの設定
    nn::pia::transport::Transport::Setting setting;
    setting.pFactory = pFactory; // NetworkFactory 派生クラスのインスタンスを指すポインタ
    setting.maxStationNum = 8; // 一つのネットワークに参加可能な最大 Station 数
    setting.maxSendThreadBufferNum = 16; // 送信スレッドにおける内部バッファのサイズ
    setting.maxReceiveThreadBufferNum = 32; // 受信スレッドにおける内部バッファのサイズ
    setting.analysisInterval = 0; // 分析データの自動プリントを無効にする場合は 0 を設定

    // Transport シングルトンの構築
    result = nn::pia::transport::Transport::CreateInstance(setting);
    PIA_ASSERT(result.IsSuccess());


    // プロトコルなどを初期化するコード
    ...
    ...


    // セットアップ区間終了。
    result = nn::pia::transport::EndSetup();
    PIA_ASSERT(result.IsSuccess());
}

Transport シングルトンの設定は Transport::Setting 構造体で行います。内部バッファのサイズ設定などはメモリ消費量に直結しますので、アプリケーションの通信量に応じた適切なサイズを設定することが重要です。適切な設定のヒントについては後述します。

送受信 API 呼び出し

PiaTrasport におけるデータ送受信は、transport::Protocol クラスから派生した transport::UnreliableProtocol クラス、transport::ReliableProtocol クラス等のインスタンスを利用して行います。詳細は 基本機能 - UnreliableProtocol基本機能 - ReliableProtocol などを参照してください。

 

ゲームフレームにおける一連の PiaTransport API 呼び出しのサンプルコードを示します。

コード 6-4. ゲームフレームにおける PiaTransport API 呼び出し
while(true)
{
    nn::pia::common::Scheduler::GetInstance()->Dispatch(); // 1回目のディスパッチ関数呼び出し

    // UnreliableProtocol や ReliableProtocol 等の受信 API 呼び出し
    ...

    // ゲームオブジェクトの更新処理
    ...

    // UnreliableProtocol や ReliableProtocol 等の送信 API 呼び出し
    ...

    nn::pia::common::Scheduler::GetInstance()->Dispatch(); // 2回目のディスパッチ関数呼び出し
}

終了処理

PiaTransport の終了処理のサンプルコードを以下に示します。

コード 6-5. PiaTransport の終了処理
void finalizePiaTransport()
{
    // プロトコルなどの終了処理
    ...
    ...

    // PiaではFinalize、DestroyInstanceは常に成功します
    if (nn::pia::transport::Transport::GetInstance() != NULL)
    {
        nn::pia::transport::Transport::DestroyInstance();
    }
    nn::pia::transport::Finalize();
}

パケットへの署名

改ざんされたデータを解釈してしまうのを防ぐために、Pia のパケットには署名データを付けることができます。署名の処理は PiaTransport 層で実行されます。

セッション API を利用してインターネット通信を行う場合、署名は自動的に設定されます。ローカル通信の場合は低位レイヤーで暗号化が行われるので、デフォルト設定は署名オフですが、オンにすることも可能です。

詳細は PiaSession のプログラミングマニュアル/関数リファレンスなどを参照してください。

署名の作成/検証には MD5 アルゴリズムが用いられます。このとき MD5 による署名データ 16 バイトがパケットのペイロード領域に書き込まれるので、 アプリケーションが実データ送受信に利用できる領域は署名データの分だけ減少します。

キープアライブ

PiaTransport は自動的にキープアライブ通信を行います。

他ステーションへのパケット送出が一定時間(キープアライブパケット送信間隔)発生しなかった場合、自動的にキープアライブ用パケットが生成され、送信されます。デフォルトのキープアライブパケット送信間隔は 1 秒、最大無通信時間は 10 秒です。

キープアライブパケットの送信間隔や最大無通信時間は、nn::pia::session::Session::Startup() 関数で設定できます。詳細は Session クラスの関数リファレンスを参照してください。

最大無通信時間を短くすると、切断判定が速やかに行われるようになるので、プレイヤー体験の向上を図ることができます。一方、キープアライブパケットを受信する機会は減るため、パケロスへの耐性は低下します。 最大無通信時間を短くする場合は、同時にキープアライブパケット送信間隔も短くするなどして、パケロス耐性を必要以上に損なわないようにすることをおすすめします。

適切なサイズのメモリを供給するためのヒント

PiaTransport 内部では、アプリケーションが送受信するデータを一時的に保持するなどの処理を実行します。 そのため、PiaTransport が使用するメモリ量は他のモジュールと比べて大きく、Pia 全体におけるメモリ消費の大部分を占めます。 したがって、PiaTransport に必要十分なサイズのメモリを供給することは、アプリケーションがメモリを効率的に使用する上で重要です。

ここでは、適切なサイズのメモリを供給するためのヒントについて解説します。

内部バッファ

PiaTransport には送信スレッド用と受信スレッド用の内部バッファが存在しています。これらのバッファの詳細な解説は省きますが、内部バッファのサイズは Transport::CreateInstance() 呼び出し時に渡される Transport::Setting 構造体のパラメータで指定できます。メモリサイズのチューニング開始前は、ある程度余裕をもった値を設定しておくことをおすすめします。以下、設定例を示します。

コード 6-6. 内部バッファサイズの設定例(比較的余裕を持った値)
nn::pia::transport::Transport::Setting setting;
setting.pFactory = pFactory; // NetworkFactory 派生クラスのインスタンスを指すポインタ
setting.maxStationNum = 4; // 一つのネットワークに参加可能な最大 Station 数
setting.maxSendThreadBufferNum = 16; // 送信スレッドにおける内部バッファのサイズ
setting.maxReceiveThreadBufferNum = 32; // 受信スレッドにおける内部バッファのサイズ
コード 6-7. 内部バッファサイズの設定例(通信台数4台、128Byteのデータを30回/秒の頻度で全ステーションに ReliableProtocol で送信するケースでの一例)
nn::pia::transport::Transport::Setting setting;
setting.pFactory = pFactory; // NetworkFactory 派生クラスのインスタンスを指すポインタ
setting.maxStationNum = 4; // 一つのネットワークに参加可能な最大 Station 数
setting.maxSendThreadBufferNum = 6; // 送信スレッドにおける内部バッファのサイズ
setting.maxReceiveThreadBufferNum = 12; // 受信スレッドにおける内部バッファのサイズ

内部バッファ使用量のピーク値の取得

PiaTransport 内部バッファ使用量のピーク値を取得するには、ウォーターマーク機能が利用できます。

ウォーターマーク機能のセットアップについては、2.3. ウォーターマークを参照してください。ウォーターマーク機能による計測の開始は、PiaTransport の初期化を行う前に実行することをおすすめします。 計測を開始した後は、実際にアプリケーションによる一連の通信処理を実行します。 通信処理を完了するか、または計測に十分な量の通信処理を実行してから、以下のようなコードで計測結果を取得します。

コード 6-8. 計測結果の取得
void PrintWatermark()
{
    nn::pia::common::WatermarkManager* pMgr = nn::pia::common::WatermarkManager::GetInstance();
    if (PIA_IS_VALID_POINTER(pMgr))
    {
        for (int i = 0; i <= nn::pia::common::WatermarkManager::KeyMax; ++i)
        {
            nn::pia::common::Watermark* pMark = pMgr->GetWatermark(i);
            PIA_ASSERT(PIA_IS_VALID_POINTER(pMark));
            if (pMark->GetUpdateCount() > 0)
            {
                PIA_SAMPLE_PRINT("Watermark: <%s> max: %lld, min: %lld, latest: %lld, Updates: %lld\n",
                    pMark->GetName(),
                    pMark->GetMaxValue(),
                    pMark->GetMinValue(),
                    pMark->GetLatestValue(),
                    pMark->GetUpdateCount()
                );
            }
        }
    }
    else
    {
        PIA_SAMPLE_PRINT("It seems that WatermarkManager instance is not ready.\n");
    }
}

以下に示すのは、上記の手順で得られた結果の一例です。

コード 6-9. ウォーターマーク計測結果
Watermark: <SendThreadStream buffer num> max: 5, min: 2, latest: 5, Updates: 14342
Watermark: <ReceiveThreadStream buffer num> max: 6, min: 2, latest: 3, Updates: 15737

max: の右脇に表示されている値がバッファ使用量のピーク値です。このピーク値が、Transport::Setting 構造体のメンバ変数で指定した内部バッファサイズと等しい場合は、内部バッファが全て使用されたことを意味するので、アプリケーションの通信量に対して内部バッファが不足気味である可能性があります。 逆に、ピーク値が指定した内部バッファサイズよりも十分に小さい場合は、内部バッファに十分な余裕があったことを示します。 これらの結果を参考に、Transport::Setting 構造体の各メンバに適切な値を設定してください。