Francisco Soto
Francisco Soto

Reputation: 10392

Chat application AJAX polling

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?

Upvotes: 14

Views: 6126

Answers (12)

Alain
Alain

Reputation: 36984

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

Upvotes: 0

melo
melo

Reputation: 315

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

Upvotes: 0

Toran Billups
Toran Billups

Reputation: 27407

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);
  }
}

Upvotes: 1

Svante Svenson
Svante Svenson

Reputation: 12478

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

Upvotes: 3

zuber
zuber

Reputation: 3487

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.

Upvotes: 2

moonshadow
moonshadow

Reputation: 89155

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.

Upvotes: 1

Jimmy
Jimmy

Reputation: 1433

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

Upvotes: 0

Ryan Guest
Ryan Guest

Reputation: 6470

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.

Upvotes: 7

UnkwnTech
UnkwnTech

Reputation: 90951

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.

Upvotes: 0

John Millikin
John Millikin

Reputation: 200976

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.

Upvotes: 2

hoyhoy
hoyhoy

Reputation: 6351

I thought everyone used cometd for this sort of thing.

Upvotes: 0

John Millikin
John Millikin

Reputation: 200976

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.

Upvotes: 5

Related Questions