您的位置:首页 > Web前端 > Node.js

重复造轮子之基于nodejs的聊天室

2013-03-01 14:20 218 查看
首先说明下,这帖子虽然是原创,但是功能网上已经有很多实现的有很多。我只是记录下自己的进步而已

配置nodejs的环境大家可以看看我的另外一篇博文简单完成nodejs的服务创建

好了,我们先从服务端代码开始,基于nodejs哦。

简单的config.js文件,代码中的解释如果看不懂,可以再次阅读简单完成nodejs的服务创建这篇博文。如果还是不明白,可以留言询问。

var express = require('express')
, routesConfig = require('./routes/config')
, http = require('http')
, routes = require('./routes/config')
;

var app = express(),
//端口号
port = 8888;

app.configure(function () {
//定义一个私有变量减少作用域链的检索消耗(其实没必要这么写,1亿次检索才会有性能差异)
var _app = app, _express = express
//设置视图文件
_app.set('views', __dirname + '/views');
//设置模板引擎
_app.set('view engine', 'ejs');
//静态资源管理
_app.use(express.static(__dirname + '/'));
app.use(express.favicon());
//(这个自动处理是将post携带的数据,放到request.body里面)
_app.use(_express.bodyParser());
//使用请求头数据自动处理:
//(这个自动处理是将,某些不是请求头数据在head中或者直接携带在postData中的请求头信息写入到request.method,如果信息在postData里面,会将post.body里面的请求头信息删除)
_app.use(_express.methodOverride());
//使用调试?
//这个不是很清楚看接口对应代码貌似和debug有关
_app.use(_app.router);
});
app.get('/', routesConfig.default);
//接受消息
var chat = require("./routes/chat_box/push");
app.get('/push', chat.push);
//异常处理
app.configure('development', function () {
app.use(express.errorHandler());
});
//创建这个服务,并指定端口,完了之后初始端口号
http.createServer(app).listen(port, function () {

console.log("Express server listening on port " + port);
});
在这段代码中,默认访问比如:http://xxxx.xxxx.xxx/ 这种类型的自动执行了routesConfig.default,这个我就不细说了,反正就输出个html而已。

然后我们看看消息推送的代码

//url模块(nodejs)
var urllib = require('url');
/*socket模块(nodejs)
开启推送端口端口号为:9999,这个端口号其实随意的,只要木有被占用就行
*/
var io = require('socket.io').listen(9999);
//简单队列
var queue = function () {
var self = this;
this.list = [];
this.add = function (data, cb) {
self.list.push(data);
cb && cb();
};
this.get = function () {
return self.list.shift();
};
};
//事实上这个user是个山寨的数据库(我用来缓存数据的,这里偷懒了)
var user = {};
/*
事件绑定
connection事件为,服务端出现链接后执行
我在该事件中表现为,每存在一个链接用户,就为其开出一片内存,用于存放推送信息,这里我没优化,用户多的情况下 应该很坑爹
然后执行推送数据的监听
*/
io.sockets.on('connection', function (socket) {
(!user[socket.id]) && (user[socket.id] = new queue());
_push(socket);
});
/*
开启推送数据的监听,用setInterval,伪造出多线程
这边我没什么好的想法,有好的解决方案,求交流
*/
var _push = function (socket) {
var _setInterval;
_setInterval = setInterval(function () {
var item = user[socket.id] && user[socket.id].get();
if (item) {
socket.emit("chat_box", item);
}
}, 100);
};
/*
接受发送消息的接口,记得那个配置的js吗?
var chat = require("./routes/chat_box/push");
app.get('/push', chat.push);
这句话就是转到这个接口来处理了
*/
exports.push = function (req, res) {
var params
, uname
, quantity
, text;
params = urllib.parse(req.url, true).query;
uname = params.uname;
quantity = params.quantity;
text = params.text;
//接受到发送消息后,为每个链接用户加入推送消息
for (var i in user) {
user[i].add({
userName: uname,
flowersQuantity: quantity,
text: text
})
}
//服务端的监控,可以在命令提示符中看到
console.log('有新消息');
};
好了服务端的代码就这些了,这里主要还是依靠socket.io这个nodejs插件。

接下来,我们看下客户端的脚本,需要说明的是因为刚开始写的时候需求不同,个别命名还是初期的命名,从英文角度看,有点变扭。

