/*============自定义对象===========*/
/*
* 方法:Array.remove(dx) 通过遍历,重构数组
* 功能:删除数组元素.
* 参数:dx删除元素的下标.
*/
Array.prototype.remove=function(dx)
{
if(isNaN(dx)||dx>this.length){return false;}
for(var i=0,n=0;i<this.length;i++)
{
if(this[i]!=this[dx])
{
this[n++]=this[i]
}
}
this.length-=1
}
/*寻找元素在数组中的位置*/
Array.prototype.indexOf = function (obj) {
for (var i = 0; i < this.length; i++) {
if (this[i] == obj) {
return i;
}
}
return -1;
}
//生成GUID,加上时间
function newGuid()
{
var guid = "";
for (var i = 1; i <= 32; i++){
var n = Math.floor(Math.random()*16.0).toString(16);
guid += n;
if((i==8)||(i==12)||(i==16)||(i==20))
guid += "-";
}
guid+="_"+new Date().getTime();
return guid;
}
//声明消息对象
function SfjWsMsg(key,value){
this.key=key;
this.value=value;
this.loginguid=null; //当前登录标识
this.identifyid=null;//每条消息唯一标识
this.state=null;//消息状态
}
/*============缓存管理===========*/
function HisMsgCache(){
this.msgcache=new Array();
//添加消息到缓存,
//hidelog notnull-不显示 null-显示(默认值)
this.addMsgToCache = function(userId,msg,hidelog){
if (!this.msgcache[userId]){
this.msgcache[userId]=new Array();
}
var msglist=this.msgcache[userId];
if (!hidelog){
console.log('User['+userId+'] addMsgToCache:'+msg);
}
msglist.push(msg);
}
//移除消息从缓存中
this.delMsgFromCache=function(userId,msg){
var msglist=this.msgcache[userId];
var index=msglist.indexOf(msg);
msglist.remove(index);
}
//返回用户对应消息列表
this.getMsgListFromCache=function(userId){
if (!this.msgcache[userId]){
this.msgcache[userId]=new Array();
}
var msglist=this.msgcache[userId];
return msglist;
}
//转换消息缓存中的waitback消息转换为nosend消息
this.transWaitBackToNoSend=function(userId){
if (!this.msgcache[userId]){
this.msgcache[userId]=new Array();
}
var msglist=this.msgcache[userId];
for(var i=msglist.length-1;i>=0;i--){
var msg=msglist[i];
var m=JSON.parse(msg)[0];
if (m.state===MSGSTATE_WAITBACK){
m.state=MSGSTATE_NOSEND;
msglist[i]='['+JSON.stringify(m)+']';
console.log('user['+userId+'] transWaitBackToNoSend:'+msglist[i]);
}
}
}
}
//消息缓存对象
var cache=new HisMsgCache();
//消息状态枚举值
var MSGSTATE_NOSEND="nosend"; //历史未发送消息
var MSGSTATE_WAITBACK="waitback"; //已发送未收到返回
/*============express管理===========*/
var app = require('express')();
app.get('/', function(req, res){
res.send('<h1>Welcome Realtime Server</h1>');
});
//在线用户列表
var onLineUsers={};
/*============Redis管理===========*/
//读取redis配置文件
var fs= require('fs');
var path = require('path');
//redis 链接
var redis = require('redis');
var pub;//发布消息
var store,store_a;//存储消息
var param;
//给消息增加唯一标识
function addMsgIdentID(msg){
var m=JSON.parse(msg)[0];
m.identifyid=newGuid();
var msg1='['+JSON.stringify(m)+']';
return msg1;
}
//给消息增加登录标识
function addMsgLoginGuid(msg,loginguid){
var m=JSON.parse(msg)[0];
m.loginguid=loginguid;
var msg1='['+JSON.stringify(m)+']';
return msg1;
}
//给消息增加状态标识
function addMsgState(msg,state){
var m=JSON.parse(msg)[0];
m.state=state;
var msg1='['+JSON.stringify(m)+']';
return msg1;
}
//发送历史/当前消息
//his notnull-历史消息 null-当前消息
function sendMsg(userId,msg,his){
//暂时加到缓存中,待收到客户端的ack后再删除
msg=addMsgState(msg,MSGSTATE_WAITBACK);
cache.addMsgToCache(userId,msg);
var info;
if (his){
info='user['+userId+'] sendhismsg:'+msg;
}else{
info='user['+userId+'] sendmsg:'+msg;
}
//发送消息
onLineUsers[userId].emit('message',msg,function (data) {
if (data) {
//参数说明:data=msg
//打印日志
console.log(info);
//接收客户端ack,从消息缓存中删除该消息
cache.delMsgFromCache(userId,data);
}
}
);
}
//发送历史消息
function sendHisMsg(userId){
store.hget(param.mobileloginguid, userId.toString(), function (e, loginguid) {
var msglist=cache.getMsgListFromCache(userId);
var i=0;
while (i<msglist.length){
var msg=msglist[i];
var m=JSON.parse(msg)[0];
if (m.loginguid===loginguid){//本次登录后的消息
if (m.state===MSGSTATE_NOSEND){ //未发送的消息
msglist.remove(i);
sendMsg(userId,msg,true);
}else{ //等待回应的消息
i++;
}
}else{//非本次登录的历史消息作废
msglist.remove(i);
}
}
});
}
fs.readFile('sysparam.properties',{encoding:'utf-8'}, function (err,data) {
if (err) {
throw err;
}
param=JSON.parse(data)[0];
pub = redis.createClient(param.redis_server_port, param.redis_server_ip);
store = redis.createClient(param.redis_server_port, param.redis_server_ip);
store_a = redis.createClient(param.redis_server_port, param.redis_server_ip);
// redis 链接错误
pub.on("error", function(error) {
console.log(error);
});
store.on("error", function(error) {
console.log(error);
});
store_a.on("error", function(error) {
console.log(error);
});
//redis 验证 (reids.conf未开启验证,此项可不需要)
pub.auth(param.redis_server_pwd);
store.auth(param.redis_server_pwd);
store_a.auth(param.redis_server_pwd);
console.log('connect redis');
//订阅频道消息
pub.subscribe(param.msgkey);
pub.on('message', function(channel, message){
//console.log('channel:'+channel);
var i=message.indexOf(":");
var userId=parseInt(message.substring(0,i));
var msg=message.substring(i+1,message.length);
msg=addMsgIdentID(msg);//增加唯一标识
store.hget(param.mobileloginguid, userId.toString(), function (e, loginguid) {
msg=addMsgLoginGuid(msg,loginguid);//增加当前登录标识
store.hget(param.mobileuserlist, userId.toString(), function (e, online) {
if (online && online==='true') {//用户已登录
//console.log("userId["+userId+'] online');
if (onLineUsers[userId]){//已连接客户端,发送当前消息
//发送当前消息
sendMsg(userId,msg);
}else{//与客户端断开,缓存当前消息
msg=addMsgState(msg,MSGSTATE_NOSEND);
cache.addMsgToCache(userId,msg);
}
}else{//用户未登录,不用进行处理
//console.log("userId["+userId+'] outline');
}
});
});
});
});
/*========socket.io管理=========*/
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', function(socket){
console.log('new user connected');
//监听新用户加入
socket.on('login', function(data){
//消息缓存中的waitback消息转换为nosend消息
cache.transWaitBackToNoSend(data.userId);
//获取旧socket
var old_socket=onLineUsers[data.userId];
if (old_socket && old_socket !== socket){
old_socket.isNowSocket=false;
old_socket.disconnect();
}
//启用socket
socket.isNowSocket=true;
onLineUsers[data.userId]=socket;
var userId=data.userId;
socket.userId=userId;
//打印日志
console.log('user['+userId+'] login');
//发送历史消息
sendHisMsg(userId);
});
//监听用户退出
socket.on('disconnect', function(){
if (socket.isNowSocket===true){
delete onLineUsers[socket.userId];
//打印日志
console.log('user['+socket.userId+'] loginout');
}
//在线人数
//console.log('onlineCount:'+onLineUsers.count());
});
//监听用户发布聊天内容
socket.on('message', function(obj){
//向所有客户端广播发布的消息
//io.emit('message', obj);
console.log('user['+socket.userId+'] ping');
});
});
http.listen(9001, function(){
console.log('listening on *:9001');
});