Question

In the project I am currently working on, we have the need to develop a web chat application, not a very complex chat, just a way to connect two people to talk about a very specific topic, we don't need any kind of authentication for one of the two users, we don't have to support emoticons, avatars, or stuff like that.

Some project members suggested that we could use XMPP through BOSH, I said that is like trying to catch a fish with a boat's net, and proposed a simpler method, like a simple Ajax/MySQL web chat, but we're worried about the performance hit in the server because of the constant polling of many chats open at the same time.

Has anyone done something like this before? What would you recommend?

Was it helpful?

Solution

You might also want to look into Comet.

It's used by GTalk, Meebo, and many other chat applications. A few years ago when I was experimenting with it, there weren't very many libraries or details about server architecture to implement it, but it looks like there is a lot more stuff out now.

Have a look at the cometd project for more technical information.

OTHER TIPS

What would you recommend?

XMPP through BOSH

There's no need to invent your own message format and transport protocol when somebody else has. If you try, it'll slowly grow to be just as complex as BOSH but without the benefit of third-party library support or standardization.

If you don't like the idea of HTTP-polling, you could have a Flash-movie on the chat page that has a constant connection to some deamon on the server, the Flash-movie would then invoke JavaScript functions on the client to update the chat as new messages comes along. (Unless you want a Flash interface for your chat..)

You might also want to look into Comet.

I thought everyone used cometd for this sort of thing.

BOSH is a standard for transporting XMPP over HTTP. It involves Comet for pushing data to the client.

There is a very good server for handling message pushing from server to browser (dubbed Comet) - Orbited. It's easily integrated with other technologies (Django, Rails, PHP etc.) just like memcached.

You really should check it if you want to handle serious load. Otherwise, simple Ajax polling is the best way.

The trick is to realise that the only time your app needs to invoke CGI on the server is when someone says something. For the regular polls, poll a static page that your CGI script updates whenever there is new chat. Use HEAD requests, compare the timestamps with those last seen, and only do a full GET when those change. I have a simple naive chat application implemented this way, and the load and bandwidth usage is negligible for the few tens of simultaneous users we have.

I did this very same thing a few months back and had fun just playing around with the concepts. I actually used the forever-frame technique instead of polling.

The below code is my "comet" js file that contains the general concepts required to get a "party chat" setup.

function Comet(key) {

  var random = key;
  var title = 'Comet';
  var connection = false;
  var iframediv = false;
  var browserIsIE = /*@cc_on!@*/false;
  var blurStatus = false;
  var tmpframe = document.createElement('iframe');
  var nl = '\r\n';

  this.initialize = function() {
    if (browserIsIE) {
      connection = new ActiveXObject("htmlfile");
      connection.open();
      connection.write("<html>");
      connection.write("<script>document.domain = '"+document.domain+"'");
      connection.write("</html>");
      connection.close();
      iframediv = connection.createElement("div");
      connection.appendChild(iframediv);
      connection.parentWindow.comet = comet;
      iframediv.innerHTML = "<iframe id='comet_iframe' src='./comet.aspx?key="+random+"'></iframe>";
    } else {
      connection = document.createElement('iframe');
      connection.setAttribute('id', 'comet_iframe');
      iframediv = document.createElement('iframe');
      iframediv.setAttribute('src', './comet.aspx?key='+random);
      connection.appendChild(iframediv);
      document.body.appendChild(connection);
    }
  }

  // this function is called from the server to keep the connection alive
  this.keepAlive = function () {
    if (!browserIsIE) {
        mozillaHack();
    }
  }

  // this function is called from the server to update the client
  this.updateClient = function (value) {
    var outputDiv = document.getElementById('output');
    outputDiv.value = value + nl + outputDiv.value;
    if (blurStatus == true) {
        document.title = value;
    }
    if (!browserIsIE) {
        mozillaHack();
    }
  }

  this.onUnload = function() {
    if (connection) {
      // this will release the iframe to prevent problems with IE when reloading the page
      connection = false;
    }
  }

  this.toggleBlurStatus = function(bool) {
    blurStatus = bool;
  }

  this.resetTitle = function() {
    document.title = title;
  }

  function mozillaHack() {
    // this hack will fix the hour glass and loading status for Mozilla browsers
    document.body.appendChild(tmpframe);
    document.body.removeChild(tmpframe);
  }
}

