上一章节中简单讲诉了前端使用websocket部分,这一章节讲诉后端使用websocket部分。
后端使用的nodejs,websocket包使用的websocket地址,可点击查看详细文档。
websocket基于事件的编程模型与nodejs中自定义事件相差无几,nodejs的事件驱动的方式十分擅长与大量的客户端保持高并发连接,所以websoket与nodejs的配合感觉很完美。
相比http,我觉得websocket更接近于传输层协议,它并没有在http的基础上模拟服务器端的推送,而是在TCP上定义独立的协议。让人迷惑的部分在于websocket的握手部分是由http完成的,让人觉得它可能是基于http实现的。
这里就不展开讲了,主要还是讲解怎么使用。
至于为什么用这个websocket包,因为这个websocket包简单好用。
下面直接进入正题:
创建server的核心代码段
function websocket_connection(s, handler) {
this._internal_s = s;
this.h = handler;
s.socket.setTimeout(40000); //设置超时时间
var self = this;
//监听超时事件
s.socket.addListener("timeout", function() {
_log("client is closed by timeout.");
self.destroy();
});
//console.log(s);
s.on("error", function () {
self._internal_s = null;
});
s.on("close", function () {
var close_fn = self.h[SYS_MSG.CLOSE];
if (close_fn != undefined && typeof close_fn === "function") {
_log("recv msg id ---> " + "_close");
close_fn({}, self);
}
});
}
exports.start_websocketserver = function (port, handler, protoroot) {
var ws = require("nodejs-websocket");
var server = ws.createServer(function (s) {
//当有客户端来连接时,会触发这个回调函数。
var self = new websocket_connection(s, handler);
//_log(port);
self.remoteAddress = self._internal_s.socket.remoteAddress;
self.remotePort = self._internal_s.socket.remotePort;
self.localAddress = self._internal_s.socket.localAddress;
self.localPort = self._internal_s.socket.localPort;
//_log(self);
//监听二进制数据事件
s.on("binary", function (inStream) {
//创建buffer对象,收集二进制数据
var buf = new Buffer.alloc(0);
//读取二进制数据的内容并且添加到buffer中
inStream.on('readable', function () {
var newBuf = inStream.read()
if (newBuf)
buf = Buffer.concat([buf, newBuf], buf.length + newBuf.length)
})
inStream.on('end', function () {
//读取完成二进制数据后,处理二进制数据
var msgid = "" + buf.readInt16BE(0);
_log("recv msg id ---> " + msgid);
var body_buffer = new Buffer.alloc(buf.length - 2);
buf.copy(body_buffer, 0, 2, buf.length - 2);
//body_buffer即为我们需要的二进制数据
buf = null;
})
});
s.on("text", function (data) {
try {
var msg = JSON.parse(data);
}
catch (e) {
}
});
var connect_cb = self.h[SYS_MSG.CONNECT];
console.log("connect_cb");
if (connect_cb != undefined && typeof connect_cb === "function") {
_log("recv msg id ---> " + "_connect");
connect_cb({}, self);
}
}).listen(port);
_log("websocket listen on port : " + port + " ok!");
return server;
};
ws.createServer(cb(s))返回一个server对象,server对象调用listen设置要监听的端口。handler是消息号与处理函数的对应关系。
当有客户端向服务器发起连接并且连接成功时,会触发一次cb,s则表示与该客户端建立的这条新连接。
text类型数据和binary类型接收时触发的是不同的事件,text数据接收只会触发一次函数回调。但是binary数据接收,可能会触发多次,每次接收到的数据可能都只有一部分,所以需要将触发readable事件读取的数据拼接起来,当所有数据接收完成的时候,会触发end事件。
需要注意的是,以前在使用nodejs-websocket的setTimeout时有一个小问题,当一个客户端和服务器只建立成功了TCP连接但是并没有升级协议为websocket,此时并不会触发cb,我们拿不到s变量所以不能设置超时,也无法断开连接,不确定最新版本是否还有这个问题。可以修改nodejs-websocket的Connection.js文件,设置超时。
服务器端还需要处理很多异常的情况,上述只是一个简单的应用。
下一章将会讲解整合protobuf。