//简单队列
var queue = function () {
var self = this;
this.list = [];
this.add = function (data, cb) {
self.list.push(data);
cb && cb();
};
this.get = function () {
return self.list.shift();
};
}
//接收队列
var flowersQueue = new queue()
//单例队列
, singletonMsgQueue = new queue()
//包队列
, boxMsgQueue = new queue()
//用户送花总数存住对象
, userData = {}
, $chat_box = $("#chat_box")
;
/*
实现逻辑:
1、出现新数据
2、数据进入接收队列
3、执行进接收队列的回调(判断送花数据是单例还是包,我把包定义成需要刷屏的数据)
4、单例数据,就是不需要刷屏的数据,直接进入单例队列
5、包数据,拆分成单例数据进入单例队列
6、使用监听器监听单例队列,按照步长输出
*/
//添加新的送花数据进队列后的回调(内部判断,单例送花,还是送花包)
var flowersQueueAddCallBack = function () {
//从队列获取一个数据
var _item = flowersQueue.get();
//不存在就返回。。。你懂得
if (!_item) return;
//如果数据中的某个属性值确定是需要刷屏处理的,我这边是因为我定的业务逻辑所以这么写了
if (_item.flowersQuantity == 20) {
//进入包队列,并执行回调
boxMsgQueue.add(_item, function () {
boxMsgQueueAddCallBack();
});
} else {
/*
这边也有业务逻辑,就是发普通聊天信息和送花信息,这些不纠结,这边就是直接进入单例队列
事实上我这边这么处理会因为网速,机器好坏,导致聊天,和刷屏一起的时候 没法所有用户同步。
这边其实可以,直接对容器append,单例数据直接获取到,直接插入,我这边暂时没这么修改,下个版本我考虑把它优化掉
*/
if (_item.flowersQuantity != 0) {
userData[_item.userName] = (userData[_item.userName] || 0) + (_item.flowersQuantity - 0);
singletonMsgQueue.add('<p>' + _item.userName + '赠送' + _item.flowersQuantity + '朵鲜花,共' + userData[_item.userName] + '朵</p>');
} else {
singletonMsgQueue.add('<p>' + _item.userName + ":" + _item.text + '</p>');
}
}
};
//添加新的包数据进,包数据队列后的回调(出现新的包数据,根据步长,拆分成单例数据进入单例队列)
var boxMsgQueueAddCallBack = function () {
//取值,同上面的方法
var _item = boxMsgQueue.get();
var i, _setInterval;
if (!_item) return
//按照步长插入单例队列
i = 0;
_setInterval = setInterval(function () {
i++;
userData[_item.userName] = (userData[_item.userName] || 0) + 1;
singletonMsgQueue.add('<p>' + _item.userName + '赠送一朵鲜花,共' + userData[_item.userName] + '朵</p>');
if (i == _item.flowersQuantity) {
clearInterval(_setInterval);
}
}, 200);
};
//监听单例队列,插入文字(不管是单例数据还是包数据,经过处理都进入单例队列)
var monitorSingletonMsgQueue = function () {
//获取队列中的第一个
var _item = singletonMsgQueue.get();
var i;
//队列存在数据
if (_item) {
//插入一条新数据
$chat_box.append(_item).scrollTop(10000);
($chat_box.find("p").length > 200) && ($chat_box.find("p:eq(0)").remove());
//队列数据过多,则加快显示速度
if (singletonMsgQueue.list.length > 100) {
i = singletonMsgQueue.list.length;
while (i--) {
$chat_box.append(singletonMsgQueue.get()).scrollTop(10000);
($chat_box.find("p").length > 200) && ($chat_box.find("p:eq(0)").remove());
}
}
}

}
//监听器
setInterval(function () {
monitorSingletonMsgQueue();
}, 200);
//获取推送信息
var socket = io.connect('http://10.0.252.124:9999');
//这边就是获取推送信息的递归了,只要把推送来的数据放到,队列里面就可以了
socket.on('chat_box', function (data) {
flowersQueue.add(data, function () {
flowersQueueAddCallBack();
});
});


好了,山寨的聊天室第一个版本就写到这了,想交流的同学,可以加我的群43881427,验证码:小菜+我

也可以直接留言,有更优的解决方案,最好还是留言吧。大家一起看看
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