赞
踩
之所以写下此文,是我觉得越是简单的技术往往能发挥越重要的作用,随着各种新的技术的诞生,实时web技术已经走进我们。websocket和node.js使开发实时应用非常简单,同时性能也非常高。
关于websocket
websocket是html5的重要feature,它直接在浏览器上对与socket的支持,这给了web开发无限的想象,虽然以前也有 flash socket+js的实现,不过毕竟不稳定,而且兼容性有很多问题,当然websocket的普及也依赖于支持html5标准的浏览器的更新,目前只有 chrome、safari、firefox 4.0等少数浏览器可以支持,不过大势所驱,加上智能移动设备的普及,websocket可以有更大的作为。
他解决了web实时化的问题,相比传统http有如下好处:
本文来重点来分析下。
在继续本文之前,让我们了解下websocket的原理:
websocket通信协议实现的是基于浏览器的原生socket,这样原先只有在c/s模式下的大量开发模式都可以搬到web上来了,基本就是通过浏览器的支持在web上实现了与服务器端的socket通信。
WebSocket没有试图在HTTP之上模拟server推送,而是直接在TCP之上定义了帧协议,因此WebSocket能够支持双向的通信。
首先来介绍下websocket客户端与服务端建立连接的过程:
先用js创建一个WebSocket实例,使用ws协议建立服务器连接,ws://www.cnodejs.org:8088
ws开头是普通的websocket连接,wss是安全的websocket连接,类似于https。
客户端与服务端建立握手,发送如下信息:
GET /echo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: http://www.cnodejs.org:8088
Origin: http://www.cnodejs.com
服务端会发回如下:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://www.cnodejs.org
WebSocket-Location: ws://www.cnodejs.org:8088/echo
具体的ws协议,可以参考: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
我们在开发过程中不需要考虑协议的细节,因为websocket API已经帮我们封装好了。
需要注意的是所有的通信数据都是以”\x00″开头以”\xFF”结尾的,并且都是UTF-8编码的。
这个过程类似于http的建立连接过程,不同的是,建立连接之后,接下来客户端和服务端的任何交互都需要再有这个动作。客户端通过websocket API提供的如下4个事件进行编程:
让我们全面了解一下websocket API,他其实非常简单,下面是所有的API:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | [Constructor(in DOMString url, in optional DOMString protocols)]
[Constructor(in DOMString url, in optional DOMString[] protocols)]
interface WebSocket {
readonly attribute DOMString url;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
const unsigned short CLOSED = 3;
readonly attribute unsigned short readyState;
readonly attribute unsigned long bufferedAmount;
// networking
attribute Function onopen;
attribute Function onmessage;
attribute Function onerror;
attribute Function onclose;
readonly attribute DOMString protocol;
void send(in DOMString data);
void close();
};
WebSocket implements EventTarget; |
详细的websocket API,可以参考此文: http://dev.w3.org/html5/websockets/
终于讲到了正题了,node.js如何与websocket结合,websocket API是基于事件的,他是对于客户端而言,而对于服务端来说,如何来处理呢?其实可以简单的理解为实现websocket协议的socket server开发。
node.js天生就是一个高效的服务端语言,可以直接使用 javascript直接来处理来自客户端的请求,这样如果服务端这边需要大量的业务逻辑开发,则可以直接使用node开发。通过node和 websocket的结合可以开发出很多实时性要求很高的web应用,如游戏、直播、股票、监控、IM等等。
而node.js如何实现websocket的支持,已经有一个比较成熟的开源系统node-websocket-server: https://github.com/miksago/node-websocket-server,让我们来探究一二:
其实原理也是很简单就是用node实现了websocket draft-76的协议,同时他对外提供了api,可以方便其他应用程序简化编程。
它继承了node的http.Server的事件和方法,这样它简化了服务端的编程,同时可以处理http的请求。
为了实现连接之间的通信和消息的广播,它实现了一个manager类,给每一个连接创建一个id,然后在内存中维护一个连接链表,并提供了上线和下线的自动管理。
它还提供对以下几个事件的接口:
让我们看看一个node-websocket-server提供的一个server的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | var sys = require("sys")
, ws = require('../lib/ws/server');
var server = ws.createServer({debug: true});
// Handle WebSocket Requests
server.addListener("connection", function(conn){
conn.send("Connection: "+conn.id);
conn.addListener("message", function(message){
conn.broadcast("<"+conn.id+"> "+message);
if(message == "error"){
conn.emit("error", "test");
}
});
});
server.addListener("error", function(){
console.log(Array.prototype.join.call(arguments, ", "));
});
server.addListener("disconnected", function(conn){
server.broadcast("<"+conn.id+"> disconnected");
});
server.listen(8000); |
通过websocket打通了浏览器和服务端之后,我们就可以尝试搭建一个实际的应用,这里以实时监控系统为例。
直接与linux自身监控工具的结合,将监控结果通过websocket直接更到网页上,由于建立了socket长连接,绑定iostat的标准输 出的事件,做到了真正的实时。同时可以支持对监控结果的讨论,增加了一个简单的chat,基于事件的通讯中,chat和监控同时发送完全不受影响,所以还 可以把更多的事件加入进来。
让我们来看看这个过程:
首先是用node.js捕获iostat的输出:
1
2
3
4
5
6 | var sys = require("sys")
, ws = require('../lib/ws/server');
var sys = require('sys');
var spawn = require('child_process').spawn;
var mon = spawn("iostat",["-I","5"]); |
spawn可以根据参数启动一个进程,同时可以对stdout, stderr, exit code进行捕获,当这些事件触发时,可以绑定我们的函数,同时捕获其输出。
这里是iostat的标准输出:
disk0 cpu load average
KB/t tps MB/s us sy id 1m 5m 15m
14.64 4 0.06 7 5 88 0.76 0.95 0.90
我们捕获他的输出,将其发送到客户端去:
1
2
3
4
5 | mon.stdout.on('data',function(data) {
data = format_string(data);
sys.puts(data);
conn.send("#mon:"+data+"");
}); |
客户端也就是浏览器,在收到消息后,对其进行简单的字符串处理,然后就可以直接在网页中输出了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | w.onmessage = function(e) {
var msg = e.data;
if(msg.match(/#mon:/)) {
var monarr = msg.split(":")[1].split(" ");
var body = "";
for(var item in monarr) {
body += "<td id='io_"+item+"'>"+monarr[item]+"</td>"
}
$("#iobody").html(body);
//log(monarr[0]);
}
else
log(e.data);
} |
这里自定义了一个#mon的简单协议,这样可以对更多类型的输出分开处理。
服务端和客户端总共100多行的代码,就已经实现了一个实时服务器性能监控系统。
全部代码下载地址: http://cnodejs.googlecode.com/svn/trunk/monsocket/examples/
(注:本程序仅在mac osx下测试通过)
如果加上RGraph(基于html5),则可以打造更加精美的实时展现: http://www.rgraph.net/docs/dynamic.html
这篇文章适合node.js的初学者或者对于websocket不够了解的人,总结起来,就是以下几个点:
参考资料
这个例子非常的简单,可以看到对于websocket的server端开发,我们已经不需要考虑 websocket协议的实现,他几乎有着和客户端浏览器上websocket API一样的事件,只有对连接、断开连接、消息、错误等事件进行处理,这样应用的开发就非常的灵活了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。