Comet for the Non-Web Programmer
July 16th, 2008 by Mario Balibrerathe revolution
Orbited 0.5 enables a software developer to create robust, performant network applications for the web. If you know one of the common web frameworks, or are already comfortable writing PHP, for example, just download the Orbited client for your preferred language and you’re good to go. However, if you’ve never done any web work before, or are fed up with the traditional approach, you can just bypass the HTTP hacks altogether and instead write your internet application the way folks have been writing network software since the 70s - with a socket.
This means you stop thinking of the server as something that answers questions from the end-user, and start viewing each endpoint as an equal participant. Either side can send or receive messages whenever they want. This is a vastly more flexible, powerful programming model than the request/response paradigm that is shoved down our throats. This type of server is extremely simple to implement, requires no framework or knowledge of any protocol, and is, of course, completely platform- and language- agnostic.
What are the implications of this new web programming model? For starters, it means solving formerly complex problems in minutes instead of months (see Michael’s discussion of gmail), with minimal code and zero “web programming.” Because let’s face it: “web programming” just means trying to write network code on top of a protocol designed for static document transmission. This is something you can’t even attempt without a steaming pile of hacks that produce bloated architecture with serious usability issues.
So now that we’re in this brand new retro socket world, how do we go about making something?
use existing servers
I try not to dwell on it, but there was life before Al Gore invented the world wide web — it just didn’t have any meaning. There was life because there were UNIX networks inhabited by bearded sages who use terminals for everything to this day. They wrote roughly 3.5 bagillion protocols, each with literally 100 million server implementations. Take your pick. Want a chat app? Download an IRC server. How about an in-browser MMORPG with magical node clustering and load-balancing? Download ActiveMQ (or any of 10 million other Stomp brokers), fire up Orbited’s Stomp client and you’re set.
The fact is, virtually all network applications require uninhibited bidirectional communication. This means that the second you get a browser socket, you can use any code anyone has ever written, including Code Hunter, Morse Code, and Mountain Dew Code Red; and you can interact with any type of server the same way you would using telnet. You can launch a massive application without writing a line of server code. Code, code, code.
roll your own
But no, you’re a unique snowflake, and you absolutely must write your own. That’s fine. It’s easy. Just get yourself a socket (there’s one in every language, and they’re all the same), wait for a connection, and talk to your client with socket.send and socket.recv. I’m telling you, you can write this type of server in about 5 lines, and develop it into a really fun game in 50 to 100 more.
Now, I realize that the idea of writing all your games and gizmos on top of a raw socket might make some people out there uncomfortable and a little sweaty in key areas. Naturally, no reader of this fine blog belongs to that bunch. Nevertheless, it should be noted that there are alternatives. There are seriously tons of network libraries out there. If you want a super-performant app I’d have to recommend an asynchronous contender, such as libevent, Twisted, or Grizzly. I usually use dez.
Generally, this type of application will have a listener of some kind that just sits there waiting for incoming connections. Whenever someone connects, the listener passes responsibility on to an object that reads and writes. What could be simpler?
Since this is totally custom software, we’re going to need a protocol for the connection object to speak to the Javascript client. For anything with more than a couple command types it usually makes sense to write a JSON- or XML- based protocol, because these methods are flexible and easy to parse, and because whatever language you choose, someone’s already done the dirty work for you.
the easy way out
Often, however, one or both endpoints won’t need to send anything very complex. Maybe you’re building a stock-ticker that updates a value every second, or a live message board. For these cases, you can safely use a delimiter. A delimiter is a string of characters that you know won’t ever appear elsewhere in a protocol “conversation.” Thus, the delimiter can confidently signify the end of a protocol “sentence.” Example delimiters include line break, tab, “::1337::”, and the null byte. Once you’ve chosen a delimiter, you can parse incoming messages with a very simple Javascript function.
var separate_events = function() {
var sep = buff.indexOf(DELIM);
if (sep == -1) {
return;
}
parse_event(eval(buff.slice(0,sep)));
buff = buff.slice(sep+DELIM.length);
separate_events();
}
This function takes a buffer string (buff) and looks for your delimiter (DELIM). If it can’t find your delimiter, the function returns. Otherwise, it knows that the first chunk of the buffer is a complete sentence, because it ends with your delimiter and starts at index 0, where the previous delimiter left off the last time this function was called. It evals this string (buff from index 0 to DELIM’s index) and passes the result to a protocol interpreter function. At this point it shifts the buffer string forward and recurses. Once you have this function in place, you just need to feed it by appending any incoming messages to the end of the buffer.
conn.onread = read;
and
var read = function(incoming_text) {
buff += incoming_text;
separate_events();
}
Now you have individual messages being sent to a function called parse_event.
var parse_event = function(data) {
alert("server says: "+data);
}
And tada! You have a basic client. I have to emphasize that it doesn’t really matter what your protocol looks like. If the application calls for it, you can just send individual letters back and forth. Alternatively, you can use XML, or have a full-fledged HTTP-like system with headers and version-variable behavior and functionality. It’s likely that your application will speak with completely different accents in different directions. Look! Tutorials: Twisted, libevent, Grizzly. Say hello to your mother for me. :-*
