6.5. 応用機能 - ReliableBroadcastProtocol

概要

ReliableBroadcastProtocol は、ある程度大きなサイズのデータを複数台のステーションに送信するためのプロトコルです。到達保証があり、セッションホスト/セッションクライアントの種別に関係なく、任意のステーションがデータ送信側となることができます。

CTR のローカル通信、LAN 通信においては、ブロードキャスト送信を行いますので、複数台の相手に効率よくデータを送信することが可能です。

送受信処理の流れ

ReliableBroadcastProtocol を利用する場合の基本的な処理の流れは以下の通りです。

  1. ReliableBroadcastProtocol の初期化処理を行います。
  2. 送信側ステーションがデータを準備します。
  3. セッション参加中の送信側ステーションが SendStart() を呼び出し、送信の準備をします。これにより、受信要求が他のステーションに通知されます。
  4. セッション参加中のステーションに、送信側からの受信要求が届きます。この受信要求の内容は GetRequest() で取得できます。
  5. この受信要求を受諾する場合は StartReceive() を呼び出します。これにより、送受信処理が開始されます。
  6. 送受信が終了します。
  7. ReliableBroadcastProtocol の終了処理を行います。

以下、各項目について解説します。

初期化処理

ReliableBroadcastProtocol の初期化処理のコーディング例を以下に示します。

コード 6-18. 初期化処理
static uint32_t s_Handle = 0; // ReliableBroadcastProtocol 用のハンドル
 
void initializeTransport()
{
    // transport のセットアップ区間開始。
    nn::pia::Result result = nn::pia::transport::BeginSetup();
    PIA_ASSERT(result.IsSuccess());

    // PiaTransport におけるのシングルトン作成などのコード
    ...
    ...

    nn::pia::transport::Transport* pTransport = nn::pia::transport::Transport::GetInstance();
    PIA_ASSERT(PIA_IS_VALID_POINTER(pTransport));

    // ReliableBroadcastProtocol の作成と初期化。BeginSetup() ~ EndSetup() 区間内で行います。
    const uint16_t ReliableBroadcastProtocolPort = 0; // ReliableBroadcastProtocol が使用するポート番号。
    s_Handle = pTransport->CreateProtocol<nn::pia::transport::ReliableBroadcastProtocol>(ReliableBroadcastProtocolPort);
    PIA_ASSERT(s_Handle != 0); // CreateProtocol()は、既に同じプロトコルIDが存在していた場合は0を返すので、そうではないことを確認
    result = pTransport->GetProtocol<nn::pia::transport::ReliableBroadcastProtocol>(s_Handle)->Initialize();
    PIA_ASSERT(result.IsSuccess());

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

データの準備

送信するデータはあらかじめアプリケーションが作成しておき、サイズを確定させておく必要があります。ストリーミング送信には対応していません。

送信の準備

送信側は、送信データとそのサイズ、転送における振る舞いを StartSend() に渡して呼び出します。転送における振る舞いは、Configuration クラスのインスタンスで設定します。設定可能な振る舞いは以下の通りですが、詳細は API リファレンスを参照してください。

  • 送信先に受信拒否された場合の振る舞い
  • 複数のステーションが同時に送信しようとした場合の振る舞い
  • 全ての送信先に対して送信が完了した際の振る舞い
  • 途中で合流したステーションに対する振る舞い
  • 受信完了の同期に関する振る舞い

StartSend() 呼び出しにより、送信側は他のステーションに向けて受信要求を送出します。

コード 6-19. 送信の準備
// ReliableBroadcastProtocol のポインタを得る関数
nn::pia::transport::ReliableBroadcastProtocol* GetReliableBroadcastProtocol()
{
    PIA_ASSERT(PIA_IS_VALID_POINTER(nn::pia::transport::Transport::GetInstance()));
    return nn::pia::transport::Transport::GetInstance()->GetProtocol<nn::pia::transport::ReliableBroadcastProtocol>(s_Handle);
}
 
const uint32_t SendDataSize = 8 * 1024 * 1024; // 送信データサイズ
static uint32_t s_SendData[SendDataSize/sizeof(uint32_t)]; // 送信データの実体
static nn::pia::transport::ReliableBroadcastProtocol::Configuration s_SendConfig; // データ転送の動作方式

void prepareSend()
{
    // データ送信の準備
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    nn::pia::Result result = pProtocol->StartSend(s_SendData, sizeof(s_SendData), s_SendConfig);
    if (result.IsFailure())
    {
        if (result == nn::pia::ResultTemporaryUnavailable())
        {
            // セッション移行処理中のため、一時的に API を利用できない状態です。ジョイントセッション機能使用時にのみ返ります。
        }
        else
        {
            PIA_TRACE_RESULT_ALWAYS(result);
            PIA_ASSERTMSG(false, "Programming error.");
        }
    }
}

受信要求の到着

送信側の StartSend() 呼び出しにより、セッション参加中のステーション(送信側以外)に受信要求が届きます。この受信要求の有無は GetRequest() で判定できます。受信要求が届いていなければ GetRequest() は NULL を返します。

受信の開始

StartReceive() 呼び出しにより、送信側から届いた受信要求を受諾し、データの送受信が開始されます。

コード 6-20. 受信の開始
const uint32_t ReceiveBufferSize = 8 * 1024 * 1024; // 受信バッファサイズ
static uint32_t s_ReceiveBuffer[ReceiveBufferSize/sizeof(uint32_t)]; // 受信バッファの実体
 
void startReceive()
{
    // 受信要求が届いていたら、データ受信開始。
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    const nn::pia::transport::ReliableBroadcastProtocol::TransferSetting* cpRequest = pProtocol->GetRequest();
    if (cpRequest != NULL)
    {
        nn::pia::StationId srcId = cpRequest->GetSourceStationId();
        PIA_ASSERT(srcId != nn::pia::StationIdInvalid);
        nn::pia::Result result = pProtocol->StartReceive(s_ReceiveBuffer, sizeof(s_ReceiveBuffer), srcId);
        if (result.IsFailure())
        {
            if (result == nn::pia::ResultTemporaryUnavailable())
            {
                // セッション移行処理中のため、一時的に API を利用できない状態です。ジョイントセッション機能使用時にのみ返ります。
            }
            else
            {
                PIA_TRACE_RESULT_ALWAYS(result);
                PIA_ASSERTMSG(false, "Programming error.");
            }
        }
    }
}

送受信の終了

受信の終了判定には GetState() が利用できます。受信側がデータを全て受信すると、GetState() は State_ReceiveSuccess を返すようになります。
一方、送信側による GetState() の返り値は Configuration::IsSendEndless() の設定に左右されます。詳細は API リファレンスを参照してください。

終了処理

ReliableBroadcastProtocol の終了処理例を以下のコードで示します。

コード 6-21. 終了処理
void finalizeTransport()
{
    nn::pia::transport::Transport* pTransport = nn::pia::transport::Transport::GetInstance();
    PIA_ASSERT(PIA_IS_VALID_POINTER(pTransport));
 
    // ReliableBroadcastProtocol の終了処理
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = pTransport->GetProtocol<nn::pia::transport::ReliableBroadcastProtocol>(s_Handle);
    if (PIA_IS_VALID_POINTER(pProtocol))
    {
        pProtocol->Finalize();
        pTransport->DestroyProtocol(s_Handle);
        s_Handle = 0;
    }
}

応用的な操作

上記は基本的な操作です。以下、応用的な操作について解説します。

StartReceive() の先行呼び出し

アプリケーションの仕様により、送信側/受信側が事前に確定している場合は、送信側が StartSend() を呼び出す前に受信側が StartReceive() を呼び出すことも可能です。StartReceive() を先に呼び出しておくと、データの取りこぼしを少しだけ減らせるので、送受信の時間を若干短縮できます。

受信要求の拒絶

前述の通り、受信要求は GetRequest() で取得できますが、この受信要求を拒絶することもできます。受信要求の拒絶には、RejectRequest() を使用します。

コード 6-22. 受信要求の拒絶
void rejectRequest()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    nn::pia::Result result = pProtocol->RejectRequest();
    PIA_ASSERT(result.IsSuccess());
}

