5.5. Advanced Features - ReliableBroadcastProtocol

Description

Use ReliableBroadcastProtocol to send relatively large amounts of data to multiple stations. With guaranteed delivery, any station can send data regardless of whether the station is the session host or client.
This allows you to efficiently send data to multiple stations at one time—particularly when using local communication networks on the CTR because data can be sent and received using the broadcast feature in the UDS library.

Send and Receive Process Flow

The basic process flow when using ReliableBroadcastProtocol is as follows.

  1. Initialize ReliableBroadcastProtocol.
  2. The sending station prepares the data.
  3. The sending station, while participating in a session, calls the SendStart() function and prepares to send. This sends a receive request notification to the other station.
  4. The other stations that are participating in a session receive the receive request from the sender. The content of this receive request can be obtained with the GetRequest() function.
  5. Call the StartReceive() function if the station accepts the receive request. This starts the send and receive processes.
  6. Sending and receiving complete.
  7. Terminate ReliableBroadcastProtocol.

Each item is described in more detail below.

Initialization

The following code sample shows the initialization of ReliableBroadcastProtocol.

Code 5-18. Initialization
static u32 s_Handle = 0; // Handle for ReliableBroadcastProtocol.
 
void initializeTransport()
{
    // Start the transport setup segment.
    nn::Result result = nn::pia::transport::BeginSetup();
    PIA_ASSERT(result.IsSuccess());

    // Code for creating a singleton of PiaTransport and the like.
    ...
    ...

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

    // Creation and initialization of ReliableBroadcastProtocol. Occurs during the interval between the BeginSetup() and EndSetup() functions.
    const u16 RELIABLE_BROADCAST_PROTOCOL_PORT = 0; // The port number used by ReliableBroadcastProtocol.
    s_Handle = pTransport->CreateProtocol<nn::pia::transport::ReliableBroadcastProtocol>(RELIABLE_BROADCAST_PROTOCOL_PORT);
    PIA_ASSERT(s_Handle != 0); // Make sure that the return value is not 0. The CreateProtocol() function returns 0 if the same protocol ID already exists.
    result = pTransport->GetProtocol<nn::pia::transport::ReliableBroadcastProtocol>(s_Handle)->Initialize();
    PIA_ASSERT(result.IsSuccess());

    // End the transport setup segment.
    result = nn::pia::transport::EndSetup();
    PIA_ASSERT(result.IsSuccess());
}

Preparing Data

The application must create the data to be sent in advance and determine the data size. Data streaming is not supported.

Preparing to Send

The sender passes the send data, its size, and the behavior to occur at transfer to the StartSend() function and calls it. The transfer behavior is configured by an instance of the Configuration class. The configurable behaviors are as follows. For more information, see the API reference.

  • Behavior when the send target denies the receive request
  • Behavior when multiple stations attempt to send at the same time
  • Behavior when sending has completed for all targets
  • Behavior for a station that joined midway
  • Behavior related to the synchronization of receive completion

Calling the StartSend() function submits a receive request by the sender to the other station.

Code 5-19. Preparing to Send
// Function to get a pointer for 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 size_t SEND_DATA_SIZE = 8 * 1024 * 1024; // The size of the data to send.
static u32 s_SendData[SEND_DATA_SIZE/sizeof(u32)]; // The instance of the send data.
static nn::pia::transport::ReliableBroadcastProtocol::Configuration s_SendConfig; // Data transfer mode.

void prepareSend()
{
    // Preparations to send data.
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    nn::Result result = pProtocol->StartSend(s_SendData, sizeof(s_SendData), s_SendConfig);
    PIA_ASSERT(result.IsSuccess());
}

Arrival of the Receive Request

When the sender calls the StartSend() function, a receive request is delivered to other stations participating in sessions (excluding the sender). The GetRequest() function can determine whether this receive request arrived. The GetRequest() function returns NULL if the request has not been delivered.

Starting Receiving

The calling of the StartReceive() function accepts the receive request that has arrived from the sender and starts the sending and receiving of data.

Code 5-20. Starting Data Reception
const size_t RECEIVE_BUFFER_SIZE = 8 * 1024 * 1024; // Receive buffer size.
static u32 s_ReceiveBuffer[RECEIVE_BUFFER_SIZE/sizeof(u32)]; // The instance of the receive buffer.
 
void startReceive()
{
    // After the receive request arrives, data receiving begins.
    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::STATION_ID_INVALID);
        nn::Result result = pProtocol->StartReceive(s_ReceiveBuffer, sizeof(s_ReceiveBuffer), srcId);
        PIA_ASSERT(result.IsSuccess());
    }
}

Ending Sending and Receiving

Use the GetState() function to determine whether the receiving of data has completed. The GetState()function returns STATE_RECEIVE_SUCCESS when all data has been received.
The return value of the GetState() function for the sender depends on the settings of the Configuration::IsSendEndless() function. For more information, see the API reference.

Finalization

The following code is an example of the finalization for ReliableBroadcastProtocol.

Code 5-21. Finalization
void finalizeTransport()
{
    nn::pia::transport::Transport* pTransport = nn::pia::transport::Transport::GetInstance();
    PIA_ASSERT(PIA_IS_VALID_POINTER(pTransport));
 
    // Finalization of 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;
    }
}

Advanced Operations

The operations described earlier are the basic operations. This section describes the advanced operations.

Preceded Calling of the StartReceive() Function

Based on application specifications, when the sender and receiver are known in advance, it is possible for the receiving end to call the StartReceive() function before the sender calls the StartSend() function. Calling the StartReceive() function ahead of time slightly lessens the loss of data and can also slightly reduce the time needed for sending and receiving.

Rejecting a Receive Request

As noted, you can get the receive request with the GetRequest() function, but you can also reject the request. To do so, use the RejectRequest() function.

Code 5-22. Rejecting a Receive Request
void rejectRequest()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    nn::Result result = pProtocol->RejectRequest();
    PIA_ASSERT(result.IsSuccess());
}

Canceling a Transfer

Call the Cancel() function to cancel a transfer midway.

Code 5-23. Canceling a Transfer
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)
    {
        // Cancels sending and receiving.
        nn::Result result = pProtocol->Cancel();
        PIA_ASSERT(result.IsSuccess());
    }
}

Checking the Progress of a Transfer

You can check the progress of a transfer with the GetProgress() function, but note that the meaning of the returned value is different for the sender and the receiver.
When the receiver calls GetProgress(), all that is returned is the size of the data received. When the sender calls it, however, the average data size sent to all target stations is returned.

Code 5-24. Checking the Progress of a Transfer
void printProgress()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    const nn::pia::transport::ReliableBroadcastProtocol::TransferSetting* cpSetting = pProtocol->GetCurrentTransferSetting();
    if (cpSetting != NULL)
    {
        // Print the progress for the recipient.
        u32 total = cpSetting->m_DataSize;
        u32 current = pProtocol->GetProgress();
        f32 rate = static_cast<f32>(current) / static_cast<f32>(total);
        PIA_BASIC_REPORT("%7dB / %7dB (%5.1f%%)", current, total, rate * 100.0f);
    }
}

Resetting

Call the Reset() function to destroy the transfer result information and return to a State::STATE_WAIT state. The Reset() function cannot be called, however, during a transfer. For more information, see the API reference.

Code 5-25. Resetting
void reset()
{
    nn::pia::transport::ReliableBroadcastProtocol* pProtocol = GetReliableBroadcastProtocol();
    PIA_ASSERT(pProtocol->IsRunning() == false);
 
    nn::Result result = pProtocol->Reset();
    PIA_ASSERT(result.IsSuccess());
}