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 theEventSync
element. - Note that
EventSync
contains 3 events which our client will support (Message
,UserLogin
andUserLogout
). - See the implementation of these events in the client's
Main.cs
class. Note that thefrmMain
class supports theIChatEvents
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 theIChatEvents
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 inLoginService_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. ThisSubscribeClientEventSink
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
andTalkPrivate
functions inChatServerService_impl
. These functions only differ in that theTalkPrivate
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.