• Examples (Unity)
• Examples (iOS)
• Examples (Android)
• Examples (C++)
Server API Documentation

 

» Advanced Connector

» Overview

The Advanced Connector example expands the very basic Connector example, adding the logic needed to activate the protocol cryptography that was introduced in SmartFoxServer 2X starting from version 2.10.

Encryption of messages exchanged between client and server requires an initial HTTPS negotiation which allows the client to acquire the private key later used for 128 bit symmetric encryption; this is performed before the login process, also included in the example.
And just like in the simpler Connector example, we will show how to deal with the different requirements of the Unity build targets, making use of more conditional compilation statements.

The Unity scene is nearly the same of the Connector's one. In the Connect & Login panel a couple of Toggles have been added to login after the connection is established and start the network lag monitoring feature of the API.

>> DOWNLOAD the source files <<

» Setup & run

In order to setup and run the example, follow these steps:

  1. make sure your SmartFoxServer 2X installation contains the BasicExamples Zone definition;
  2. start SmartFoxServer 2X (v2.10 or later is mandatory; v2.13 or later is highly recommended);
  3. start Unity (v5.6 or later required);
  4. in the Projects panel click on the Open icon and browse to the /client/AdvancedConnector folder, then click the Open button;
  5. wait for the project setup completion (Unity needs to regenerate some libraries);
  6. go to the Project panel, click on the Assets folder and double click on the MainScene scene to open it;
  7. click on the Play button to run the example in the Unity Editor, or go to the Build settings panel and build it for your target platform of choice.

All relevant assets are contained in the Assets/ConnectorAssets folder; in particular the C# code is in the Scripts subfolder and the SmartFoxServer 2X client API DLLs are in the Plugins subfolder. Read the introduction to understand why multiple DLLs are used.

» Code highlights

Code for this example is contained in the script file called AdvancedConnector in the Scripts subfolder, attached to the ConnectionPanel game object in the scene.
The structure of the file is a basic Unity C# script with a Start() and Update() methods. Additionally there is a listener for the click event of the sole button in the UI, a few helper methods and the listeners for the SmartFoxServer client API events.

» Fields declaration

At the top of the script, in addition to the fields already discussed in the Connector tutorial, there's a few more private properties containing other port values for the connection.

private int defaultWssPort = 8443;
private int httpPort = 8080;
private int httpsPort = 8443;

The defaultWssPort settings is used to pre-populate port input field in case encryption is used in WebGL builds, while httpsPort is required for the encryption initialization step in non-WebGL builds. More on both subjects later on. The httpPort is for BlueBox connection, which is outside the scope of this tutorial (but you can find more information here).

Please note that defaultWsPort = httpPort and defaultWssPort = httpsPort; this is because WebSocket and WebSocketSecure connection, tunnelled BlueBox communication and protocol encryption initialization all rely on the SmartFoxServer's internal web server.

Always in the fields declaration, the useEncryption flag is used to modify the script execution after the connection, to activate the SmartFoxServer protocol encryption or not.

IMPORTANT
By default the useEncryption flag is set to false because protocol encryption requires a good understanding of the approach adopted in the API and a specific setup of SmartFoxServer 2X. For this reason we strongly recommend to read this document carefully before activating it.
Also, encryption can't be tested on localhost (where you are probably running this example) because a valid SSL certificate is needed.

» Unity's Start callback

The Start() method just takes care of the interface initialization. This is where the UI input fields are pre-populated with the default values set in the fileds declaration.

hostInput.text = defaultHost;

#if !UNITY_WEBGL
portInput.text = defaultTcpPort.ToString();
#else
if (!useEncryption)
    portInput.text = defaultWsPort.ToString();
else
    portInput.text = defaultWssPort.ToString();
#endif

The reason why we have three possible port numbers depends on the type of connection we want to establish with SmartFoxServer:

The proper port value is set using a bit of conditional compilation. For more informations please visit this page in the Unity documentation.

» Establishing a connection

The behavior of the initial part of the code executed on CONNECT button click, described in the Connector example, was modified to include a check of the useEncryption flag. In fact the policy prefetch can't be executed when encryption is activated. The reason is explained in the Platform specific notes paragraph of this document.

#if UNITY_WEBPLAYER
if (!useEncryption) {
    if (!Security.PrefetchSocketPolicy(hostInput.text, Convert.ToInt32(portInput.text), 500)) {
        Debug.LogError("Security Exception. Policy file loading failed!");
    }
}
#endif

The next relevant code section creates the SmartFox class instance and registers the event handlers required by the example.

Again, the example uses some conditional compilation to create the main API object. In fact, as mentioned before, Unity's WebGL build requires a websocket connection to be established between the client and the server., instead of the default socket connection used by SmartFoxServer. A special constructor on the SmartFox object instructs the API to use this type of connection, and the useEncryption flag determines if the secure version should be used (WSS).

