6.10. Advanced Features - Relay Communication Feature

This section describes relay communication.

Description

When a station cannot establish a direct connection with a particular other station in the mesh (for example, because of a NAT traversal failure during Internet communication), it can still communicate with that station by using the relay communication feature of PiaSession to send and receive data via another station with which it has established a connection. You can enable this feature by specifying a network topology that uses relay communication when instantiating the Session class. For more information about the characteristics of each network topology, see  6.1. PiaSession Overview .

For a description of the relay communication features, see  Figure 6-8. Relay Communication Connections.

Figure 6-8. Relay Communication Connections

The routes taken during relay communication are Route A, Route B, and Route D. Route C, which is an example of a route that involves more than two stations, cannot be selected.

Selecting Relay Routes

A relay connection describes the connection topology that allows stations that are not directly connected to send and receive data via a third station. Selecting the station to use for relaying is called route selection. The session host makes this route selection when stations join and leave a session and sends that route information to the clients. Route selection and the creation and parsing of route information is carried out in the common::Scheduler::Dispatch() function.

In the example presented in  Figure 6-8. Relay Communication Connections, there is no direct communication route between Station 2 and Station 4. In this situation, there are a number of possible communication routes for Station 2 to Station 4, including through Station 1 (Route A), through Station 3 (Route B), and through Stations 1 and 3 (Route C). In situations like this where a number of routes are possible for relay communication, consider the following factors when deciding which communication route to use.

  • Consider setting a number of relay requests to assign to each station so the requests do not swamp any particular station. Use SetMaxNumOfRelayRoutesAssignedToStation() to set the maximum number of relay requests to assign to any one station.
  • Try to minimize any communication delays. Use SetMaxRtt() to set an upper limit on the total delay time for the relay communication. The selected relay routes are not updated while no stations join or leave the session, even if communication delays exceed this value due to factors such as a change in the communication environment.
  • Relay routes that go through two or more stations cannot be selected.

Although these routes generally do not change after they have been selected in the relay connection process, they might be changed when stations join or leave the mesh to optimize overall communication routes in the mesh.

Priority of Maintaining Connections

If the station the relay request is being sent to gets disconnected, a search for an alternate route is performed automatically. The search is carried out quickly (it ideally takes about the time of a round trip), but during that time the route-selection conditions described above are ignored and priority is given to maintaining connections. The session host searches for a new station to accept the relay requests that satisfies the route-selection conditions, and then sends the new route information to the client. The client begins using that new station for its relay communications as soon as it receives the route information from the host. However, in some situations the host cannot find a new relay-request station that satisfies the route-selection conditions. When that happens, some alternate route is used even though it does not satisfy all the conditions because priority is given to maintaining the connection.

Relay Transmissions

Relay transmissions, or forwarding, refers to the process whereby two stations that do not have a direct communication route send and receive data via a third station.

When an application calls the Pia sending and receiving functions (such as transport::UnreliableProtocol::Send and transport::UnreliableProtocol::Receive), it only needs to specify the final destination without worrying about whether it has a direct communication route.

The station receiving the forward request forwards any data that is not addressed to itself to the final destination. This process is performed in the same common::Scheduler::Dispatch() function.

In the example presented in  Figure 6-8. Relay Communication Connections, there is no direct communication route between Station 2 and Station 4. In this situation, the application only needs to specify Station 4, the final destination, when sending data to Station 4. Pia relays the data using the communication route (such as Route A) that was selected by the relay connection process. On Station 4, the receiving station, the application can determine that the sender was Station 2, and not Station 1.

Also note that there is no direct communication route between Station 2 and Station 5 in Figure 6-8. Relay Communication Connections . Assume Route A is the communication route between Station 2 and Station 4, and Route D is the route between Station 2 and Station 5. If the application on Station 2 calls transport::UnreliableProtocol::SendToAll and sends the same data to Station 4 and Station 5, Pia sends a single set of data to Station 1 with the addresses set for both Station 4 and Station 5. Station 1 forwards that data to Station 4 and Station 5. In this way, Pia's relay communication feature automatically integrates duplicated data to significantly reduce the consumed bandwidth.

Tracing for Debugging

The relay route table presents the connection states for all combinations of routes among stations. Set the TRACE_FLAG_TRANSPORT_RELAY trace flag to output this relay route table for tracing. When the relay route table is updated, the trace is output in the format shown in Figure 6-9. Trace of the Relay Route Table .

Figure 6-9. Trace of the Relay Route Table

In this table, the ith row from the top (0 <= i <= 7) and the jth column from the left (0 <= j <= 7) take the value k[i][j]. k[i][j] indicates the connection state between station i (StationIndex == i) and station j (StationIndex == j).

When i != j, you can distinguish between the following connection states between the two stations.

- When k[i][j] is "-" : Stations i and j are disconnected.

- When k[i][j] == j : Stations i and j are directly connected.

- When k[i][j] != i and k[i][j] != j : Stations i and j are connected via station k[i][j].

The route being used for relay communication is marked with an asterisk (*) to the right of the number.

In this example, Stations 0 through 3 are participating in a mesh, and Station 2 and Station 3 are connected for relay communication via Station 1 (k[2][3] == 1, k[3][2] == 1). All other routes are direct connections.

Reasons Why Joining May Fail When Relay Communication Is Enabled