送受信のキャンセル

送受信を途中でキャンセルするには、Cancel() を呼び出します。

コード 6-23. 送受信のキャンセル
void cancel()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    enum nn::pia::transport::ReliableBroadcastProtocol::State state = pProtocol->GetState();
    if(state == nn::pia::transport::ReliableBroadcastProtocol::State_Sending ||
       state == nn::pia::transport::ReliableBroadcastProtocol::State_Receiving)
    {
        // 送受信を中断します。
        nn::pia::Result result = pProtocol->Cancel();
        PIA_ASSERT(result.IsSuccess());
    }
}

送受信の進捗確認

送受信の進捗状況は GetProgress() で確認できますが、GetProgress() が返す値の意味は受信側と送信側で異なることに注意してください。
受信側が呼び出した GetProgress() は単に受信したデータサイズを返しますが、送信側が呼び出した GetProgress() は、送信対象のステーションに対して送信したデータサイズの平均値が返されます。

コード 6-24. 送受信の進捗確認
void printProgress()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    const nn::pia::transport::ReliableBroadcastProtocol::TransferSetting* cpSetting = pProtocol->GetCurrentTransferSetting();
    if (cpSetting != NULL)
    {
        // 受信側の進捗をプリントします。
        uint32_t total = cpSetting->m_DataSize;
        uint32_t current = pProtocol->GetProgress();
        float rate = static_cast<float>(current) / static_cast<float>(total);
        PIA_BASIC_REPORT("%7dB / %7dB (%5.1f%%)", current, total, rate * 100.0f);
    }
}

リセット

送受信結果の情報を破棄し、State_Wait 状態に戻るときには Reset() を呼び出します。ただし、送受信実行中に Reset() を呼び出すことはできません。詳細は API リファレンスを参照してください。

コード 6-25. リセット
void reset()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    PIA_ASSERT(pProtocol->IsRunning() == false);
 
    nn::pia::Result result = pProtocol->Reset();
    PIA_ASSERT(result.IsSuccess());
}