By now, you’ve surely realised that “HTML5” is so much more than just markup. There’s also an army of associated JavaScript APIs. Among the ranks are a few new technologies that open up how we communicate between client and server and across documents. Let’s take a look.
This post will be an overview of the technologies available, how well they’re currently supported, and, where possible, live demos. I’m going to touch on the following technologies:
- XHR & XHR2 with CORS
- Web Messaging
- Web Sockets
- Server Sent Events
- Web Workers
Before I get on to the APIs, I want to outline a fairly common communication model that several of these APIs use.
A common communication event model
All event handlers (with the exception of XHR) receive an event
object containing a data
property. This property includes the data sent as part of the message.
The event model (again with the exception of XHR) is mostly based around onmessage
and postMessage
or send
. For example:
// in the recipient code
recipient.onmessage = function (event) {
console.log('received message: ' + event.data);
};
// from the sender code
recipient.postMessage('hi there'); // or recipient.send('hi there');
This is just a common model and isn’t the exactly the same among all these technologies. The two key similarities are that they use:
- a sending method (
postMessage
orsend
) on the recipient object, and - an event handler that listens for the
message
event and receives anevent
object containing adata
property.
Very importantly, most browsers only support sending strings from sender to recipient, so we often need to JSON stringify
and parse
if we want to send anything other than a string.
XHR & XHR2 with CORS
XHR can be both synchronous and asynchronous. XHR is the only API that (purposely) supports synchronous requests, meaning the execution of code will block until the callback fires.
There’s nothing particularly new about XHR, but in XHR2 we can handle uploads, and there’s a progress
event to tell you how the upload or download is getting on.
The super shiny new toy in XHR2 is its support for Cross-Origin Resource Sharing (CORS). This means you can make an XHR request across domains, but only if the server you’re connecting to allows it.
The request is as you’d expect from XHR:
var client = new XMLHttpRequest();
client.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
alert('The most awesome-est person to follow: ' + this.responseText);
}
};
client.open('GET', '/no-cors');
client.send();
If our server responds with a CORS header, however, we can put our XHR responder on another server. So on the URL http://remysharp.com/demo/cors.php, I have the following PHP script:
<?php
header('Access-Control-Allow-Origin: *');
?>
@rem
This says that anyone can make an XHR request to this particular script. Now when I run the following code in a browser that supports XHR2, the cross domain request succeeds!
var client = new XMLHttpRequest();
client.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
alert('The most awesome-est person to follow: ' + this.responseText);
}
};
client.open('GET', 'http://remysharp.com/demo/cors.php');
client.send();
Here’s a live example of CORS. (You can also edit it here.)
Note that IE8 supports CORS, but not XHR2 (no surprise there then). You need to use their proprietary (booo!) XDomainRequest
object. Nicholas C. Zakas has an excellent article explaining how to handle these differences.
XHR usage is pretty common already, but XHR2 with CORS is a winner over JSON-P, particularly as you have finer control over the request, can handle timeouts, and can handle errors correctly.
Support for XHR & XHR2 with CORS
- XHR support is pretty solid nowadays (even though IE6 uses
ActiveXObject
to get it going) - XHR2 with CORS: Safari & MobileSafari, Firefox 3.5, Chrome and IE8 (via XDomainRequest)
postMessage
This API is older, but it’s very useful if you want to get around the XHR same-origin rules. If you have an <iframe>
document that can accept onmessage
events from your origin (i.e., your site), then you can communicate across domains (and origins).
For example, a page that accepts an onmessage
event might contain code such as this:
window.onmessage = function (event) {
if (event.origin == 'mytrustedsite.com') {
alert('my trusted site said: ' + event.data);
}
};
Now you can include an <iframe>
that contains that code, and using the <iframe>
DOM node, you can post to the <iframe>
:
// where iframe is the actual iframe DOM node
iframe.contentWindow.postMessage("hello there", location.host);
This gives you the ability to send strings across two mutually trusted domains. (Remember that you can use JSON.stringify
and JSON.parse
to convert to an object to and from string format.)
Support for postMessage
- Chrome
- Safari
- Opera
- Firefox
- IE8
Here’s a demo using postMessage
.
There’s also a project called EasyXDM, which adds cross domain messaging to IE6 and upwards (along with all the other browsers) through the library’s abstraction. Definitely worth a look if this is a route you need to take.
Web Sockets
It’s my opinion that Web Sockets replaces Comet. Comet is a way of hacking the browser to giving us real-time server messages. The Web Sockets API provides that natively.
Web Sockets are used to send messages to and from the server — i.e., a bi-directional socket. In contrast to other similar technologies, with Web Sockets, you can go across domains, and you’re not bound by the same-origin policy. This means you can host your normal “apps” server while another server is for streaming content. Or you could host your own pages and connect to a live Twitter stream if your users turn on Web Socket support.
You can only send messages once the socket is open (duh). The communication model looks like this:
var ws = new WebSocket('ws://somesite.com/updates');
ws.onmessage = function (event) {
alert(event.data);
};
ws.onopen = function () {
ws.send('yay! we connected!');
};
Once the socket is closed, you can’t reuse it. Similarly, there’s no explicit method for opening a socket. That just happens when you create the WebSocket
object.
This API is extremely simple. I most often get asked, “What do you put on the server side?” I personally use Node, but you could use an Nginx server or something like Jetty. I’m no expert on the latter servers, but I can vouch that a Node-based server is very, very simple to get going. The live demo below also includes a link to the code that I used to run the server.
Check out this demo of Web Sockets.
Support for Web Sockets
- Chrome
- Safari & MobileSafari
There’s also an excellent Flash poly-fill called web-socket-js. Drop this into an application and it provides Web Sockets support as if it were native. I’ve used this on a few projects of my own, and it works very well.
In early December 2010, there was a security notice posted about Web Sockets, and both Firefox and Opera pulled it from their upcoming releases. Mozilla have said that they expect Web Sockets to be back in Firefox by version 4.0.1.
Server-Sent Events
The Server-Sent Events API is something that originated from Opera back in 2006 and is used for pushing events from the server to the client. Note that the client cannot send messages to the server through an EventSource
(SSE) — it can only listen for messages.
This API uses the onmessage
model. It’s constructed using the EventSource
object and is limited by the same origin rules:
var es = new EventSource('/sse');
es.onopen = function () {
console.log('opened stream');
};
es.onmessage = function (event) {
console.log('new message: ' + event.data);
};
The SSE automatically connects when you create the object (similar to Web Sockets), and once open will trigger the onopen
event.
Here’s a live demo of Server-Sent Events
Here’s how this is to work: when a new message is pushed from the server to the client, it triggers the onmessage
callback.
The key to your server is ensuring it doesn't close the connection on the client - the browser. Most of the examples around the web are doing this: closing the connection which tells the API to switch in to polling mode (note that this is the exact problem I hit when I first published this article).
When the API is in polling mode, it's no more different from an XHR poll, and the onopen
will continually fire.
Support for Server-Sent Events
- Opera 11
- Safari & MobileSafari
- Chrome
Web Workers
Web Workers are a way of creating a new thread of execution inside the browser. I’m including this because you still need to communicate with your Web Workers, and the method of communication is similar to some of those techniques discussed above. Do note, however, that this is not a method communicating from a client (browser) to a server. It’s more like there’s another browser window executing a particular block of JavaScript.
As an example of when to use a Web Worker, say you’re running a lot of JavaScript and the UI becomes unresponsive. The browser UI hangs because, in a way, it’s a “single-threaded application”. (Under the hood it isn’t really, but from a rendering and JavaScript perspective it is). This JavaScript task could be given to a Web Worker so that the UI can continue functioning.
It’s vital to understand that a Web Worker lives in a sand-boxed environment that doesn’t have access to things like the DOM. What’s more, you can only communicate with it using the onmessage
and postMessage
functions.
Our application can create and send messages to a worker using the following code:
var worker = new Worker('bigjob.js');
worker.onmessage = function (event) {
alert('Message from worker: ' + event.data); // remember event.data is a string!
};
worker.postMessage('task=job1');
In the JavaScript file bigjob.js
, we run some computationally intensive task and listen for messages in a way similar to what we’ve done in the previous examples. We can also post messages back to the invoking application:
this.onmessage = function (event) {
var job = event.data;
if (job == 'task=job1') {
job1();
} else if (job == 'task=job2') {
job2();
}
};
// just a pseudo example
function job1() {
// do some big task
while (working) {
// continue task
this.postMessage('job1 ' + amountComplete + '% complete');
}
this.postMessage('job1 100% complete');
}
There’s a lot more to Web Workers than just running a couple of small tasks, and no doubt we HTML5 Doctors will be posting a detailed article soon. This example just demonstrates how to communicate with Web Workers and how similar that is to the other technologies we’ve discussed here.
Support for Web Workers
- Chrome
- Safari
- Opera
- Firefox
A final word
Hopefully you agree that this is just the tip of the iceberg of communicating between client and server in HTML5. We’re no longer stuck with the same origin policy we had with vanilla Ajax. In fact, when you think about it, since IE8, we’ve actually had decent cross-domain messaging.
I’m personally most excited about Web Sockets and the support of CORS in services that have APIs, like Flickr, Twitter, and URL shorteners. What could you build with this?
15 Responses on the article “Methods of communication”
Although Comet was originally defined as a paradigm for Ajax to allow server to client push that definition, along with the technology, has evolved. In Alex Russell’s diagram he lists a “Comet engine” and I believe the technology behind the Comet engine doesn’t have to be JavaScript (in the same way WebSockets isn’t just for Web Browsers).
The same method of connecting to a server and receiving push updates has been used in many client applications using many technologies. In Kwwika for instance, which uses clustered Comet servers under the hood, we have APIs for JavaScript and .NET, Silverlight, ActionScript (Flash), C, Objective-C (iPhone/iPad) and Java. Just because we have these client APIs that connect to our service does this mean that we can’t call our servers Comet servers. IMHO, no!
A good Comet server implementation will hide away the underlying communication protocol. The client API will choose the most appropriate means of communication for the client technology and runtime environment (including network) as well as having a fallback strategy e.g. try HTTP Streaming then HTTP long-polling then, if you really have to, fallback to HTTP classic polling.
The next evolution for Comet servers has been to add support for the WebSocket protocol as one of it’s communication options. The WebSockets protocol is still under development so my preferred choice of communication would probably still be HTTP Streaming as it’s more reliable and is less likely to be interfered with by proxies and firewalls (WebSockets rely on HTTP headers which can easily be mangled. HTTPS connections are a solution).
So, I wouldn’t put it as you have. Instead of:
I would instead say:
If WebSockets gets the support it deserves it will win out. The native bi-directional support is a really big deal and I hope that the protocol becomes rock solid and that the Internet Infrastructure quickly updates to make it suitable for all production environments.
Whoops! That final quote should’ve been:
Hey, I am pretty sure browsers implement server-sent more correctly than you indicate here. What I suspect is happening is that you are closing the connection from the server and therefore the browser reestablishes the specification as you are returning a 2xx response. This is all per the specification.
Hah, the browser reestablishes the connection, not the specification! :-)
Testcases for EventSource can be found here: http://tc.labs.opera.com/apis/EventSource/ Also accessible via SVN: http://tc.labs.opera.com/svn/apis/EventSource/
The problem you’re most likely seeing with SSE isn’t that browsers are broken but that your server is closing the connection! Opera 11 should be spec compliant. You just need to keep the connection alive from your end.
Also, structured clone is supported for window.postMessage and worker.postMessage since Opera 10.60. I think structured clone is supported in other browsers too these days, at least for workers.
Another friend in the Communications APIs is MessageChannel (supported in Opera and WebKit).
Hi. Great blog, but I disagree with your opinion that “that Web Sockets replaces Comet”. I believe that Web Sockets are best used within a Comet implementation as the best possible streaming connection technique, rather than as a replacement for Comet. There are various reasons for my belief, but these are the ones that immediately come to mind. In the interests of full disclosure, I work for a company that provides a Comet server, however I believe the following statements are impartial observations of some genuine issues with Web Sockets.
1. There are Web Socket issues with proxy servers. The protocol looks somewhat like http, however it isn’t quite http. It smuggles an 8 byte body to the server during the negotiation stage which is likely to cause massive issues with proxy servers. Web proxies love to interfere with HTTP connections and until they are all updated to understand Web Socket protocol (or, more unlikely, if even possible, the Web Socket protocol is changed so that it conforms to http) this is likely to become a more significant barrier than browser support for Web Sockets. In this scenario a Comet library can failover to a non Web Socket connection type.
2. There is no intrinsic support (at the moment) for heartbeats to keep connection alive. Badly configured firewalls have a wonderful tendency to decide that a connection must be dead if no data has been sent through it for a while, defeating the purpose of a supposed persistent connection. Comet implementations typically already have application level heartbeats to solve this. Obviously a Web Socket implementation can do this too, but you’re starting on a slippery slope of writing more code to get around their deficiencies – surely it would be better to use a library that already does this for you (and covers all the scenarios you haven’t thought of yet).
3. With Web Sockets you need to write your own reconnection logic, potentially including failover to another server if a connection to the original server cannot be established. This is a very real scenario when dealing with the transient network connectivity of mobile devices. Again Comet implementations will typically handle this for you.
Web Sockets are absolutely awesome, providing full duplex connection. However the fact that they are low level means that you need to write a reasonable amount of code to make them reliable within a production environment. Couple this with the fact that they aren’t supported in all browsers, and the most sensible route I can see is to use a library that encapsulates all of this logic for you. if someone decided to write one of these from scratch, rather than using one of the existing Comet libraries, then, for me at least, I would consider it to be a Comet implementation, but one that only happens to use the Web Socket protocol.
Although Comet was originally defined as a paradigm for Ajax to allow server to client push that definition, along with the technology, has evolved. In Alex Russell’s diagram he lists a “Comet engine” and I believe the technology behind the Comet engine doesn’t have to be JavaScript (in the same way WebSockets isn’t just for Web Browsers).
The same method of connecting to a server and receiving push updates has been used in many client applications using many technologies. In Kwwika for instance, which uses clustered Comet servers under the hood, we have APIs for JavaScript and .NET, Silverlight, ActionScript (Flash), C, Objective-C (iPhone/iPad) and Java. Just because we have these client APIs that connect to our service does this mean that we can’t call our servers Comet servers. IMHO, no!
A good Comet server implementation will hide away the underlying communication protocol. The client API will choose the most appropriate means of communication for the client technology and runtime environment (including network) as well as having a fallback strategy e.g. try HTTP Streaming then HTTP long-polling then, if you really have to, fallback to HTTP classic polling.
The next evolution for Comet servers has been to add support for the WebSocket protocol as one of it’s communication options. The WebSockets protocol is still under development so my preferred choice of communication would probably still be HTTP Streaming as it’s more reliable and is less likely to be interfered with by proxies and firewalls (WebSockets rely on HTTP headers which can easily be mangled. HTTPS connections are a solution).
So, I wouldn’t put it as you have. Instead of:
I would instead say:
If WebSockets gets the support it deserves it will win out. The native bi-directional support is a really big deal and I hope that the protocol becomes rock solid and that the Internet Infrastructure quickly updates to make it suitable for all production environments.
Note: This is a resubmission of my comment on January 18th, 2011 at 7:13 pm since the previous one is still awaiting moderation.
Regarding the Web Worker, I’m a little more than a newbie in javascript programmer, and I always thought you could reach multi-thread capabilities within javascript with the usage of the setTimeout() function.
Can you please clarify that? What are the benefits of WebWorker (besides the context isolation) over the well-known setTimeout() function?
Thanks for this summary, this is a subject I am sorely lacking in. This is a great jumping point for research.
Even though Server-Sent Events are only “officially” supported in some browsers, it’s pretty easy to add support in others. In Firefox, for example, you can use progress events on the XHR object to mimic the behavior. Unfortunately, IE will still need to do polling until they support progress events, but it’s very easy to switch to the correct approach on the server. In my YUI EventSource component, I just add a custom header via JavaScript telling the server that the browser only supports polling, which allows you to write server-side code similar to the example posted here: http://yuilibrary.com/gallery/show/eventsource
@Ricardo – Web workers are very different than setTimeout(). The setTimeout() function simply tells the browser to wait until later to execute JavaScript – but it does so on the same thread (called the UI thread, the same one responsible for repaints and reflows). Web workers actually execute outside of the UI thread, so they can be executing at the exact same time as code that manipulates the DOM. Keep in mind that Web Workers cannot access the DOM because they don’t execute on the UI thread, so you are limited to passing data into and out of a worker. You may want to check out my presentation about JavaScript performance, which has some info about this: http://www.slideshare.net/nzakas/high-performance-javascript-fronteers-2010
@Nicholas – I’ve also written an EventSource polyfill that I’m using in jsconsole.com> for the remote device debugging that’s used on the mobile devices that don’t natively support EventSource – like the Android 2.2 and Palm Pre. Exactly as you described, it defaults down to Ajax based polling: https://github.com/remy/polyfills/blob/master/EventSource.js
It’s not 100% perfect, but it definitely mirrors basic usage of EventSource and gives me the device support I needed.
I have a question about using server side code (Node.js) and SSE. It seems once you open a response, you will keep trying to post to the response stream even if it has been closed by the client. In the code you posted sseConnections is an array that holds every http response object that is created. Once a browser closes the connection and then starts back up, you will still be sending SSEs to response objects that no longer exist…
Is there any way to determine if a response object is still valid?
So that if you have 100’s of people connecting to your site, you will not keep trying to update them once they have closed the browser?
@cclark: This is an old comment, but to follow up, if you add a listener for the “close” event on the request object, you can do any sort of necessary cleanup.
For example, to close down a redis pub/sub connection when someone closes their window, you can do the following:
req.on("close", function() {
// close down this pub/sub connection when a client
// closes their browser window
pub_sub_client.unsubscribe();
pub_sub_client.quit();
});
Hi all
Someone to know how to do an Audio Streaming between clients with HTML5? (In any browser)
Join the discussion.