Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Ajax Chat Sample via Comet Ajax

0.00/5 (No votes)
22 Apr 2010 2  
An article of how you can create serious web applications via PokeIn Comet Ajax library in minutes

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.

ChatSampleCS

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here