When you enable relay communication, a station can successfully join a mesh even if its connection with another station fails. This helps to significantly reduce the join-in failure rate, particularly when many stations are connected in a mesh. However, in the following kinds of cases, joining the mesh fails.

When Connection to the Host Fails

It is the job of the host to select relay routes. If a station fails to connect to the first host in the process of joining a mesh, it fails to join any mesh. When this happens, the Result value returned by the asynchronous mesh joining process (obtainable from session::Session::GetJoinSessionResult when using Session, or session::Mesh::GetJoinMeshResult when using direct mesh control) is the same value that would be returned on failure to join when relay communication was disabled.

However, when NEX auto-matchmaking is used, a station that readily fails to make P2P connections and would delay the creation of the matchmaking session typically will not become the host. (For more information, see the Delaying the Creation of a New Matchmaking Session During Auto-Matchmaking section in the NEX programming manual.) As a result, the local station normally succeeds in joining the mesh (providing it, too, does not readily fail to make P2P connections, such as when it is on an EDM circuit). Even if the local station is on an EDM circuit, it has a high probability of successfully connecting to the first station, which is the host.

When No Relay Route Can Be Found That Does Not Exceed the Maximum Number of Relay Requests

When a relatively large proportion of the stations in a mesh are already using relay communications, the join-in process fails if a route cannot be found that does not exceed the value set for the maximum number of relay requests assigned to each station. When this happens, the Result value returned by the asynchronous mesh joining process (obtainable from session::Session::GetJoinSessionResult when using Session, or session::Mesh::GetJoinMeshResult when using direct mesh control) is session::ResultRelayFailedRelayNumLimit.

When No Relay Route Can Be Found That Does Not Exceed the Maximum Delay Time

When there are large delays in communication among stations in a mesh, the join-in process fails if a route cannot be found that does not exceed the upper limit set for the total delay time for the relay communication. When this happens, the Result value returned by the asynchronous mesh joining process (obtainable from session::Session::GetJoinSessionResult when using Session, or session::Mesh::GetJoinMeshResult when using direct mesh control) is session::ResultRelayFailedRttLimit.

When No Candidate Route Can Be Found Because No Station Can Act as the Relay

Under special circumstances where communication within a mesh is possible but no station is available to serve as a relay route between a station that is joining the mesh and the other stations, the join-in process fails. When this happens, the Result value returned by the asynchronous mesh joining process (obtainable from session::Session::GetJoinSessionResult when using Session, or session::Mesh::GetJoinMeshResult when using direct mesh control) is session::ResultRelayFailedNoCandidate.

If the failure to join the mesh is related to the relay connection process, it could be because several of the restrictions described above apply at the same time. However, even when this is the case, the value returned by Result indicates only one restriction as the cause.

Relay Emulation Feature

The relay emulation feature is for debugging relay communication.

You can use the relay emulation feature to specify relay connections between any stations.

Preparing to Use the Relay Emulation Feature

Before generating an instance of the Session class or Mesh class, you must call the function that configures the debug feature (session::Session::SetDebugSetting when using Session and session::Mesh::SetDebugSetting when directly controlling the mesh) to enable the relay emulation feature.

Code 6-21. Enabling Relay Emulation
// Configuration of the DebugSetting structure is complete.
nn::pia::session::Session::DebugSetting debugSetting;
// Enable the relay emulation settings flag.
debugSetting.isEnableRelayEmulation = true;
// Configuration of the DebugSetting structure is complete.

result = nn::pia::session::Session::SetDebugSetting(debugSetting);
if ( result.IsFailure() )
{
    // Error handling.
    // Does not fail as long as it is called at the appropriate time.
}
// After this process succeeds, call Session::CreateInstance to create an instance of the Session class.
// Relay emulation will become enabled.

Using the Relay Emulation Feature

Specify two StationIndex values to create a relay connection between those two stations.

You can only specify this setting when the stations are not connected to a mesh. Make sure that you set the same values on all stations participating in the mesh.

Code 6-22. Configuring Relay Emulation
// Assume that the stations are not yet connected.
// These arguments set relay connections between STATION_INDEX_2 and STATION_INDEX_3, and between STATION_INDEX_2 and STATION_INDEX_4.
nn::pia::StationIndex targetIndexA = nn::pia::STATION_INDEX_2;
nn::pia::StationIndex targetIndexB = nn::pia::STATION_INDEX_3;
nn::pia::StationIndex targetIndexC = nn::pia::STATION_INDEX_4;
const nn::pia::session::Mesh::EmulationType cEmulationType = nn::pia::session::Mesh::EMULATION_TYPE_RELAY;
 
result = nn::pia::session::Session::GetInstance()->RelayEmulation(targetIndexA, targetIndexB, cEmulationType);
if ( result.IsFailure() )
{
    // Error handling.
    // Does not fail as long as it is called at the appropriate time with the appropriate arguments.
}
 
// It is also possible to configure relay emulation between other stations at the same time.
result = nn::pia::session::Session::GetInstance()->RelayEmulation(targetIndexA, targetIndexC, cEmulationType);
if ( result.IsFailure() )
{
    // Error handling.
    // Does not fail as long as it is called at the appropriate time with the appropriate arguments.
}
 
// When the connection process is run after all stations are configured as above, relay connections are established between STATION_INDEX_2 and STATION_INDEX_3 and between STATION_INDEX_2 and STATION_INDEX_4.