Let's Create a Chat Addin for your Web Site
The chat feature on the web page to provide chat between the visitors, obviously would be good. I will try to explain how you can prepare this type of web page addins via PokeIn. If you have a rush, you can download the sample project from the link above. This time, I created a sample for VB.NET too. Please don't forget, you need to download PokeIn Library and add that as a reference on your project.
I had mentioned about the entry points of PokeIn in "this" article before. And now, I want to talk about the other parts of library.
Here is the entire Chat
class of our application:
C#
public class ChatApp
{
public static Dictionary Users = null;
public static Dictionary Names = null;
string ClientID;
string Username;
public ChatApp(string clientId)
{
ClientID = clientId;
Username = "";
}
~ChatApp()
{
lock (Users)
{
Users.Remove(ClientID);
}
lock (Names)
{
Names.Remove(Username);
}
}
public void SetName(string user_name)
{
if (Username != "")
{
PokeIn.Comet.CometWorker.SendToClient(ClientID,
"alert('You already have an username!');");
return;
}
bool duplicate = false;
lock (Names)
{
duplicate = Names.ContainsKey(user_name);
}
if (duplicate)
PokeIn.Comet.CometWorker.SendToClient(ClientID,
"alert('Another user is using the name you choose!
\\nPlease try another one.');");
else
{
lock (Names)
{
Names.Add(user_name, ClientID);
}
lock (Users)
{
Users.Add(ClientID, user_name);
}
Username = user_name;
PokeIn.Comet.CometWorker.SendToClient(ClientID,
"UsernameSet('" + PokeIn.Comet.BrowserHelper.SafeParameter(user_name) + "');");
}
}
public void Send(string message)
{
PokeIn.Comet.CometWorker.SendToAll("ChatMessageFrom('" +
PokeIn.Comet.BrowserHelper.SafeParameter(Username) +
"','" + PokeIn.Comet.BrowserHelper.SafeParameter( message ) +
"');");
}
}
VB.NET
Public Class ChatApp
Private ClientID As String
Public Shared Names As Dictionary(Of String, String)
Private Username As String
Public Shared Users As Dictionary(Of String, String)
Public Sub New(ByVal clientId As String)
Me.ClientID = clientId
Me.Username = ""
End Sub
Shared Sub New()
ChatApp.Users = Nothing
ChatApp.Names = Nothing
End Sub
Protected Overrides Sub Finalize()
Try
SyncLock ChatApp.Users
ChatApp.Users.Remove(Me.ClientID)
End SyncLock
SyncLock ChatApp.Names
ChatApp.Names.Remove(Me.Username)
End SyncLock
Finally
MyBase.Finalize()
End Try
End Sub
Public Sub Send(ByVal message As String)
PokeIn.Comet.CometWorker.SendToAll(String.Concat(New String() {
"ChatMessageFrom('", PokeIn.Comet.BrowserHelper.SafeParameter(Me.Username),
"','", PokeIn.Comet.BrowserHelper.SafeParameter(message), "');"}))
End Sub
Public Sub SetName(ByVal user_name As String)
If (Me.Username <> "") Then
PokeIn.Comet.CometWorker.SendToClient(Me.ClientID,
"alert('You already have an username!');")
Else
Dim duplicate As Boolean = False
SyncLock ChatApp.Names
duplicate = ChatApp.Names.ContainsKey(user_name)
End SyncLock
If duplicate Then
PokeIn.Comet.CometWorker.SendToClient(Me.ClientID,
"alert('Another user is using the name you choose!
\nPlease try another one.');")
Else
SyncLock ChatApp.Names
ChatApp.Names.Add(user_name, Me.ClientID)
End SyncLock
SyncLock ChatApp.Users
ChatApp.Users.Add(Me.ClientID, user_name)
End SyncLock
Me.Username = user_name
PokeIn.Comet.CometWorker.SendToClient(Me.ClientID,
("UsernameSet('" & PokeIn.Comet.BrowserHelper.SafeParameter
(user_name) & "');"))
End If
End If
End Sub
End Class
For every new connection, PokeIn creates an instance of this class with the clientId
of connection. So, ChatApp
class stores this information and waits for the username. Client side sends the username via SetName
function call and if there is no same username in the Users
dictionary, client side gets UsernameSet
event. After it, Client
can call the Send
function to send messages to others.
As you can see, we created a manager class for a web application just like for a desktop application.
Let's focus on some parts of code.
Why I'm Calling "PokeIn.Comet.BrowserHelper.SafeParameter" Function for String Parameter?
C#
PokeIn.Comet.CometWorker.SendToClient(ClientID, "UsernameSet('" +
PokeIn.Comet.BrowserHelper.SafeParameter(user_name) + "');");
VB.NET
PokeIn.Comet.CometWorker.SendToClient(Me.ClientID,
("UsernameSet('" & PokeIn.Comet.BrowserHelper.SafeParameter(user_name) & "');"))
As you know from the first article, SendToClient
function sends messages to browser side to run. So, every message may have a string
parameter in own function parameter list. And value of those parameters may contain some harmful characters for our JavaScript function body. To discard these types of errors easily, we can use this method.
Why We Used Destructor Function in Our Class?
C#
~ChatApp()
{
lock (Users)
{
Users.Remove(ClientID);
}
lock (Names)
{
Names.Remove(Username);
}
}
VB.NET
Protected Overrides Sub Finalize()
Try
SyncLock ChatApp.Users
ChatApp.Users.Remove(Me.ClientID)
End SyncLock
SyncLock ChatApp.Names
ChatApp.Names.Remove(Me.Username)
End SyncLock
Finally
MyBase.Finalize()
End Try
End Sub
If the user closes the chat window, we need to remove him safely from our active user list to free resources and available names. So, when PokeIn realizes that the user is offline, it will remove the object instances the client has. In this sample, our class destructors will run and we can remove the user information from our lists.
What for Lock / SyncLock ?
"Users" and "Names" dictionaries are usable for all active users concurrently (don't forget! we have created a multiuser application). So, for thread safety, we locked the shared objects. So we know that the shared dictionaries will be accessible by the entire application safely. If you downloaded the sample application, probably you saw that I locked these shared objects every time I used them.
Tricks
PokeIn.Comet.BrowserHelper.RedirectPage
: Allows you to redirect given client to given URL (useful for kick?:)), i.e., You can track the chat activity of each client and kick some of them via remoting their page to your application home, etc. Or some server operations may need to refresh all client pages.
PokeIn.Comet.BrowserHelper.SetElementProperty
: You can set any property of any client side object from the server side.
There will be some client elements that need changes directly from server side code.
PokeIn.Comet.BrowserHelper.SetElementEvent
: You can listen to client side element events from the server side (very useful?)
You can prepare the autocomplete controls easily with this method.
So, let me know about the articles on PokeIn library and/or Comet Ajax you want. ;)
History
- 22nd April, 2010: Initial post