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

In-Memory peer to peer chat

0.00/5 (No votes)
13 Jul 2014 1  
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:

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.

$(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:

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:

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:

public static List<REC> data;

Next comes PUSH and PULL handlers.

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 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