[
  {
    "path": "README.md",
    "content": "基于Nodejs websocket socket.io的消息转发系统  message pusher written in nodejs based on socket.io\n==============\n\n消息实时推送，支持在线用户数实时统计。基于[Socket.IO](https://socket.io/)开发，使用websocket推送数据，当浏览器不支持websocket时自动切换comet推送数据。\n\n支持Linux,mac,windows等环境部署。\n\n\n\n效果截图\n======\n![node-msg-sender-demo](http://112.74.81.224:3000/images/demo.png)\n \n线上demo  \n======\n\nhttp://112.74.81.224:3000/\n\n可以通过url：http://112.74.81.224:3000/sendMsg/?type=private&uid=1504936989000&content=消息内容 向当前用户发送消息\n\n可以通过url：http://112.74.81.224:3000/sendMsg/?type=public&content=消息内容 向所有在线用户推送消息\n\nuid为接收消息的uid，如果不传递则向所有人推送消息  \ncontent 为消息内容\n\n注：可以通过php或者其它语言的curl功能实现后台推送\n\n下载安装\n======\n1、git clone https://github.com/gytai/node-websocket-msg-sender.git\n\n2、npm install\n\n3、apt-get install redis-server\n\n4、redis-server\n\n后端服务启动停止,先安装PM2(Advanced Node.js process manager，http://pm2.keymetrics.io/)\n======\n### 启动服务\npm2 start bin/www --name msg-sender\n\n### 停止服务\npm2 stop msg-sender\n\nWeb前端代码类似：\n====\n```javascript\n// 引入前端文件\n<script src=\"/socket.io/socket.io.js\"></script>\n<script>\n      var socket = io.connect('http://localhost:3000');\n      socket.emit('login', new Date().getTime());\n\n      // 后端推送来消息时\n      socket.on('message', function(msg){\n          $('#content').html('收到消息：'+msg);\n          $('.notification.sticky').notify();\n      });\n\n      // 后端推送来在线数据时\n      socket.on('update_online_count', function(data){\n          console.log(data);\n          $('#online_box').html('当前在线客户端数:&nbsp;'+data.online_count);\n      });\n</script>\n```\n\n其他客户端\n====\n根据websocket协议即可。具体参考websocket协议。\n\n\nNodejs后端调用api向任意用户推送数据\n====\n```javascript\n    var type = req.query.type || msgType.public;\n    var content = req.query.content || 'none';\n    var uid = req.query.uid;\n\n    switch (type){\n        case msgType.public:\n            ioSvc.serverBroadcastMsg(content);\n            break;\n        case msgType.private:\n            if(!uid){\n                return res.send({code:400,msg:'uid参数必传'});\n            }\n            ioSvc.serverToPrivateMsg(uid,content);\n            break;\n    }\n```\n\nHttp 发送数据，可以配置跨站发送（需要设置跨域放行）。例如安卓或者IOS等其他客户端也可以方便的发送消息。\n====\n可以通过url：http://localhost:3000/sendMsg/?type=private&uid=1504936989000&content=消息内容 向当前用户发送消息\n\n可以通过url：http://localhost:3000/sendMsg/?type=public&content=消息内容 向所有在线用户推送消息\n\n\n\n"
  },
  {
    "path": "app.js",
    "content": "var express = require('express');\nvar path = require('path');\nvar favicon = require('serve-favicon');\nvar logger = require('morgan');\nvar cookieParser = require('cookie-parser');\nvar bodyParser = require('body-parser');\n\nvar app = express();\n\nvar index = require('./routes/index');\nvar users = require('./routes/users');\n\n// view engine setup\napp.set('views', path.join(__dirname, 'views'));\napp.set('view engine', 'ejs');\n\n// uncomment after placing your favicon in /public\n//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));\napp.use(logger('dev'));\napp.use(bodyParser.json());\napp.use(bodyParser.urlencoded({ extended: false }));\napp.use(cookieParser());\napp.use(express.static(path.join(__dirname, 'public')));\n\napp.use('/', index);\napp.use('/users', users);\n\n// catch 404 and forward to error handler\napp.use(function(req, res, next) {\n  var err = new Error('Not Found');\n  err.status = 404;\n  next(err);\n});\n\n// error handler\napp.use(function(err, req, res, next) {\n  // set locals, only providing error in development\n  res.locals.message = err.message;\n  res.locals.error = req.app.get('env') === 'development' ? err : {};\n\n  // render the error page\n  res.status(err.status || 500);\n  res.render('error');\n});\n\nmodule.exports = app;\n"
  },
  {
    "path": "bin/www",
    "content": "#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar app = require('../app');\nvar debug = require('debug')('nodemessage:server');\nvar http = require('http');\nvar ioSvc = require('../io/io');\n/**\n * Get port from environment and store in Express.\n */\n\nvar port = normalizePort(process.env.PORT || '3000');\napp.set('port', port);\n\n/**\n * Create HTTP server.\n */\n\nvar server = http.createServer(app);\n\n//??Socket.IO\nvar io = require('socket.io')(server);\nioSvc.ioServer(io);\n\n/**\n * Listen on provided port, on all network interfaces.\n */\n\nserver.listen(port);\nserver.on('error', onError);\nserver.on('listening', onListening);\n\n/**\n * Normalize a port into a number, string, or false.\n */\n\nfunction normalizePort(val) {\n  var port = parseInt(val, 10);\n\n  if (isNaN(port)) {\n    // named pipe\n    return val;\n  }\n\n  if (port >= 0) {\n    // port number\n    return port;\n  }\n\n  return false;\n}\n\n/**\n * Event listener for HTTP server \"error\" event.\n */\n\nfunction onError(error) {\n  if (error.syscall !== 'listen') {\n    throw error;\n  }\n\n  var bind = typeof port === 'string'\n    ? 'Pipe ' + port\n    : 'Port ' + port;\n\n  // handle specific listen errors with friendly messages\n  switch (error.code) {\n    case 'EACCES':\n      console.error(bind + ' requires elevated privileges');\n      process.exit(1);\n      break;\n    case 'EADDRINUSE':\n      console.error(bind + ' is already in use');\n      process.exit(1);\n      break;\n    default:\n      throw error;\n  }\n}\n\n/**\n * Event listener for HTTP server \"listening\" event.\n */\n\nfunction onListening() {\n  var addr = server.address();\n  var bind = typeof addr === 'string'\n    ? 'pipe ' + addr\n    : 'port ' + addr.port;\n  debug('Listening on ' + bind);\n}\n"
  },
  {
    "path": "io/io.js",
    "content": "/*\n*介绍：socket.io 功能封装\n*作者：TaiGuangYin\n*时间：2017-09-09\n* */\nvar redis = require('../utils/redis');\nvar msgType = require('./messageTpye');\nvar ioSvc = require('./ioHelper').ioSvc;\n\n//服务端连接\nfunction ioServer(io) {\n\n    var _self = this;\n    ioSvc.setInstance(io);\n\n    //初始化连接人数\n    redis.set('online_count',0,null,function (err,ret) {\n        if(err){\n            console.error(err);\n        }\n    });\n\n    io.on('connection', function (socket) {\n        console.log('SocketIO有新的连接!');\n\n        _self.updateOnlieCount(true);\n\n        //用户与Socket进行绑定\n        socket.on('login', function (uid) {\n            console.log(uid+'登录成功');\n            redis.set(uid,socket.id,null,function (err,ret) {\n                if(err){\n                    console.error(err);\n                }\n            });\n            redis.set(socket.id,uid,null,function (err,ret) {\n                if(err){\n                    console.error(err);\n                }\n            });\n        });\n\n        //断开事件\n        socket.on('disconnect', function() {\n            console.log(\"与服务其断开\");\n            _self.updateOnlieCount(false);\n            redis.get(socket.id,function (err,val) {\n                if(err){\n                    console.error(err);\n                }\n                redis.del(socket.id,function (err,ret) {\n                    if(err){\n                        console.error(err);\n                    }\n\n                });\n                redis.del(val,function (err,ret) {\n                    if(err){\n                        console.error(err);\n                    }\n                });\n            });\n        });\n\n        //重连事件\n        socket.on('reconnect', function() {\n            console.log(\"重新连接到服务器\");\n        });\n\n        //监听客户端发送的信息,实现消息转发到各个其他客户端\n        socket.on('message',function(msg){\n            if(msg.type == msgType.messageType.public){\n                socket.broadcast.emit(\"message\",msg.content);\n            }else if(msg.type == msgType.messageType.private){\n                var uid = msg.uid;\n                redis.get(uid,function (err,sid) {\n                   if(err){\n                       console.error(err);\n                   }\n                   if(sid){\n                       //给指定的客户端发送消息\n                       io.sockets.socket(sid).emit('message', msg.content);\n                   }\n                });\n            }\n\n        });\n    });\n\n    this.updateOnlieCount = function (isConnect) {\n        //记录在线客户连接数\n        redis.get('online_count',function (err,val) {\n            if(err){\n                console.error(err);\n            }\n            if(!val){\n                val = 0;\n            }\n            if(typeof val == 'string'){\n                val = parseInt(val);\n            }\n            if(isConnect){\n                val += 1;\n            }else{\n                val -= 1;\n                if(val<=0){\n                    val = 0;\n                }\n            }\n\n            console.log('当前在线人数：'+val);\n            io.sockets.emit('update_online_count', { online_count: val });\n\n            redis.set('online_count',val,null,function (err,ret) {\n                if(err){\n                    console.error(err);\n                }\n            });\n        });\n    };\n\n}\n\n\n//模块导出\nexports.ioServer = ioServer;"
  },
  {
    "path": "io/ioHelper.js",
    "content": "var redis = require('../utils/redis');\n\nvar ioSvc = {};\nioSvc.io = null;\n\n//初始化实例\nioSvc.setInstance = function (io) {\n    this.io = io;\n};\n\nioSvc.getInstance =function () {\n    return this.io;\n};\n\n//服务器给所有客户端广播消息\nioSvc.serverBroadcastMsg = function (data) {\n    console.log('发送广播消息');\n    console.log(data);\n    this.io.sockets.emit('message',data);\n};\n\n//服务端给指定用户发消息\nioSvc.serverToPrivateMsg = function (uid,data) {\n    console.log('发送私人消息');\n    console.log(data);\n    redis.get(uid,function (err,sid) {\n        if(err){\n            console.error(err);\n        }\n        if(sid){\n            //给指定的客户端发送消息\n            this.io.sockets.socket(sid).emit('message',data);\n        }\n    });\n};\n\nexports.ioSvc = ioSvc;"
  },
  {
    "path": "io/messageTpye.js",
    "content": "const messageType= {\n    'public':'public',\n    'private':'private'\n};\n\nexports.messageType = messageType;"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"nodemessage\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"node ./bin/www\"\n  },\n  \"dependencies\": {\n    \"body-parser\": \"~1.17.1\",\n    \"cookie-parser\": \"~1.4.3\",\n    \"debug\": \"~2.6.3\",\n    \"ejs\": \"~2.5.6\",\n    \"express\": \"~4.15.2\",\n    \"morgan\": \"~1.8.1\",\n    \"serve-favicon\": \"~2.4.2\",\n    \"socket.io\": \"^2.0.3\",\n    \"socket.io-client\": \"^2.0.3\",\n    \"redis\": \"^2.8.0\"\n  }\n}\n"
  },
  {
    "path": "public/javascripts/notify.js",
    "content": "(function ($) {\n    $.fn.extend({\n        notify: function (options) {\n            var settings = $.extend({ type: 'sticky', speed: 500, onDemandButtonHeight: 35 }, options);\n            return this.each(function () {\n                var wrapper = $(this);\n                var ondemandBtn = $('.ondemand-button');\n                var dh = -35;\n                var w = wrapper.outerWidth() - ondemandBtn.outerWidth();\n                ondemandBtn.css('left', w).css('margin-top',  dh + \"px\" );\n                var h = -wrapper.outerHeight();\n                wrapper.addClass(settings.type).css('margin-top', h).addClass('visible').removeClass('hide');\n                if (settings.type != 'ondemand') {\n                    wrapper.stop(true, false).animate({ marginTop: 0 }, settings.speed);\n                }\n                else {\n                    ondemandBtn.stop(true, false).animate({ marginTop: 0 }, settings.speed);\n                }\n\n                var closeBtn = $('.close', wrapper);\n                closeBtn.click(function () {\n                    if (settings.type == 'ondemand') {\n                        wrapper.stop(true, false).animate({ marginTop: h }, settings.speed, function () {\n                            wrapper.removeClass('visible').addClass('hide');\n                            ondemandBtn.stop(true, false).animate({ marginTop: 0 }, settings.speed);\n                        });\n                    }\n                    else {\n                        wrapper.stop(true, false).animate({ marginTop: h }, settings.speed, function () {\n                            wrapper.removeClass('visible').addClass('hide');\n                        });\n                    }\n                });\n                if (settings.type == 'floated') {\n                    $(document).scroll(function (e) {\n                        wrapper.stop(true, false).animate({ top: $(document).scrollTop() }, settings.speed);\n                    }).resize(function (e) {\n                        wrapper.stop(true, false).animate({ top: $(document).scrollTop() }, settings.speed);\n                    });\n                }\n                else if (settings.type == 'ondemand') {\n                    ondemandBtn.click(function () {\n                        $(this).animate({ marginTop: dh }, settings.speed, function () {\n                            wrapper.removeClass('hide').addClass('visible').animate({ marginTop: 0 }, settings.speed, function () {\n\n                            });\n                        })\n                    });\n                }\n\n            });\n\n        }\n    });\n})(jQuery);\n"
  },
  {
    "path": "public/stylesheets/style.css",
    "content": "@charset \"utf-8\";\nbody {\n  margin:0px; padding:0px;\n  font-family: Arial, Helvetica, sans-serif;\n  background:url(/images/repeat.jpg);\n  font-size:15px;\n  color:#000;\n}\nul{list-style:none; margin:0px; padding:0px; margin-top:20px;}\nli{padding-bottom:20px;}\n.sticky p, .floated p, .fixed p, .ondemand p{ float:left; padding:0px; margin:0px; margin-left:10px; line-height:45px; color:#fff; font-size:12px;}\n.sticky a, .floated a, .fixed a, .ondemand a{ float:right; margin:13px 10px 0px 0px; }\nimg{border:0px;}\n.wrapper{padding:20px;}\n\n.sticky {\n\n  position:fixed;\n  top:0;\n  left:0;\n  z-index:1000;\n  width:100%;\n  border-bottom:3px solid #fff !important;\n\n  background: #91BD09; /* Old browsers */\n  background: -moz-linear-gradient(top, #91BD09 0%, #91BD09 100%); /* FF3.6+ */\n\n  /* FireFox 3.6 */\n  /* Safari4+, Chrome */\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr='#91BD09', endColorstr='#91BD09')\";\n  -pie-background: linear-gradient(#91BD09, #91BD09 100%);\n  behavior: url(PIE.htc);\n  -moz-box-shadow: 1px 1px 7px #676767;\n  -webkit-box-shadow: 1px 1px 7px #676767;\n  box-shadow: 1px 1px 7px #676767;\n  height: 45px;\n  background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0, #91BD09),color-stop(1, #91BD09));/* IE6,IE7 */\n  /* IE8 */\n  /* Firefox F3.5+ */\n  /* Safari3.0+, Chrome */\n}\n\n\n.floated {\n\n  position:absolute;\n  top:0;\n  left:0;\n  z-index:1000;\n  width:100%;\n  border-bottom:3px solid #fff !important;\n  background: #0e59ae; /* Old browsers */\n  background: -moz-linear-gradient(top, #0e59ae 0%, #0e59ae 100%); /* FF3.6+ */\n\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr='#0E59AE', endColorstr='#0E59AE')\";\n\n  -moz-box-shadow: 1px 1px 7px #676767;\n  -webkit-box-shadow: 1px 1px 7px #676767;\n  box-shadow: 1px 1px 7px #676767;\n  height: 45px;\n  background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0, #0E59AE),color-stop(1, #0E59AE));/* IE6,IE7 */\n  -pie-background: linear-gradient(#0E59AE, #0E59AE 100%);\n  behavior: url(PIE.htc);\n}\n\n\n.fixed {\n  position:absolute;\n  top:0;\n  left:0;\n  width:100%;\n  border-bottom:3px solid #fff !important;\n\n  background: #660099; /* Old browsers */\n  background: -moz-linear-gradient(top, #660099 0%, #660099 100%); /* FF3.6+ */\n\n  /* FireFox 3.6 */\n  /* Safari4+, Chrome */\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr='#660099', endColorstr='#660099')\";\n  -pie-background: linear-gradient(#660099, #660099 100%);\n  behavior: url(PIE.htc);\n  -moz-box-shadow: 1px 1px 7px #676767;\n  -webkit-box-shadow: 1px 1px 7px #676767;\n  box-shadow: 1px 1px 7px #676767;\n  height: 45px;\n  background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0, #660099),color-stop(1, #660099));/* IE6,IE7 */\n  /* IE8 */\n  /* Firefox F3.5+ */\n  /* Safari3.0+, Chrome */\n}\n\n.ondemand {\n\n  width:100%;\n  border-bottom:3px solid #fff !important;\n  position:absolute;\n  top:0;\n  left:0;\n  z-index:1000;\n\n  background: #CC0000; /* Old browsers */\n  background: -moz-linear-gradient(top, #CC0000 0%, #CC0000 100%); /* FF3.6+ */\n\n  /* FireFox 3.6 */\n  /* Safari4+, Chrome */\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr='#CC0000', endColorstr='#CC0000')\";\n  -pie-background: linear-gradient(#CC0000, #CC0000 100%);\n  behavior: url(PIE.htc);\n  -moz-box-shadow: 1px 1px 7px #676767;\n  -webkit-box-shadow: 1px 1px 7px #676767;\n  box-shadow: 1px 1px 7px #676767;\n  height: 45px;\n  background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0, #CC0000),color-stop(1, #CC0000));/* IE6,IE7 */\n  /* IE8 */\n  /* Firefox F3.5+ */\n  /* Safari3.0+, Chrome */\n}\n.ondemand-button\n{\n  width:40px !important;\n  height:40px;\n  float:right !important;\n  z-index:999;\n  position:absolute;\n  margin-right:100px!important;\n}\n\n\n\n\n\n#footer{width:100%; margin:0 auto; font-size:12px; color:#0E59AE; height:30px;  margin-top:200px;border-top:1px solid #CCC;padding:18px;}\n.hide{display:none;}\n\n\n/* Buttons */\n\n.round.button {\n  -moz-border-radius: 15px;\n  -webkit-border-radius: 15px;\n  border-radius: 15px;\n  background-image: url(button-images/round-button-overlay.png);\n  border: 1px solid rgba(0, 0, 0, 0.25);\n  font-size: 13px;\n  padding: 0;\n}\n\n.button {\n  -moz-border-radius: 5px;\n  -webkit-border-radius: 5px;\n  border-radius: 5px;\n  -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.50);\n  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.50);\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.50);\n  background: #222;\n  border: 1px solid rgba(0, 0, 0, 0.25);\n  color: white !important;\n  cursor: pointer;\n  display: inline-block;\n  font-size: 13px;\n  font-weight: bold;\n  line-height: 1;\n  overflow: visible;\n  padding: 5px 15px 6px;\n  position: relative;\n  text-decoration: none;\n  text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);\n  width: auto;\n  text-align: center;\n}\n\n.round.button span {\n  -moz-border-radius: 14px;\n  -webkit-border-radius: 14px;\n  border-radius: 14px;\n  display: block;\n  line-height: 1;\n  padding: 4px 15px 6px;\n}\n\n\n.green.button {\n  background-color:#91BD09;\n}\n.green.button:hover {\n  background-color:#749A02;\n}\n.green.button:active {\n  background-color:#a4d50b;\n}\n.blue.button {\n  background-color:#0E59AE;\n}\n.blue.button:hover {\n  background-color:#063468;\n}\n.blue.button:active {\n  background-color:#1169cc;\n}\n.purple.button {\n  background-color:#660099;\n}\n.purple.button:hover {\n  background-color:#330066;\n}\n.purple.button:active {\n  background-color:#7f02bd;\n}\n\n.red.button {\n  background-color:#CC0000;\n}\n.red.button:hover {\n  background-color:#990000;\n}\n.red.button:active {\n  background-color:#ea0202;\n}\n.close\n{}\n\n.show{\n\n  background: #CC0000; /* Old browsers */\n  background: -moz-linear-gradient(top, #CC0000 0%, #CC0000 100%); /* FF3.6+ */\n\n\n  /* FireFox 3.6 */\n  /* Safari4+, Chrome */\n  -ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr='#CC0000', endColorstr='#CC0000')\";\n  -pie-background: linear-gradient(#CC0000, #CC0000 100%);\n  behavior: url(PIE.htc);\n  -moz-box-shadow: 1px 1px 7px #676767;\n  -webkit-box-shadow: 1px 1px 7px #676767;\n  box-shadow: 1px 1px 7px #676767;\n  height: 35px;\n  float: right;\n  width: 30px;\n  overflow:hidden;\n  /*margin-top: 0px !important;*/\n  margin-right: 10px !important;\n  text-align: center;\n  background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0, #CC0000),color-stop(1, #CC0000));/* IE6,IE7 */\n  /* IE8 */\n  /* Firefox F3.5+ */\n  /* Safari3.0+, Chrome */\n  /* Opera 10.5, IE 9.0 */\n\n\n}\n\n.show img{margin-top:10px;}\n"
  },
  {
    "path": "routes/index.js",
    "content": "var express = require('express');\nvar router = express.Router();\nvar ioSvc = require('../io/ioHelper').ioSvc;\nvar msgType = require('../io/messageTpye').messageType;\n\n/* GET home page. */\nrouter.get('/', function(req, res, next) {\n  res.render('index', { title: 'Node-Msg-Sender' });\n});\n\nrouter.get('/sendMsg', function(req, res, next) {\n    var type = req.query.type || msgType.public;\n    var content = req.query.content || 'none';\n    var uid = req.query.uid;\n\n    switch (type){\n        case msgType.public:\n            ioSvc.serverBroadcastMsg(content);\n            break;\n        case msgType.private:\n            if(!uid){\n                return res.send({code:400,msg:'uid参数必传'});\n            }\n            ioSvc.serverToPrivateMsg(uid,content);\n            break;\n    }\n    return res.send({code:200,msg:'发送成功'});\n\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "routes/users.js",
    "content": "var express = require('express');\nvar router = express.Router();\n\n/* GET users listing. */\nrouter.get('/', function(req, res, next) {\n  res.send('respond with a resource');\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "utils/redis.js",
    "content": "var redisSvc = {};\nvar redis = require(\"redis\");\n\nif(!client){\n    var client = redis.createClient();\n}\n\nclient.on(\"error\", function (err) {\n    console.log(\"Redis Error :\" , err);\n    client = null;\n});\n\nclient.on('connect', function(){\n    console.log('Redis连接成功.');\n});\n\n/**\n * 添加string类型的数据\n * @param key 键\n * @params value 值\n * @params expire (过期时间,单位秒;可为空，为空表示不过期)\n * @param callBack(err,result)\n */\nredisSvc.set = function(key, value, expire, callback){\n\n    client.set(key, value, function(err, result){\n\n        if (err) {\n            console.log(err);\n            callback(err,null);\n            return;\n        }\n\n        if (!isNaN(expire) && expire > 0) {\n            client.expire(key, parseInt(expire));\n        }\n\n        callback(null,result)\n    })\n};\n\n/**\n * 查询string类型的数据\n * @param key 键\n * @param callBack(err,result)\n */\nredisSvc.get = function(key, callback){\n\n    client.get(key, function(err,result){\n\n        if (err) {\n            console.log(err);\n            callback(err,null);\n            return;\n        }\n\n        callback(null,result);\n    });\n};\n\n/*\n*删除String 类型的key\n * @param key 键\n * @param callBack(err,result)\n*/\nredisSvc.del = function(key, callback){\n\n    client.del(key, function(err,result){\n\n        if (err) {\n            console.log(err);\n            callback(err,null);\n            return;\n        }\n\n        callback(null,result);\n    });\n};\n\n\nmodule.exports = redisSvc;"
  },
  {
    "path": "views/error.ejs",
    "content": "<h1><%= message %></h1>\n<h2><%= error.status %></h2>\n<pre><%= error.stack %></pre>\n"
  },
  {
    "path": "views/index.ejs",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title><%= title %></title>\n    <link rel='stylesheet' href='/stylesheets/style.css' />\n    <meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n    <script src='//cdn.bootcss.com/jquery/1.11.3/jquery.js'></script>\n    <script src='/javascripts/notify.js'></script>\n  </head>\n  <body>\n    <div class=\"notification sticky hide\">\n        <p id=\"content\"> </p>\n        <a class=\"close\" href=\"javascript:\"> <img src=\"/images/icon-close.png\" /></a>\n    </div>\n    <div class=\"wrapper\">\n        <div style=\"width:850px;\">\n            <h3>介绍:</h3>\n            <b>这是一个消息推送系统，基于<a rel=\"nofollow\" href=\"https://socket.io/\">Socket.IO</a>开发。<br><br><br></b>\n            <h3>支持以下特性：</h3>\n            <ul>\n                <li>多浏览器支持</li>\n                <li>多类型客户端支持</li>\n                <li>支持针对单个用户推送消息</li>\n                <li>支持向所有用户推送消息</li>\n                <li>支持服务端主动推送消息</li>\n                <li>支持客户端之前推送消息</li>\n                <li>长连接推送（websocket或者comet），消息即时到达</li>\n                <li>支持在线用户数实时统计推送（见页脚统计）</li>\n            </ul>\n            <h3>测试:</h3>\n            当前用户uid：<b class=\"uid\"></b><br>\n            可以通过url：<a id=\"send_to_one\" href=\"http://localhost:3000/sendMsg/?type=public&uid=1445590039000&content=消息内容\" target=\"_blank\"><font style=\"color:#91BD09\">http://<font class=\"domain\"></font>:3000/sendMsg/?type=private&uid=<b class=\"uid\"></b>&content=消息内容</font></a>  向当前用户发送消息<br>\n            可以通过url：<a href=\"http://localhost:3000/sendMsg/?type=publish&to=&content=消息内容\" target=\"_blank\"  id=\"send_to_all\" ><font style=\"color:#91BD09\">http://<font class=\"domain\"></font>:3000/sendMsg/?type=public&content=消息内容</font></a> 向所有在线用户推送消息<br>\n            <script>\n                // 使用时替换成真实的uid，这里方便演示使用时间戳\n                var uid = Date.parse(new Date());\n                $('#send_to_one').attr('href', 'http://'+document.domain+':3000/sendMsg/?type=private&content=%E6%B6%88%E6%81%AF%E5%86%85%E5%AE%B9&uid='+uid);\n                $('.uid').html(uid);\n                $('#send_to_all').attr('href', 'http://'+document.domain+':3000/sendMsg/?type=public&content=%E6%B6%88%E6%81%AF%E5%86%85%E5%AE%B9');\n                $('.uid').html(uid);\n                $('.domain').html(document.domain);\n            </script>\n        </div>\n    </div>\n    <div id=\"footer\">\n        <center id=\"online_box\"></center>\n        <center><p style=\"font-size:11px;color:#555;\"> Powered by <a href=\"https://github.com/gytai/node-msg-sender.git\" target=\"_blank\"><strong>TaiGuangYin!</strong></a></p></center>\n    </div>\n  </body>\n  <script src=\"/socket.io/socket.io.js\"></script>\n\n  <script>\n      var socket = io.connect('http://'+document.domain+':3000');\n      socket.on('connect', function () {\n          console.log('连接成功...');\n          socket.emit('login', new Date().getTime());\n      });\n\n      // 后端推送来消息时\n      socket.on('message', function(msg){\n          $('#content').text('收到消息：'+msg);\n          $('.notification.sticky').notify();\n      });\n\n      // 后端推送来在线数据时\n      socket.on('update_online_count', function(data){\n          console.log(data);\n          $('#online_box').html('当前在线客户端数:&nbsp;'+data.online_count);\n      });\n\n  </script>\n</html>\n"
  }
]