Connection Resiliency with AppWarp
On mobile devices, data connectivity has always been an issue. While users are on the go, the data source will often switch towers and or downgrade/upgrade between 2G and 3G. This has an impact on applications that rely on persistent data connectivity.
AppWarp’s binary protocol runs on top of TCP. This provides developers with fast, efficient and reliable realtime connectivity. However on mobile devices, it will often be the case that the device IP address will change when its on the go. This results in the underlying connection to break and the application code must recover from this by reconnecting, rejoining and resubscribing to the room the user was in before the error occurred. Worse, other users in the room will receive an event indicating that the user has left the room. This could even impact the game logic as other client don’t know whether the concerned user experienced intermittent connectivity issues or willfully disconnected (left the room).
We have introduced our Connection Resiliency feature to offer developers an elegant solution to this scenario. When any connectivity error occurs, AppWarp SDK will raise an error indicating whether the error is recoverable or non-recoverable. If its a recoverable error, our new RecoverConnection API can be used and if connectivity has been restored, will automatically restore the client to its last state in terms of the room it was in and all its subscriptions. So no re-joining or re-subscribing and state keeping is required in the application. Additionally when such intermittent errors occur, AppWarp server will raise a specific event to other users in the room indicating that the user has been paused. After a specified timeout, if the concerned user is unable to restore its connection, a user left room event will be raised for other users in the room. On the other hand if the connection is restored successfully, a user resumed event is raised.
Some of the relevant APIs are described below
public static void setRecoveryAllowance ( int maxRecoveryTime )
Sets the connection recovery time (seconds) allowed that will be negotiated with the server. By default it is 0 so there is no connection recovery and the SDK behavior will be as before. It is important you invoke this right after you initialize the WarpClient and before you make your first call to connect. Recommended values are between 60 to 120 i.e. 1 minute to 2 minutes. See example below
WarpClient.initialize ( Constants.apiKey , Constants.s2Address);
WarpClient.setRecoveryAllowance ( 120 );
public void connectWithUserName ( String userName )
This attempts to establish a new connection being initiated with the given username.
public void RecoverConnection ()
This attempts to restore the previous connection if it was broken and a recoverable connection error event was raised. If connectivity is not there at the time of calling, it will again raise a recoverable connection error event ( guaranteed within 6 seconds ). and the application can choose to recover again immediately or after a while. See the example towards the end below on how to use this.
public void disconnect ()
This will disconnect the current session with the server. Once disconnected, the session can’t be recovered and a new connection will need to be established.
public interface ConnectionRequestListener {
/**
* Invoked in response to a ConnectWithUsername or RecoverConnection request.
* This is also raised if an established connection breaks.
* @param event
*/
public void onConnectDone ( ConnectEvent event );
/**
* Invoked in response to a disconnect request.
* @param event
*/
public void onDisconnectDone ( ConnectEvent event );
}
The result code in the ConnectEvent is important as it indicates whether the connection was successfully established/restored or a recoverable/non-recoverable error has occurred. Below are the important codes that need to be handled
public static final byte SUCCESS = 0 ; // successfully established a new connection
public static final byte AUTH_ERROR = 1 ; // indicates incorrect api and secret key pair
public static final byte CONNECTION_ERROR = 5 ; // non-recoverable connection error
public static final byte SUCCESS_RECOVERED = 8 ; // successfully recovered the previous connection
public static final byte CONNECTION_ERROR_RECOVERABLE = 9 ; // recoverable connection error
An example of how to handle this event is given below.
@Override
public void onConnectDone (final ConnectEvent event ) {
handler.post (new Runnable () {
@Override
public void run () {
progressDialog.dismiss ();
if (event.getResult () == WarpResponseResultCode.SUCCESS ){
Toast.makeText (MainActivity.this , "Connection success" , Toast .LENGTH_SHORT ).show ();
}
else if (event.getResult () == WarpResponseResultCode .SUCCESS_RECOVERED ){
Toast.makeText (MainActivity.this , "Connection recovered" , Toast.LENGTH_SHORT ).show ();
}
else if (event.getResult () == WarpResponseResultCode.CONNECTION_ERROR_RECOVERABLE ){
Toast.makeText (MainActivity.this , "Recoverable connection error. Recovering session in 5 seconds" , Toast .LENGTH_SHORT ).show ();
handler.postDelayed (new Runnable () {
@Override
public void run () {
progressDialog = ProgressDialog.show (MainActivity.this , "" , "Recovering..." );
theClient.RecoverConnection ();
}
}, 5000 );
}
else {
Toast.makeText (MainActivity.this , "non-recoverable connection error. Reconnecting in 5 seconds" , Toast.LENGTH_SHORT ).show ();
handler.postDelayed (new Runnable () {
@Override
public void run () {
progressDialog = ProgressDialog.show (MainActivity.this , "" , "Reconnecting..." );
theClient.connectWithUserName (Utils.USER_NAME );
}
}, 5000 );
}
}
});
}
Note that if using UDP, after recovering, you need to call initUDP() again to re-establish the connectionless path with the server.
Connection Resiliency APIs are a way of handling connection errors in your game. It should not be thought of as a way of avoiding such errors as these reflect the health of the underlying transport.