SuperHTTP Chat sample (.NET)

Overview

This sample demonstrates how the super channel can be used to create a chat application. The SuperHttp channel is used to create a flexible communication channel that uses persistent connections to enable true asynchronous calls and server callbacks.

Getting started

  • Compile the entire solution.
  • Run the server application.
  • Run several client applications (at least three). The client application is written for Silverlight and needs a proper web browser to run.
  • Test the chat by sending both broadcast and private messages. Set broadcast by selecting [All Users].

Examine the code

  • Open the ChatServer.rodl to see its two services and the EventSync element.
  • Note that EventSync contains 3 events which our client will support (Message, UserLogin and UserLogout).
  • See the implementation of these events in the client's Main.cs class. Note that the frmMain class supports the IChatEvents interface.
  • Note that all events invoke a single delegate SyncEvent:
Invoke(new InvokeDelegate(SyncEvent), new object[] {InvokeType.Message, new object[] { From, Target, Message }}); 
  • See that in the client's InitializeCommunicationChannel method, we register our form to receive all events passed from the IChatEvents interface (fEventsReceiver.RegisterEventHandler(this, typeof(IChatEvents));).
  • Also take a look how the Ping method calls were implemented, which allow the client application to notify the server that the client application is still running, thus preventing the session from expiring.
public void Ping()
{
    //Used to receive ping requests from clients
}
  • See the implementation part of our services (e.g. the Login operation in LoginService_Impl).
public virtual void Login(String Nickname)
    {
        lock (ChatServerService.UserClientIDList)
        {
            if (ChatServerService.UserClientIDList.Contains(Nickname)) throw new Exception("Nickname already in use");
            if (Session["nick"] != null)
                ChatServerService.UserClientIDList.Remove(Session["nick"]);

            SubscribeClientEventSink(typeof(IChatEvents));
            IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents), new Guid[] { Session.SessionID });
            foreach (DictionaryEntry et in ChatServerService.UserClientIDList)
            {
                ev.UserLogin(et.Key as String);
            }

            ChatServerService.UserClientIDList.Add(Nickname, Session.SessionID);

            ArrayList lDestinations = new ArrayList();
            foreach (DictionaryEntry et in ChatServerService.UserClientIDList)
            {
                lDestinations.Add(et.Value);
            }
            ev = (IChatEvents)GetEventSink(typeof(IChatEvents), (Guid[])lDestinations.ToArray(typeof(Guid)));

            ev.UserLogin(Nickname);
        }
        Session["nick"] = Nickname;
    }

    public virtual void Logout()
    {
        lock (ChatServerService.UserClientIDList)
        {
            ChatServerService.UserClientIDList.Remove(Session["nick"]);
        }
        IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents));
        ev.UserLogout(Session["nick"] as String);
        UnsubscribeClientEventSink();
        DestroySession();
    }
  • Note that we call the SubscribeClientEventSink function. This function will subscribe the current client to the event sink, passed there as a parameter. This SubscribeClientEventSink function is typical for a .NET solution. Without it, the client would not be able to receive events.
  • See how we can obtain an event proxy in order to fire events
IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents), new Guid[] { Session.SessionID });
  • Note that we pass the recipient ID into the function new Guid[] { Session.SessionID }. It allows us to restrict the recipients list to required clients. This feature is used for filling the list of available users for a new client when it logs into chat.
  • After adding a new user to the chat users list, we obtain the sync proxy ev = (IChatEvents)GetEventSink(typeof(IChatEvents)); event again, but this time without any restrictions on clients in order to notify everybody that a new user has just logged into the system ev.UserLogin(Nickname).
  • Look at the implementation of the Talk and TalkPrivate functions in ChatServerService_impl. These functions only differ in that the TalkPrivate implementation first defines the recipient and then fires the event only for that client: IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents), new Guid[] { cGuid }); (where cGuid is the ID of recipient).
public virtual void TalkPrivate(String TargetNickname, String Message)
  {
      Guid cGuid;
      lock (userClientIDList)
      {
          if (userClientIDList.Contains(TargetNickname))
              cGuid = (Guid)userClientIDList[TargetNickname];
          else
              throw new Exception("Invalid user: " + TargetNickname);
      }
      IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents), new Guid[] { cGuid });
      ev.Message(Session["nick"] as string, TargetNickname, Message);
  }

  public virtual void Talk(String Message)
  {
      IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents));
      ev.Message(Session["nick"] as string, "", Message);
  }
  • Note that with the event proxy (that we obtain with the help of the GetEventSink function), we can call all event methods that are defined in our RODL, which will then automatically be dispatched to all clients registered to receive the events.

Concepts Covered