Simple Web Chat with Meteor Comet Server
Comet is not really brand-new (Ajax) term today; however, with most of people how it works remains somewhat mysterious.
I have a little hand-on experience with Comet when creating a hobby game project with DWR Reverse Ajax sometime ago. It (DWR) was simple to start and really worked but required a Java web server (Tomcat, Jetty…) that I found rather expensive (in term of resources) for such small application. I want a small, dedicated and reliable server for Comet apps while don’t like to be deeply sunk in technical terms like “Bayeux” or “Continuation”.
After reviewing the Comet Maturity Guide from Comet Daily I decided to give Meteor Comet server a try for several reasons, especially because it’s built on Perl that can be easily deployed in any server with Perl installed (i.e. almost all Linux servers, no?) and it should be lightweight (up to my experience with Perl). Here is other info about Meteor server:
- Server daemon will run on any platform for which Perl is available.
- In live use typically 1,000 clients per node receiving 2 msgs (~400 bytes) per sec each. Tested up to 5,000 clients per node receiving 1 msg/sec each.
- Nodes exist independently, supports broadcasting for message distribution. Cluster of three Meteor nodes runs FT Alphaville.
- Transports are completely configurable within simple constraints.
- Server supports client ‘catch-up’, allowing clients to regulate quality of service themselves.
- Stable, in production use.
- GPL v2. Free.
Install & Setup Meteor Server
You can find very good how-to guide for installing Meteor server from Meteorserver.org; actually it’s quite simple to follow. Just download, extract to default location (/usr/local/meteor) and configure daemon service as guided. One notice is on Fedora/Cent OS the start function in init scripts must be changed from:
sudo –u root ./meteord >/var/log/meteord 2>&1 &
to:
./meteord >/var/log/meteord 2>&1 &
After installed and run, the server will listen on 2 ports: 4670 and 4671 as default - the first port is “Subscriber port” and the second is “Controller port”. However, you typically want to serve your website on port 80 to avoid cross-site scripting restrictions, therefore, Meteor must also serve on the same port. Yes, the installation guide points out three possible ways to do that:
1. Set SubscriberPort to 80 and SubscriberIP to a specific IP, say X.X.X.1, in the Meteor config file. This will bind Meteor to a particular IP address on port 80 and leave Apache to bind to port 80 on a different IP. To make this work you will need to run Meteor as root, and configure Apache (or whatever web-server you’re using) to bind to port 80 on a different IP, say X.X.X.2.
2. Configure a firewall to redirect traffic on port 80 of one specific IP, to the Meteor Subscriber port (typically 4670). Then both Meteor and Apache can run in their default configurations, and Meteor can be run as a non-privileged user. So, if you’re using iptables you need the following commands:
1 2 3 iptables -t nat -I PREROUTING -p tcp --dport 80 --destination X.X.X.1 -j REDIRECT --to-port 4670 iptables -A INPUT -p tcp --dport 4670 -j ACCEPT /etc/init.d/iptables saveor if you’ve got a hardware firewall, set up an equivilent set of rules on that.
3. Run Meteor on a different physical server to the one running Apache. Then each server need only have one IP, and Apache can be run in its default configuration. Meteor must be set to use port 80 and be run as root.
Well, in brief, you’ll need either another dedicated (virtual) server or to obtain another IP. But if you don’t have another server and don’t want to buy extra IP just for testing, I can tell you the forth way to get Meteor server work in the same machine and the same IP. The answer is to configure “Reverse Proxy” to pass all requests from specific domain to Meteor. Fortunately, it’s very simple to do that with Apache’s mod proxy. Here are what I did.
1. Make sure Apache to load “mod proxy” by commenting out the following line in httpd.conf file.
1 2 3 | LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_connect_module modules/mod_proxy_connect.so LoadModule proxy_http_module modules/mod_proxy_http.so |
2. Add “reverse proxy” for the sub-domain (like data.yourdomain.com) that points to Meteor
1 2 3 4 5 6 7 8 9 10 | NameVirtualHost *:80 # other virtual hosts #... <VirtualHost *:80> ServerAdmin admin@localhost ServerName data.yourdomain.com ProxyPass / http://127.0.0.1:4670/ ProxyPassReverse / http://127.0.0.1:4670/ </VirtualHost> |
Restart Apache and you’ll see the magic work!
The Chat Application
I often start with a Comet solution by building an online chat so that I can test how fast and reliable the solution is across different browsers. Also, it’s interesting to see messages coming in real-time manner.
Of course, there is no rocket science here; it looks simple like the demo below.
Essentially, you’ll have to include Meteor’s scripts from sub-domain, data.javascriptly.com in this demo. This script file comes from Meteor server we configured as above and enclose all functions to pull messages in (near) real time.
<script type="text/javascript" src="http://data.javascriptly.com/meteor.js" charset="utf-8"></script>Then, setup Comet connection on page load:
1 2 3 4 5 6 7 8 9 10 11 | // Set this to something unique to this client Meteor.hostid = "1234567"; // Our Meteor server is on the data. subdomain Meteor.host = "data." + location.hostname; // Call the addMsg() function when data arrives Meteor.registerEventCallback("process", addMsg); // Join the chat channel and get last 10 events, then stream Meteor.joinChannel("chat", 10); Meteor.mode = 'stream'; // Start streaming! Meteor.connect(); |
Every time a message arrives, the addMsg() function will be executed to add it to chat console, simple enough!
Now, when you click “Send” button the message you’ve typed in will be sent to Apache/PHP server (not the Meteor server) using an Ajax POST.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function sendMsg(){ var msg = $('#message').val(); if(msg.length == 0) return; var user = $('#user').val(); if(user.length == 0) user = "Guest"; $.post('chat.php', { user: toCharRef(user), message: toCharRef(msg) }); $('#message').val(""); $('#message').focus(); } |
On receipt Apache/PHP will parse the message and write it to Meteor’s Controller which is listening on port 4671 like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ... $ch = "chat"; $op = false; // Open a controller channel to Meteor server echo "Connecting to Meteor\n"; if (!($op = fsockopen("127.0.0.1", 4671, $errno, $errstr, 5))) { echo "Meteor not responding\n"; } else{ socket_set_blocking($op,false); echo "Connected\n"; $haswritten = false; $buf = ""; $out = "ADDMESSAGE ".$ch." <strong>".$user." ".$time."</strong> ".$message."\n"; ... } |
Meteor server, then, delivers added message to all clients subscribing to “chat” channel. That’s all.
The app works seamlessly on IE6/7/8, Firefox 3, Safari 3.1 while on Opera 9.2 and Google Chrome Meteor uses “simple forever IFRAME” technique for streaming which makes hourglass pointer display all the time. Hope Andrew Betts and team will find some workaround to avoid this issue in next version?
Chat message supports Unicode (the feature many developers ignore) so you can chat in any language you like: German, French, Chinese, Japanese and English of course. Please feel free to leave your comments/questions here.


Meteor in the Madhouse
Dark Matter, Missing Planets and New Comets: Paradoxes Resolved, Origins Illuminated Second Edition














Programming ASP.NET MVC
Hal Leonard Recording Method Vol.3 Recording Software and Plug-ins (Hal Leonard Recording Methid) (v. 3)
JavaScript Programmer's Reference


Professional Web 2.0 Programming (Wrox Professional Guides)


Programming in Visual Basic 2008
Professional Ajax, 2nd Edition (Programmer to Programmer)
Professional ASP.NET 2.0 AJAX (Programmer to Programmer)
Zend Studio for Eclipse Developer's Guide (Developer's Library)
php|architect's Guide to Programming with Zend Framework
Firebug