Yesterday as homework your job was to create a simple server that can sign in and sign out.  In one view, we don’t really have a client/server in this case because both our ‘client’ and our ‘server’ are clients of the Microsoft Office Communications server.

 

My solution to the assignment isn’t that difficult and looks very similar to what we had before we added the INVITE logic to our client.  The only real differences are the use of the word “Server” instead of “Client” and removing the setting for “Our server” since obviously the server knows who it is (through the Uri setting).

 

Each day I have posted the code for the client from the previous day.  Starting tomorrow, I will post the code for both the client and the server.  However, for today if you don’t already have the server code you’ll need to create a new project with the adjustments listed above.

 

Today we are going to focus most of our efforts on getting our server to accept the INVITE coming from our client.  The server will sign in as one SIP address while the client will sign in as a different one.  In the “Our Server” setting of the client you will need to specify the SIP URL of the server.

 

In order to accept an INVITE from our client, we must first be listening for them.  The question then becomes, which object will accept an INVITE?  So what have we used so far?

 

·         SipEndpoint – This may be involved somehow, but is its job to listen for connections?

·         SignalingSession – This is what we want, but obviously it cannot listen for itself.

·         RealTimeConnectionManager – Hmmmm…

 

RealTimeConnectionManager happens to have a member called StartListening.  That certainly looks promising!  Actually, this is not present on RealTimeConnectionManager – which can only send messages.  In order to use StartListening you must have a RealTimeServerConnectionManager.  That is why we have been using this class the entire time.  StartListening accepts an IP endpoint we will listen on and for our purposes we will use the default loopback address and all ports.  In a shipping application you would probably want to limit these parameters.

 

So now you are listening for invites, how can you find out when one occurs?  This is where SipEndpoint comes to the rescue.  When an invite occurs, its SessionReceived event will fire.  Once this event fires, our server just needs to call BeginParticipate to accept the session from its end.  Of course, a real server would probably want to examine the session and accept it only on certain conditions, but our server will just accept all requests.

 

Add the following code in the server manager at the end of RegisterCallback.

 

RecordProgress("Listening for incoming connections.");

try

{

    _connectionManager.StartListening(new IPEndPoint(IPAddress.Loopback, 0));

}

catch (Exception ex)

{

    Error("Error when StartListening was called", ex.ToString());

    return;

}

_endPoint.SessionReceived += new EventHandler<SessionReceivedEventArgs>(SessionReceived);

 

When the SessionReceived event fires, we can retrieve the session using the SessionReceivedEventArgs passed.  The following is the code for SessionReceived.

 

void SessionReceived(object sender, SessionReceivedEventArgs e)

{

    RecordProgress("An invite was received.");

 

    // Accept the invite

    e.Session.BeginParticipate(new AsyncCallback(ParticipateCallback), e.Session);

}

 

This code is rather straightforward.  Let’s now take a look at the code for ParticipateCallback.

 

/// <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

    {

        //This response tells us if the session creation succeeded.

        response = session.EndParticipate(ar);

        RecordProgress("The invite was accepted successfully.");

    }

    catch (OperationTimeoutException)

    {

        Error("The invite could not be accepted because the session establishment timed out.");

    }

    catch (FailureResponseException e)

    {

        if (e.ResponseData != null)

        {

            Error("The invite could not be accepted; Error Code={0} Error Text={1}.",

                  e.ResponseData.ResponseCode,

                  e.ResponseData.ResponseText);

        }

        else

        {

            Error("The invite could not be accepted");

        }

    }

    catch (ConnectionFailureException)

    {

        Error("The invite could not be accepted; the connection could not be established.");

    }

    catch (RealTimeException e)

    {

        Error("The invite could not be accepted; the following problem occurred {0}.",

              e.Message);

    }

}

 

All of these exceptions were covered yesterday when we discussed how to create an INVITE.  If you now build the server and fill in the options, start the server and then start the client, you’ll see the from messages that the invite is accepted successfully.  We’re slowly getting to our goal of sending a message!