#if !UNITY_WEBGL
sfs = new SmartFox();
#else
sfs = new SmartFox(useEncryption ? UseWebSocket.WSS_BIN : UseWebSocket.WS_BIN);
#endif

Before actually starting the connection process, a few more event listeners are added with respect to the original Connector example. We'll analyze them while progressing in this tutorial.

sfs.AddEventListener(SFSEvent.CONNECTION, OnConnection);
sfs.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
sfs.AddEventListener(SFSEvent.CRYPTO_INIT, OnCryptoInit);
sfs.AddEventListener(SFSEvent.LOGIN, OnLogin);
sfs.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
sfs.AddEventListener(SFSEvent.PING_PONG, OnPingPong);

In the last part of the connection code, an additional parameter is set in the the ConfigData object, before passing it to the SmartFox.Connect() method: the HttpsPort setting is needed in case the protocol encryption is enabled (note that it must match the corresponding server setting). This is required for the cryptography initialization in the OnConnection() event handler, as described below.

» Handling SmartFoxServer events

In addition to the basic connection and disconnection events, in this example we also register listeners to events related to encryption initialization, user login and network lag measurement.

Which parameters are provided by what events is documented in the SFSEvent API documentation.

CONNECTION event handler

The OnConnection() method is called when the API completes the connection process (whether successfully or not). The event parameter is a "success" bool.

private void OnConnection(BaseEvent evt) {
    if ((bool)evt.Params["success"]) {
        trace("Connection established successfully");
        trace("SFS2X API version: " + sfs.Version);
        trace("Connection mode is: " + sfs.ConnectionMode);
        
        // Enable disconnect button
        button.interactable = true;
        buttonLabel.text = "DISCONNECT";

        #if !UNITY_WEBGL
        // Enable protocol encryption on non-WebGL builds only (WebGL build uses WSS protocol already)
        if (useEncryption) {
            // Initialize encryption
            // All builds except Windows Store require a coroutine
            #if UNITY_EDITOR || !UNITY_WINRT_8_1
            StartCoroutine(sfs.InitCrypto());
            #else
            sfs.InitCrypto();
            #endif
        } else {
            // Attempt login
            login();
        }
        #else
        // Attempt login
        login();
        #endif
    } else {
        trace("Connection failed; is the server running at all?");
        
        // Remove SFS2X listeners and re-enable interface
        reset();
    }
}

If a connection is established successfully, after some details are printed to the example's Debug panel, it is time to attempt the login. If no encryption is required (useEncryption is false), the login() method is called immediately, which in turn sends a specific request to the server (but only if the related Toggle in the UI is checked):

if (loginToggle.isOn) {
    // Login as guest
    sfs.Send(new Sfs2X.Requests.LoginRequest(""));
}

As no username and Zone are passed to the LoginRequest constructor, a "guest" login is attempted to the Zone set in the ConfigData object before.

If the communication must be encrypted (useEncryption is true), then a couple of nested conditional compilation instructions are needed, to cover the different requirements of the Unity builds:

CRYPTO INIT event handler

The OnCryptoInit() method is called when the API completes the encryption initialization process (whether successfully or not). The event parameter is a "success" bool.

private void OnCryptoInit(BaseEvent evt) {
    if ((bool) evt.Params["success"])
    {
        trace("Encryption initialized successfully");
        
        // Attempt login
        login();
    } else {
        trace("Encryption initialization failed: " + (string)evt.Params["errorMessage"]);
    }
}

If the client could connect to the server via HTTPS and retreive the encryption key, then "success" is true: we can now proceed with the login as described before.

LOGIN and LOGIN ERROR event handlers

The OnLogin() and OnLoginError() methods are called respectively if the user was able to login successfully or not.

private void OnLogin(BaseEvent evt) {
    User user = (Sfs2X.Entities.User)evt.Params["user"];
    
    trace("Login successful");
    trace("Username is: " + user.Name);
    
    // Enable lag monitor
    if (lagMonitorToggle.isOn)
        sfs.EnableLagMonitor(true);
}

private void OnLoginError(BaseEvent evt) {
    trace("Login failed: " + (string) evt.Params["errorMessage"]);
}

private void OnPingPong(BaseEvent evt) {
    trace("Measured lag is: " + (int) evt.Params["lagValue"] + "ms");
}

If the login is successful, the name assigned to the user by the server (remember: we logged in as guests!) is printed in the Debug panel. Also, if the related Toggle in the UI is checked, the API's internal network lag monitor is started using the SmartFox.EnableLagMonitor() method. This causes the PING PONG event to be fired at a given rate (depending on the parameters passed to the method) and the OnPingPong() handler being called accordingly.

The event's "lagValue" parameter returns the network lag value in milliseconds. Read the method documentation for more informations.

NOTE
You should also read the comments to methods and properties in the example source code for additional informations.

» More resources

You can learn more about the SmartFoxServer basics by consulting the following resources: