So far we have a simple client that doesn’t do very much.  In the next few days, we are going to add the capability for our client to send a message to Microsoft Office Communicator.  We are going to do this by creating an INVITE to the Office Communicator client and then send a MESSAGE to it. 

 

If you have no idea what an INVITE is, I suggest you spend some time perusing the many SIP resources on the Internet.  For example, see the SIP RFC at http://tools.ietf.org/html/rfc3261. This series is meant to help others learn the UCMA API, which assumes a decent knowledge of SIP.

 

In a simple form, to create an INVITE to another client one needs only to create a SignalingSession instance and then call BeginParticipate on it, but there are a number of steps that we need to accomplish while doing this.  The first thing we need to do is add a private field to our manager class to hold our SignalingSession instance.

 

private SignalingSession _session;

 

SignalingSession is basically an INVITE dialog for our purposes.  The constructor for SignalingSession accepts an endpoint (which we already created) and a RealTimeAddress.  RealTimeAddress takes a string constructor with the SIP address of the other party in the dialog.  For our purposes now, we will use our ‘server’ as the other client.  On our options dialog, you enter this under ‘our server’ and the format must be of sip:user@domain.com.  Note that there are other constructors for SignalingSession that we will not cover here.

 

So let’s get started by creating our SignalingSession instance and calling BeginParticipate on it.

 

/// <summary>

/// Sets up the dialog with our server

/// </summary>

private void SetupDialog()

{

    RecordProgress("Setup the dialog");

 

    // Create the signaling session

    _session = new SignalingSession(_endPoint, new RealTimeAddress(Settings.OurServerName));

 

    try

    {

        RecordProgress("Participating in the session");

        _session.BeginParticipate(new AsyncCallback(ParticipateCallback), _session);

    }

    catch (RealTimeException e)

    {

        Error("The Notification could not be received by {0} Exception Message {1}.",

                        Settings.OurServerName,

                        e.Message);

    }

}

 

We catch several exceptions here and report them back to the UI.  The RealTimeException class is a generic catch-all for any RTC exception that can be thrown when calling BeginParticipate.  Most exceptions will occur when we call EndParticipate.  The following is the code for our callback method.

 

/// <summary>

/// PartipateCallback is called back once the participate operation is completed.

/// </summary>

/// <param name="ar"></param>

private void ParticipateCallback(IAsyncResult ar)

{

    SignalingSession session = ar.AsyncState as SignalingSession;

    SipMessageData response = null;

 

    try

    {

        response = session.EndParticipate(ar);

        RecordProgress("Session setup successful");

    }

    catch (OperationTimeoutException)

    {

        Error("The Notification could not be received by {0} because the session establishment timed out.",

              Settings.OurServerName);

    }

    catch (FailureResponseException e)

    {

        if (e.ResponseData != null)

        {

            Error("The Notification could not be received by {0}; Error Code={1} Error Text={2}.",

                  Settings.OurServerName,

                  e.ResponseData.ResponseCode,

                  e.ResponseData.ResponseText);

        }

        else

        {

            Error("The Notification could not be received by {0}.", Settings.OurServerName);

        }

    }

    catch (ConnectionFailureException)

    {

        Error("The Notification could not be received by {0}; the connection could not be established.",

              Settings.OurServerName);

    }

    catch (RealTimeException e)

    {

        Error("The Notification could not be received by {0}; the following problem occurred {1}.",

              Settings.OurServerName,

              e.Message);

    }

}

 

So what do all of these exceptions mean? 

·         As we mentioned before, RealTimeException is a catch all for any other type of RTC exception.

·         A ConnectionFailureException will occur if the URI and/or port you specified do not exist. 

·         A FailureResponseException could mean several things. 

·         The ServerPolicyException (not caught explicitly here) derives from it and indicates this message is not acceptable per the server policy.  There are several other descendents of FailureResponseException (such as PublishSubscribeException which is not relevant here) but in general they all indicate that the INVITE failed for a particular reason explained in the exception. 

·         OperationTimeoutException can occur if it took too long for the other party to accept the invite.

 

We now need to call our SetupDialog method after the endpoint has registered itself with the server.  Add the following line in RegisterCallback after we have triggered the Started event.

 

SetupDialog();

 

We are almost there, but first we need to change our sign out logic.  Right now, we terminate the endpoint.  However, we now have a session which we need to terminate first.  Change the Terminate method to do the following.

 

public void Terminate()

{

    if (null != _session)

    {

        _session.BeginTerminate(new AsyncCallback(SessionTerminateCallback), _session);

    }

    else

    {

        // Terminate the endpoint

        _endPoint.BeginTerminate(new AsyncCallback(TerminateCallback), _endPoint);

    }

}

 

In this code we call BeginTerminate on the session if we have one; otherwise we call BeginTerminate on the endpoint.  Before any of you start entering heated comments, note that the code above would most likely not be sufficient for a shipping application.  Because this is a multithreaded application, there are a number of possible issues here – such as we created the session but have not called BeginParticipate yet.  As the main point of this series is to discuss the UCMA API, I will leave it up to you to take care of the threading issues here, although I will try to point out possible issues as they arise.

 

The following is the code for the callback method.

 

/// <summary>

/// Terminates the session

/// </summary>

/// <param name="asyncResult"></param>

private void SessionTerminateCallback(IAsyncResult asyncResult)

{

    SignalingSession session = asyncResult as SignalingSession;

 

    try

    {

        _session.EndTerminate(asyncResult);

 

        // Terminate the endpoint

        _endPoint.BeginTerminate(new AsyncCallback(TerminateCallback), _endPoint);

        RecordProgress("Session terminated.");

    }

    catch (Exception e)

    {

        Error("An exception occurred when terminating the session.\n{0}", e.ToString());

    }

}

 

Once we have terminated the session, we terminate the endpoint.  We already have the callback method for the endpoint termination so we are done.

 

Try compiling this example and see if it works.  What!  OC rejected my invite?! Well you didn’t think it would be that easy, did you? J To really get an invite to work, it is time to create our server.  It is now time for your first homework assignment.  Using the code for the client we have just built, create a server that can sign in and sign out.  Tomorrow we will discuss how to accept this invite.