Socket.io-node可以说是现在很火的一个nodejs project,这篇文章是Socket.io-node刚刚实现的时候,作者写的一篇文章,现在Socket.io-node的稳定版本是0.7,也添加了很多新的feature,有兴趣的可以看一下:https://github.com/LearnBoost/Socket.IO-node
==========================入正题分割线=====================
原文地址:http://howtonode.org/websockets-socketio
If you've stayed on top of the advances in the realtime web for the past few years, you've probably heard of different techniques aimed to reduce the latency (ie: speed) of the message exchange between a client and a server. If you're developing a multiplayer game, a chat application, or showing frequent updates of data like tweets or stock price changes, you probably want to reverse the traditional model of communication. So instead of requesting (polling) data on a specific interval of time, you want the server to send (push) data to the client.
Nowadays, terms like long polling
, comet
and WebSocket
come to mind when it comes to developing a realtime web application or widget. But it's not always obvious how they work, their advantages, disadvantages and limitations, or even what percentage of the web browser market share supports them.
Socket.IO is a lightweight API that runs on the browser and looks like this:
var socket = new io.Socket(); socket.on('connect', function(){ // connected! }); socket.on('message', function(msg){ // message coming }); socket.send('Hello world!');
If you're familiar with WebSocket
, the protocol that aims to simplify bi-directional communication over HTTP once and for all, you'll notice that it looks very similar. The difference is that Socket.IO, under the hood, will enable realtime communication for IE6-9, Firefox 3-4, Safari 3-5, Chrome 3-6, iOS (iPhone and iPad), and other commonplace user agents.
In this day and age, odds are that if you're a web developer you've used AJAX once or twice. Very much like what socket.io does for realtime, libraries like jQuery
have provided abstractions that aim to remove the incompatibilities of what browsers offer for asynchronous HTTP requests (IE uses a proprietary ActiveX object, and mostly everyone else uses the standard XMLHttpRequest).
Now, if you wanted to make a realtime widget that retrieves data from the server, your first idea might look somewhat like this:
setInterval(function(){ $.ajax({ url: '/my/page', success: function(data){ // do something with the data } }); }, 5000);
So, every 5 seconds we poll
the server for new updates. In my book, that's almost as efficient as the transmission of IP Datagrams on pigeons.
You might also want to try to reduce the interval, and say, put it at 100 milliseconds.
setInterval(function(){ // now this should be fast! $.ajax({ url: '/my/page', success: function(data){} }); }, 100);
However, we're ignoring two major downsides now:
Long polling addresses the weakness of traditional polling methods by asking the server for new information on a certain interval, but keeping the connection open if the server has nothing new for us. This technique dramatically decreases latency and network traffic, which means it efficiently disguises itself as a server-push technique.
function load(){ $.ajax({ url: '/my/page', success: function(){ // do something with the data }, complete: load, timeout: 20000 }); }
If you come from a more traditional programming environment (eg: Desktop software), you're probably wondering why we don't keep the connection open.
This is possible with at least two fairly well known techniques:
multipart/x-mixed-replace
MIME type (which is enabled by setting multipart = true
in the XMLHTTPRequest instance)Although it was introduced by Netscape in 1995 (yes, when some of us were still unable to read properly), the only commonplace user agent to support it is Firefox.
<iframe>
populated with a response with the headers Transfer-encoding: chunked
and Connection: keep-alive
.The technique consists of writing <script>
tags that call a function on the parent page as information becomes available to push to the client.
The disadvantage of this method is that it'll trigger a never-ending spinner or progress bar in most user agents, severely hurting the user experience. In Internet Explorer, this can be worked around by inserting the <iframe>
in a hidden document (via the obscure ActiveX object htmlfile
). This technique was exposed to me for the first time thanks to the Gmail Chat team. This gem was analyzed/discovered back in the day by Alex Russell
By now, it's obvious that some lower-latency techniques are available to certain user agents, under certain conditions. The fundamental problem is that now the server has to treat HTTP requests differently, altering
All these techniques try to minimize the latency of the incoming data from the server, but normal XMLHTTPRequest have to be used to send data from the client. Which brings us to the most optimal solution available today.
Meet WebSocket, an effort "to provide a mechanism for browser-based applications that need two-way communication with servers that does not rely on opening multiple HTTP connections", as the author Ian Hickson puts it.
WebSocket takes advantage of the Upgrade header of the HTTP/1.1 specification, which means it's essentially a new protocol for communication:
The Upgrade general-header allows the client to specify what additional communication protocols it supports and would like to use if the server finds it appropriate to switch protocols. Examples: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
WebSocket won't close the connection after sending a message or receiving one. It's essentially "TCP for the web", but with a security model built in the protocol, a fairly rare framing system and UTF-8 encoding (no binary).
If we choose to implement it, some of the problems stated above still hold true:
Node.JS presents developers with a truly unique and exciting possibility: rolling your own scalable non-blocking HTTP server in one of the most popular dynamic scripting languages of all time, with a simplistic API.
Writing a (dummy) long-polling server is as easy as:
http.createServer(function (request, response) { setTimeout(function(){ response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World/n'); }, 20000); }).listen(8124);
Even with this simple API, consolidating your app logic in a way that works across all transports can be difficult.
Socket.IO-node
is here to help: Here's a chat server in 10 lines of code that also announces (broadcasts) who connects to the server:
var buffer = []; io.on('connection', function(client){ client.send({ buffer: buffer }); client.broadcast({ announcement: client.sessionId + ' connected' }); client.on('message', function(message){ var msg = { message: [client.sessionId, message] }; buffer.push(msg); if (buffer.length > 15) buffer.shift(); client.broadcast(msg); }); client.on('disconnect', function(){ client.broadcast({ announcement: client.sessionId + ' disconnected' }); }); });
And the best part is, under the hood, it'll handle your WebSocket, WebSocket over Flash, long polling, multipart and iframe connections. Sweet, isn't it?
If you want to learn more about Socket.IO, be sure to watch the git repositories, and check out some of the projects people have created with it: