6.3. 基本機能 - UnreliableProtocol

概要

UnreliableProtocol は UDP に似たプロトコルで、以下のような性質があります。

  • データ到達保証がない
  • データの送信順に受信されるとは限らない
  • 一度に送信できるデータのサイズに制限がある

処理の流れ

以下、初期化処理、送信処理、受信処理、終了処理の流れを説明します。

初期化処理

最初に Transport::CreateProtocol() で UnreliableProtocol のインスタンスを作成します。インスタンス作成時に得られたハンドルを Transport::GetProtocol() に渡すとインスタンスへのポインタが得られますので、続いてUnreliableProtocol::Initialize() を呼び出します。これらはメモリアロケーションが伴う処理ですので、BeginSetup() ~ EndSetup() 区間の中で実行する必要があります。

コード 6-10. UnreliableProtocol の初期化処理
nn::pia::Result r = nn::pia::transport::BeginSetup();
PIA_ASSERT(r.IsSuccess());

// Transport クラスの初期化処理など
...
...

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

// UnreliableProtocol(データ到達保証のない通信)インスタンスの作成。
// CreateInstance()には、ポート番号を引数で渡すこともできます。ここではデフォルト引数の 0 を用いています。
// CreateProtocol() が返すハンドルは後で必要になりますので、保持しておきます。
m_hUnreliable = pTransport->CreateProtocol<nn::pia::transport::UnreliableProtocol>();


// このハンドルを用いてインスタンスへのポインタを取得し、 Initialize() を呼び出します。
// Initialize() には受信バッファの個数を指定できます。アプリケーションの通信量に応じた
// 適切な値を設定する必要があります。
nn::pia::transport::UnreliableProtocol* pUnreliable = NULL;
pUnreliable = nn::pia::transport::Transport::GetInstance()->GetProtocol<nn::pia::transport::UnreliableProtocol>(m_hUnreliable);
PIA_ASSERT(PIA_IS_VALID_POINTER(pUnreliable));
const uint32_t ReceiveBufferNum = 32;
r = pUnreliable->Initialize(ReceiveBufferNum);
PIA_ASSERT(r.IsSuccess());
 
// その他のクラスの初期化処理など
...
...

r = nn::pia::transport::EndSetup();
PIA_ASSERT(r.IsSuccess());

送信処理

送信処理は UnreliableProtocol::Send() で行います。

UnreliableProtocol はデータの到達保証をしないプロトコルですので Send() が成功の Result を返したとしてもデータが相手ステーションに届くとは限りません。

同一のデータを全ステーションに向けて送信する場合は、UnreliableProtocol::SendToAll() が利用できます。相手ごとに Send() を呼び出す場合に比べて内部処理が一括され、CPU 負荷が軽減されます。

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

コード 6-11. UnreliableProtocol の送信処理
// プロトコルインスタンスへのポインタを取得。
nn::pia::transport::UnreliableProtocol* pUnreliable = NULL;
pUnreliable = nn::pia::transport::Transport::GetInstance()->GetProtocol<nn::pia::transport::UnreliableProtocol>(m_hUnreliable);
PIA_ASSERT(PIA_IS_VALID_POINTER(pUnreliable));


 
// 送信の実行とエラーハンドリング
nn::pia::Result r = pUnreliable->Send(destStationId, data, dataSize);
if (r.IsSuccess())
{
    // OK. Do nothing.
}
else if (r == nn::pia::ResultNotFound())
{
    // destStationId のステーションはセッションに参加していません。
}
else if (r == nn::pia::ResultNotInCommunication())
{
    // 通信が可能な状態ではありません。
}
else if (r == nn::pia::ResultTemporaryUnavailable())
{
    // セッション移行処理中のため、一時的に API を利用できない状態です。ジョイントセッション機能使用時にのみ返ります。
}
else
{
    PIA_TRACE_RESULT(r);
    PIA_ASSERTMSG(false, "Programming error.");
}

受信処理

受信処理は UnreliableProtocol::Receive() で行います。

PiaTransport におけるデータ受信はポーリング方式です。取得できるデータが無くなると UnreliableProtocol::Receive() が ResultNoData を返しますので、このとき受信処理を打ち切るなどのハンドリングを行う必要があります。

コード 6-12. UnreliableProtocol の受信処理
nn::pia::Result r;

// プロトコルインスタンスへのポインタを取得
nn::pia::transport::UnreliableProtocol* pUnreliable = NULL;
pUnreliable = nn::pia::transport::Transport::GetInstance()->GetProtocol<nn::pia::transport::UnreliableProtocol>(m_hUnreliable);
PIA_ASSERT(PIA_IS_VALID_POINTER(pUnreliable));

// 受信データ格納用の変数群。
nn::pia::StationId srcId;
uint8_t buf[nn::pia::transport::UnreliableProtocol::MaxDataSize]; // 到着する可能性のある最大サイズで配列を確保しておきます。
uint32_t receivedSize;

// 到着したデータの受信とエラーハンドリング。
r = pUnreliable->Receive(&srcId, buf, &receivedSize, sizeof(buf));
if (r.IsSuccess())
{
    // データを無事に受信できたケースです。
    PIA_CACHED_PRINTF("Received %d bytes from Station 0x%016llx.\n", receivedSize, srcId);
}
else if (r == nn::pia::ResultNoData())
{
    // データ未到着のケースです。この場合は何もしません。
}
else if (r == nn::pia::ResultNotInCommunication())
{
    // 通信が可能な状態ではありません。
}
else if (r == nn::pia::ResultTemporaryUnavailable())
{
    // セッション移行処理中のため、一時的に API を利用できない状態です。ジョイントセッション機能使用時にのみ返ります。
}else
{
    PIA_TRACE_RESULT_ALWAYS(r);
    PIA_ASSERTMSG(false, "Programming error.");
}

終了処理

終了処理は UnreliableProtocol::Finalize() を呼び出した後、Transport::DestroyProtocol() で UnreliableProtocol インスタンスを破棄します。

コード 6-13. UnreliableProtocol の終了処理
nn::pia::transport::UnreliableProtocol* pUnreliable = pTransport->GetProtocol<nn::pia::transport::UnreliableProtocol>(m_hUnreliable);
if (PIA_IS_VALID_POINTER(pUnreliable))
{
    pUnreliable->Finalize();
    pTransport->DestroyProtocol(m_hUnreliable);
    m_hUnreliable = 0; // ハンドルのクリア
}

一度に送信可能なデータサイズ

UnreliableProtocol では、パケットのペイロードを超過するサイズのデータを一度の Send() / SendToAll() 呼び出しで送信することはできません。 一度に送信可能なデータサイズの最大値は UnreliableProtocol::MaxDataSize 定数で与えられますが、 パケットのペイロードサイズはパケットへの署名の有無や、インターネット通信における MTU 値の設定によって変化します。

これらの設定を考慮した上で、UnreliableProtocol の送信 API で送信可能なデータサイズの上限を取得する API が UnreliableProtocol::GetDataSizeLimit() です。なお、GetDataSizeLimit() で返される値が MaxDataSize を越えることはありません。

UnreliableProtocol の送信 API 呼び出し時に、GetDataSizeLimit() を超過するサイズのデータを送信しようとすると、送信は失敗し、ResultInvalidArgument が返されます。