http://book.mixu.net/ch13.html
很早开始就想用WebSocket完成即时消息推送功能。之前本打算用WebSocket + C#实现的,结果人上了年纪变笨了,弄了一天也没弄好 ⊙﹏⊙
今天参考了几篇资料,终于搞定了一个Socket.IO结合nodeJs的Demo。
用Socket.IO有个很大的好处就是开发者不需要去关心浏览器差异。Chrome下会用WebSocket,如果是用的IE它就会轮询。
nodeJs的环境搭建之类的知识这里就不提了,暂提供一个入门的文章: Node入门 ,Socket.IO的官网
再推荐一篇不错的外文:Comet and Socket.io deployment
后台代码 server.js
var fs = require('fs'), http = require('http'), sio = require('socket.io'); var server = http.createServer(function(req, res) { res.writeHead(200, { 'Content-type': 'text/html' }); res.end(fs.readFileSync('./index.htm')); }); server.listen(8888, function() { console.log('Server listening at http://localhost:8888/'); }); // Attach the socket.io server io = sio.listen(server); // store messages var messages = []; // Define a message handler io.sockets.on('connection', function(socket) { socket.on('message', function(msg) { console.log('Received: ', msg); messages.push(msg); socket.broadcast.emit('message', msg); }); // send messages to new clients messages.forEach(function(msg) { socket.send(msg); }) });
前台代码 index.htm
<html> <head> <style type="text/css"> #messages { padding: 0px; list-style-type: none;} #messages li { padding: 2px 0px; border-bottom: 1px solid #ccc; } style> <script src="http://code.jquery.com/jquery-1.7.1.min.js">script> <script src="/socket.io/socket.io.js">script> <script> $(function() { var socket = io.connect(); socket.on('connect', function() { socket.on('message', function(message) { $('#messages').append($('').text(message)); }); socket.on('disconnect', function() { $('#messages').append('Disconnected '); }); }); var el = $('#chatmsg'); $('#chatmsg').keypress(function(e) { if (e.which == 13) { e.preventDefault(); socket.send(el.val()); $('#messages').append($('').text(el.val())); el.val(''); } }); }); script> head> <body> <ul id="messages">ul> <hr> <input type="text" id="chatmsg"> body> html>
运行方法
注意一点:在index.htm中引用了一个文件"/socket.io/socket.io.js",这个是由后台的Socket.IO模块自动提供的,我们不需要去管它。
在Socket.IO官网给的例子里,没有说明这里点,害得我乱折腾一天也没明白,直到看到上面的那篇外文才明白。。。
这个小Demo连聊天室都算不上,只是完成了即时信息推送而已。接下来有时间了再继续完善吧!
So... do you want to build a chat? Or a real-time multiplayer game?
In order to build a (soft-) real-time app, you need the ability to update information quickly within the end user's browser.
HTTP was not designed to support full two-way communication. However, there are multiple ways in which the client can receive information in real time or almost real time:
Periodic polling. Essentially, you ask the server whether it has new data every n seconds, and idle meanwhile:
Client: Are we there yet? Server: No Client: [Wait a few seconds] Client: Are we there yet? Server: No Client: [Wait a few seconds] ... (repeat this a lot) Client: Are we there yet? Server: Yes. Here is a message for you.
The problem with periodic polling is that: 1) it tends to generate a lot of requests and 2) it's not instant - if messages arrive during the time the client is waiting, then those will only be received later.
Long polling. This is similar to periodic polling, except that the server does not return the response immediately. Instead, the response is kept in a pending state until either new data arrives, or the request times out in the browser. Compared to periodic polling, the advantage here is that clients need to make fewer requests (requests are only made again if there is data) and that there is no "idle" timeout between making requests: a new request is made immediately after receiving data.
Client: Are we there yet? Server: [Wait for ~30 seconds] Server: No Client: Are we there yet? Server: Yes. Here is a message for you.
This approach is slightly better than periodic polling, since messages can be delivered immediately as long as a pending request exists. The server holds on to the request until the timeout triggers or a new message is available, so there will be fewer requests.
However, if you need to send a message to the server from the client while a long polling request is ongoing, a second request has to be made back to the server since the data cannot be sent via the existing (HTTP) request.
Sockets / long-lived connections. WebSockets (and other transports with socket semantics) improve on this further. The client connects once, and then a permanent TCP connection is maintained. Messages can be passed in both ways through this single request. As a conversation:
Client: Are we there yet? Server: [Wait for until we're there] Server: Yes. Here is a message for you.
If the client needs to send a message to the server, it can send it through the existing connection rather than through a separate request. This efficient and fast, but Websockets are only available in newer, better browsers.
As you can see above, there are several different ways to implement Comet.
Socket.io offers several different transports:
Ideally, we would like to use the most efficient transport (Websockets) - but fall back to other transports on older browsers. This is what Socket.io does.
I almost left this part out, since I can't really do justice to Socket.io in one section of a full chapter. But here it is: a simple example using Socket.io. In this example, we will write simple echo server that receives messages from the browser and echoes them back.
Let's start with a package.json:
{ "name": "siosample", "description": "Simple Socket.io app", "version": "0.0.1", "main": "server.js", "dependencies": { "socket.io": "0.8.x" }, "private": "true" }
This allows us to install the app with all the dependencies using npm install
.
In server.js:
var fs = require('fs'), http = require('http'), sio = require('socket.io'); var server = http.createServer(function(req, res) { res.writeHead(200, { 'Content-type': 'text/html'}); res.end(fs.readFileSync('./index.html')); }); server.listen(8000, function() { console.log('Server listening at http://localhost:8000/'); }); // Attach the socket.io server io = sio.listen(server); // store messages var messages = []; // Define a message handler io.sockets.on('connection', function (socket) { socket.on('message', function (msg) { console.log('Received: ', msg); messages.push(msg); socket.broadcast.emit('message', msg); }); // send messages to new clients messages.forEach(function(msg) { socket.send(msg); }) });
First we start a regular HTTP server that always respondes with the content of "./index.html". Then the Socket.io server is attached to that server, allowing Socket.io to respond to requests directed towards it on port 8000.
Socket.io follows the basic EventEmitter pattern: messages and connection state changes become events on socket
. On "connection", we add a message handler that echoes the message back and broadcasts it to all other connected clients. Additionally, messages are stored in memory in an array, which is sent back to new clients so that the can see previous messages.
Next, let's write the client page (index.html):
$(function(){ var socket = io.connect(); socket.on('connect', function () { socket.on('message', function(message) { $('#messages').append($('').text(message)); }); socket.on('disconnect', function() { $('#messages').append('Disconnected '); }); }); var el = $('#chatmsg'); $('#chatmsg').keypress(function(e) { if(e.which == 13) { e.preventDefault(); socket.send(el.val()); $('#messages').append($('').text(el.val())); el.val(''); } }); });id="messages">
type="text" id="chatmsg">
BTW, "/socket.io/socket.io.js" is served by Socket.io, so you don't need to have a file placed there.
To start the server, run node server.js
and point your browser tohttp://localhost:8000/. To chat between two users, open a second tab to the same address.
Check out the Socket.io website (and Github for server, client) for more information on using Socket.io.
There are two more advanced examples on Github.
I'm going to focus on deployment, which has not been covered in depth.
As you can see above, using Socket.io is fairly simple. However, there are several issues related to deploying an application using Socket.io which need to be addressed.
The same origin policy is a security measure built in to web browsers. It restricts access to the DOM, Javascript HTTP requests and cookies.
In short, the policy is that the protocol (http vs https), host (www.example.com vs example.com) and port (default vs e.g. :8000) of the request must match exactly.
Requests that are made from Javascript to a different host, port or protocol are not allowed, except via two mechanisms:
Cross-Origin Resource Sharing is a way for the server to tell the browser that a request that violates the same origin policy is allowed. This is done by adding a HTTP header (Access-Control-Allow-Origin:) and applies to requests made from Javascript.
JSONP, or JSON with padding, is an alternative technique, which relies on the fact that the