Click here to Skip to main content
15,867,330 members
Articles / Web Development / HTML5

In-Memory peer to peer chat

Rate me:
Please Sign up or sign in to vote.
4.71/5 (6 votes)
13 Jul 2014CPOL3 min read 18.5K   433   15   4
This article dipicts the design of a low resource consumed P2P chat system

Introduction

What about a chat portal which offers 100% privacy? Yes, in practice it is impossible but at least can't we manage not to store users' personal chat messages in server? 

This article is an attempt to design such a chat system. I am using what ever language features coming to mind so please keep in mind that there are better and other suitable options available.

Solution Design

Requirement: A very simple chat program which allows individuals to chat each other, while not keeping their personal messages in database.

Solution: Keep the messages temperorily in an in-memory object until it is delivered to the other user or until a specific time delay, say 2 minutes is expired.

Technical Details:

Image 1

Here, a static List<> is used to temperorily store messages. Browser will pull messages from this object asynchronously in defined intervals, eg: 3 seconds and show in browser. Once the message is read from the object those will be instantly removed. Orphen messages in list also will be removed on defined intervals so that the variable memory consumption remains minimum.

Technologies and features used in the sample application are:

  1. HTML5 and CSS
  2. jQuery - version: 1.10.2
  3. ASP.NET with C# under .NET Framework 4.5 

In this sample, no any sophisticated technologies like WebAPI are used so the funcationality can be implemented in any language easily.

P2P chat involves chat between two persons. Let me call them you and a stranger.

Using the code

Client side code

Below are the PUSH and PULL chat message handlers written in jQuery.

JavaScript
$(function () {
        $("#t").focus();
        $("#s").click(function () { // PUSH
            if ($("#t").val().trim() != "") {
                u1 = $("#u1").val();
                u2 = $("#u2").val();
                $.ajax({
                    url: "e.aspx",
                    type: "POST",
                    data: { c: "p", u1: u1, u2: u2, t: escape($("#t").val()) }
                }); 
        // Pushes new message along with user1 (you) and user2 (stranger)'s usernames
            }
            $("#t").val("").focus();
        });
        
        
    });
    function pull() { // PULL - check server to see if any new messages available
        u1 = $("#u1").val();
        u2 = $("#u2").val();
        if (u1.trim() == "" || u2.trim() == "") return;
        $.ajax({
            url: "e.aspx",
            type: "POST",
            data: { c: "g", u1: u1, u2:u2 }
        })
        .done(function (d) {
            $.each($.parseJSON(d), function (k, v) {
                var cls = "cu";
                var me = v.wu;
                if (v.wu == u1) {
                    cls = "cu1";
                }
                
                $("#c").html($("#c").html() + " <span class='" + cls + "'>" + me + ": " + v.msg + "</span><br />");
                $("#c").scrollTop(1000000);
            });
        });
    }

And, pull() is executed every X seconds, eg:

JavaScript
setInterval(pull, 1000);

Server side code

Next code shows how to handle PULL and PUSH commands triggered from browser.

First, a chat message record looks like this:

C#
public class REC
{
    public string user1 { get; set; } // your nick
    public string user2 { get; set; } // stranger's nick
    public string msg { get; set; } // chat message
    public string wu { get; set; } // prointer to which user's message
    public DateTime dt { get; set; } // datetime details of message for the use of removing orphen messages
}

Below is the declaration of our in-memory storage object:

C#
public static List<REC> data;

Next comes PUSH and PULL handlers.

C#
string ret = null;
u1 = Request["u1"].ToString().ToLower();
u2 = Request["u2"].ToString().ToLower();
switch (Request["c"])
{
    case "p": // PUSH
        var t = Uri.UnescapeDataString(Request["t"].ToString());
        DateTime dt = DateTime.UtcNow;
        data.Add(new REC(u1, u2, t, u1, dt));
        data.Add(new REC(u2, u1, t, u1, dt));
        ret = "1";
        break;
    case "g": // PULL
        var z = data.Where(x => (x.user1.Contains(u1)));
        ret = new JavaScriptSerializer().Serialize(z);
        data.RemoveAll(x => (x.user1.Contains(u1)));
        data.RemoveAll(x => (DateTime.UtcNow - x.dt).TotalMinutes > 2); // remove orphen messages after 2 mins
        break;
}
Response.Write(ret);

PUSH: Adds the incoming chat message to data. You can see, two records are inserted - one for you and other for stanger.

PULL: Retrieves the chat message of particular user. Instatly, it removes it from the data object after retrieval. Second RemoveAll() lamda removes any messages which are of orphen in nature. Due to network issues or browser closing, there might be unused messages pending delivery.

Points of Remember

  1. A Static List<> is what came to my mind at first but note that using static is not a recomended method. It can loose values if the app domain is recycled, it will not work for load balanced environments, its storage capability depends on how much memory your machine hold etc. Please explore other ways of in-memory storage
  2. This sample application is not scalable unless modified for scalability
  3. This design does not consider group chats.
  4. If a third person uses one of the first or second user's nick, the system will behave weird
  5. No proper exception handling in sample code
  6. Coding best practices hardly followed

Demo

I have hosted a sample demo here. But I dont know how long this will be available, so please do not complain if the link is not working.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect ORION INDIA SYSTEMS
India India
Praveen.V.Nair - aka NinethSense - PMP, Microsoft MVP - is working as a Head of Technology and Architecture at Orion India Systems, Kochi, India. He has been playing with electronics from the age of 10 and with computers from the age of 14. He usually blogs at http://blog.ninethsense.com/.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Spencer Kittleson14-Jul-14 16:49
professionalSpencer Kittleson14-Jul-14 16:49 
GeneralRe: My vote of 5 Pin
Praveen Nair (NinethSense)14-Jul-14 21:00
Praveen Nair (NinethSense)14-Jul-14 21:00 
Thank you Spencer.

It is true that I did not give importance to the coding standards but, unlike server code (C#), giving importance to client code (js/html/css) naming optimization is always good since it can reduce the amount of data downloaded from server there by increasing the website loading performance, though it does not make much difference for small apps like this. I am a fan of short variable names (only client side scripting), minification and bundling Smile | :)
PraVeeN
blog.ninethsense.com/


modified 15-Jul-14 6:08am.

GeneralMy vote of 5 Pin
Humayun Kabir Mamun14-Jul-14 0:18
Humayun Kabir Mamun14-Jul-14 0:18 
GeneralRe: My vote of 5 Pin
Praveen Nair (NinethSense)14-Jul-14 21:01
Praveen Nair (NinethSense)14-Jul-14 21:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.