I thought everyone used cometd for this sort of thing.

I agree with John. But there was another question that was not answered.
I have done this but instead of using a database we used a flat file, it did eventually cripple the server, but it wasn't until we has ~450 active users, and if we had done it with a database it probably would have fared better.
This was done on a basic hosting account from Godaddy.

Edit: BTW Godaddy sounded less then amused when I got the phone call.

I think polling is the simplest approach and would recommend that first. If the load becomes a problem start, looking into more complicated techniques. A good discussion on the pros and cons are here - http://www.infoq.com/news/2007/07/pushvspull
http://ajaxian.com/archives/a-report-on-push-versus-pull

Checkout Speeqe. Its a open-source solution for Web-based chat rooms that uses BOSH and XMPP behind the scenes.

I just found this post, it is old, but polling concept gives troubles for a lot of poeple. So i'll put an implementation example here. But before giving it to you, I should give you an advice that made me mad some time ago :

When you poll, you should take care of sessions behaviour (race conditions). To make it simple : if you open a session, the session file is locked until the session is closed to avoid 2 theads writting different data into it. So, if you need a session to check if a user is logged or so, always close the session before polling.

My demo gives you an example of a polling implementation in PHP. I will not use a database, but a file instead. When you click polling button, you will enter the loop and until the file is modified, you will stay polling. When you fill the form and click Release, what you typed will be saved into the file. Modification time of the file will change so the polling will stop.

Tip: use a tool like Firebug to see what's happen.

Now lets speak in a better langage than my english :

<?php

    // For this demo
    if (file_exists('poll.txt') == false) {
        file_put_contents('poll.txt', '');
    }

    if (isset($_GET['poll'])) {

        // Don't forget to change the default time limit
        set_time_limit(120);

        date_default_timezone_set('Europe/Paris');
        $time = time();

        // We loop until you click on the "release" button...
        $poll = true;
        $number_of_tries = 1;
        while ($poll)
        {
            // Here we simulate a request (last mtime of file could be a creation/update_date field on a base)
            clearstatcache();
            $mtime = filemtime('poll.txt');

            if ($mtime > $time) {
                $result = htmlentities(file_get_contents('poll.txt'));
                $poll = false;
            }

            // Of course, else your polling will kill your resources!
            $number_of_tries++;
            sleep(1);
        }

        // Outputs result
        echo "Number of tries : {$number_of_tries}<br/>{$result}";
        die();
    }

    // Here we catch the release form
    if (isset($_GET['release']))
    {
        $data = '';
        if (isset($_GET['data'])) {
            $data = $_GET['data'];
        }
        file_put_contents('poll.txt', $data);
        die();
    }

?>

<!-- click this button to begin long-polling -->
<input id="poll" type="button" value="Click me to start polling" />

<br/><br/>

Give me some text here :
<br/>
<input id="data" type="text" />
<br/>

<!-- click this button to release long-polling -->
<input id="release" type="button" value="Click me to release polling" disabled="disabled" />

<br/><br/>

Result after releasing polling :
<div id="result"></div>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">

// Script to launch polling
$('#poll').click(function() {
    $('#poll').attr('disabled', 'disabled');
    $('#release').removeAttr('disabled');
    $.ajax({
        url: 'poll.php',
        data: {
            poll: 'yes' // sets our $_GET['poll']
        },
        success: function(data) {
            $('#result').html(data);
            $('#poll').removeAttr('disabled');
            $('#release').attr('disabled', 'disabled');
        }
    });
});

// Script to release polling
$('#release').click(function() {
    $.ajax({
        url: 'poll.php',
        data: {
            release: 'yes', // sets our $_GET['release']
            data: $('#data').val() // sets our $_GET['data']
        }
    });
});

</script>

You can try it here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top