Introduction to the UCMA API - Part 9 - Detecting when the session has been closed
You have probably already noticed that UCMA is currently a low level API. The advantage, of course, is you have a great deal of flexibility in achieving your goals. The disadvantage is it takes a bit longer to get to where you are going. Rest assured you will soon be able to send and receive messages, but that will not be today.
Today we are going to accomplish what I had originally planned for yesterday – the ability for the client and server to detect when the other has closed the session. We will implement this in both our client and in our server, though the approach will be slightly different with each. For the server, if a client disconnects we need only to discard that client and wait for another client. The client, on the other hand, will not be able to function without the server. In the future we may add the capability for the client to keep retrying the server but that will be a later post.
The server code is actually somewhat easy. The SignalingSession class contains an event named StateChanged. The SignalingStateChangedEventArgs passed to the event handler contains a SignalingState enumeration that can indicate whether the session was disconnected. To handle the StateChanged event, add the following line at the end of the constructor for IDKStudioSession.
_session.StateChanged += new EventHandler<SignalingStateChangedEventArgs>(_session_StateChanged);
We will also need to add some synchronization fields.
private bool _shuttingDown = false;
private object _syncObject = new object();
The _shuttingDown field will allow us to determine if we are shutting down because the user clicked Stop or because the client closed the connection. In Terminate we need to set the value of _shuttingDown to true.
lock (_syncObject)
{
_shuttingDown = true;
}
The event handler checks whether the session has been disconnected and if so signals the manager to remove the connection from its dictionary. It only does this if we are not already shutting down – otherwise we risk calling SessionCompleted twice.
void _session_StateChanged(object sender, SignalingStateChangedEventArgs e)
{
if (e.State == SignalingState.Disconnected)
{
lock (_syncObject)
{
if (false == _shuttingDown)
{
if (null != SessionCompleted)
{
SessionCompleted(this, new CompletedEventArgs(CompletionStatus.Success,
"Session disconnected by the client"));
}
}
}
}
}
This is sufficient for the server (most of the work for this was done yesterday). On the client, the approach is similar. Add the following code in the SetupDialog method of the client after we create the SignalingSession instance.
_session.StateChanged += new EventHandler<SignalingStateChangedEventArgs>(_session_StateChanged);
We also need to add the same synchronization logic that we have in the server. Add the following fields to the client manager.
private bool _shuttingDown = false;
private object _syncObject = new object();
Add the following code at the very beginning of the Terminate method in the client.
lock (_syncObject)
{
_shuttingDown = true;
}
Finally, if the state is disconnected and we are not shutting down due to the user clicking the Stop button, we should unregister our endpoint.
void _session_StateChanged(object sender, SignalingStateChangedEventArgs e)
{
RecordProgress("New state={0}", e.State);
if (e.State == SignalingState.Disconnected)
{
lock (_syncObject)
{
if (false == _shuttingDown)
{
RecordProgress("Server disconnected the session");
_endPoint.BeginUnregister(new AsyncCallback(UnregisterCallback), _endPoint);
}
}
}
}
You can now play around with starting and stopping the client and server, and you will notice that each reacts appropriately to the other shutting down. We are almost at the point where we can send a message – there is just one more step in the way and we will cover that tomorrow.