SuperTCP Chat sample (.NET)
Overview
This sample shows how the Super TCP Channel can be used to create a chat application. The Super TCP Channel channel provides a flexible communication channel that uses persistent connections to enable true asynchronous calls and server callbacks.
The sample is compatible with the SuperTCP Chat Sample (Delphi), meaning the .NET client can talk to the Delphi server and vice versa.
Getting started
- Compile the entire solution.
- Run the server application.
- Run several client applications (3 client instances are recommended to demonstrate all implemented features).
- Test the chat by sending both broadcast and private messages. If the
[All Users]
item is selected in the users list, the message is visible to all the chat users, otherwise it is visible to the selected user only.
Examine the code
The server
- Open the
ChatServer.rodl
to see its 2 services and 3 events under theEventSync
element. - Notice that the
ChatServerService
is secure, the login procedure ID being controlled by theLoginService
as described in this article - Examine the Login service implementation:
public virtual void Login(String nickname)
{
lock (ChatServerService.UserClientIDList)
{
// Making sure the login name is unique
if (ChatServerService.UserClientIDList.Contains(nickname)) throw new Exception("Nickname already in use");
if (Session["nick"] != null)
ChatServerService.UserClientIDList.Remove(Session["nick"]);
// Subscribing the current client for events
SubscribeClientEventSink(typeof(IChatEvents));
// We need to provide the new client with knowledge about all existing users by sending their names one by one
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);
// Notifying all existing users about the new user
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;
}
- We call the
SubscribeClientEventSink
function. This function subscribes the current client to the event sink, passed as a parameter. Without calling this function, the client will never receive events. - See how we can obtain an event proxy in order to fire events. Note that we pass the recipient ID into this function as
new Guid[] { Session.SessionID }
. This allows to restrict the recipients list, otherwise the event is sent to all subscribed clients.
IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents), new Guid[] { Session.SessionID });
- The implementation of the
Talk
andTalkPrivate
functions inChatService_impl.cs
use a similar approach for sending events. These functions only differ in that theTalkPrivate
implementation first defines the recipient and then fires the event only for that client.
public virtual void TalkPrivate(String nickname, String message)
{
Guid cGuid;
lock (userClientIDList)
{
if (userClientIDList.Contains(nickname))
cGuid = (Guid)userClientIDList[nickname];
else
throw new Exception("Invalid user: " + nickname);
}
IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents), new Guid[] { cGuid });
ev.Message(Session["nick"] as String, nickname, message);
}
public virtual void Talk(String message)
{
IChatEvents ev = (IChatEvents)GetEventSink(typeof(IChatEvents));
ev.Message(Session["nick"] as String, "", message);
}
The client
- Examine
frmMain
class. Notice that this class supports theIChatEvents
interface that allows it to be an event handler. - The
events
field (of EventReceiver type) is responsible for the receiving and dispatching of events. It is necessary to call theRegisterEventHandler
method in order to start receiving events and dispatch them to the appropriate handler.
public class frmMain : System.Windows.Forms.Form, IChatEvents
{
// ...
private void Form1_Load(object sender, System.EventArgs e)
{
// ...
events.RegisterEventHandler(this, typeof(IChatEvents));
}
}
- Note that all events are fired on a background thread. To allow GUI elements access, the
Form.Invoke
must be used.