How The Server And A Device Stay In Touch
The flow of communications between device and server are sufficient to keep the each aware of the other’s presence. However, a pause in communications isn’t always an indication that the device (or server) has gone offline. So how do device and server each work out whether the other is no longer available for some reason?
First, let’s look at how the two connect initially. When it performs a cold start, the device brings up its WiFi radio, connects to the local network and gains its IP address. The device can now send a connection request to the server, which it assumes is online and listening for such requests. If it is listening, the server will acknowledge the request. On receipt of the acknowledgement, the device signals the server back and the two now have an open TCP connection.
This interaction is known as a ‘three-way handshake’. Once it has completed successfully, impOS™ and server establish a TLS connection, exchanging certificates to create a secure point-to-point connection between them.
If your device code calls server.isconnected(), or the agent calls device.isconnected(), either of these impOS API methods will return true
.
The flow of data will typically be sufficient to keep the connection in place, but what happens if the data flow stops?
From the server’s point of view, if packets from the device stop being received for a period of approximately 60 seconds, it will attempt to elicit a response from the device with a ‘keepalive’ packet. If the device does not respond, further attempts to contact the device will be made. If no acknowledgement is forthcoming within around 100 seconds, the server will judge the device to be offline and close the connection.
From this point, calls to the impOS API method device.isconnected() will return false
. If the agent code has registered a callback function using the API method device.ondisconnect(), that function will now be called.
The important point to understand is that it is this recorded change of state that causes the callback to be triggered, not the cessation of data being received from the device. If the device fails to respond to the first keepalive, but responds to a subsequent one, its recorded connection state will never change from connected to disconnected, device.isconnected() will return true
throughout, and any callbacks registered with device.ondisconnect() and device.onconnect() will not be called. The callbacks will only be triggered by a formal change of connection state.
The checks for link state described above assume, of course, that the disconnection is unanticipated. If the server knows the device is going offline — because the device has called server.sleepfor() or server.sleepuntil() — it will close the connection and amend its connection record immediately, triggering the device.ondisconnect() callback if one has been registered. At the appointed time, the device will wake and its code will presumably call server.connect() to re-establish the connection between the two. If that happens successfully, the server updates its records, device.onconnect() will fire and device.isconnected() will return true
.
The device also has a period after which, if it has not heard from a server it currently believes to be available, it will send keepalive packets. On the WiFi State Diagram this is marked by the transition between the active-online and active-connecting states. Again, if the server comes back within this period, the connection state as recorded by the device will not change, and server.isconnected() will continue to return true
. Only after the final keepalive times out will the state record change and server.isconnected() return false
. The device enters the active-offline state.
Any callback registered with server.onunexpecteddisconnect() will therefore not be called unless the device formally registers that final change of connection state. Of course, if the state change occurs because of a deliberate disconnection triggered by server.sleepfor(), server.sleepuntil(), imp.deepsleepfor(), imp.deepsleepuntil() or server.disconnect(), then the callback will not be triggered.
server.onunexpecteddisconnect() will also only be called if the RETURN_ON_ERROR disconnection policy is in force. If the device is set to SUSPEND_ON_ERROR, the failure of the server to respond to the device’s keepalive packets will see the device immediately powering down into a deep sleep state. The device will remain asleep for nine minutes, after which it will wake. After waking, the device will not attempt to connect until the device code makes a call that requires network connectivity, at which point it initiates a connection as described at the start of this document.
If the RETURN_ON_ERROR policy is in force, the device will not attempt to open a connection to the server after waking, after an unexpected disconnection or after a deliberate disconnection unless explicitly instructed to do so by Squirrel with a call to server.connect(). This is important to remember, because if the connection was previously lost unexpectedly, the device will not automatically reconnect: you need to provide code which will call server.connect() when you need a connection, and handle both success and failures to connect.
Unlike a warm start, a power cycle — ie. a cold start — will always cause WiFi to be brought up and connected if it can. This is because the device will always check that it has the latest version of its Squirrel application code to run.