功能分析
- 用户: 直播员(登录), 游客。
- 直播方式:直播员登录后台,输入相应的的直播信息(图片和文字)。
- 技术统计: 在线实时记录运动员数据。
- 游客套路: 游客实时进行在线讨论。
- 微博分享: 游客通过腾讯微博和新浪微博分享直播内容。
模块示意图
- 5个controller对应5个模块chat.js, live.js,login.js, score.js, share.js
- 5个controller继承父类action.js
- router.js负责路由处理
- app.js入口文件, 初始化路径以及项目所需模块
分析所用到的模块
Node.js模块名 文件名称 模块描述 http Node.js原生模块 负责服务器创建管理 fs Node.js原生模块 文件处理,读取 url Node.js原生模块 url请求路径处理 querystring Node.js原生模块 HTTP请求参数分析模块 httpParam http_param.js 负责HTTP参数GET和POST获取方式 staticModule static_module.js 负责本系统所有静态文件的管理 router router.js 本系统路由处理模块 action action.js 系统controller基类 jade NPM模块 前端模板 socket NPM模块 socket模块 path Node.js原生模块 路径处理 util Node.js原生模块 主要应用其继承API session node_session.js 他人开发的一个sessiong管理模块
目录结构
├── app // Node.js可运行代码
│ ├── controller // controller层
│ ├── core //无依赖的模块
├── conf // 配置文件信息
├── node_modules //依赖
├── static //静态文件资源
│ ├── css
│ ├── images
│ ├── js
├── view //jade模块文件
├── app.js //启动文件
主要模块介绍
app.js
- 文件路径统一化管理
- 文件路径名使用全大写字母进行变量命名
- 所有应用到的模块添加命名空间,统一管理
- 响应类方法或数据,同意添加到相应对象res中进行管理
/** * 设置路径全局变量 */ global.BASE_DIR = __dirname; global.APP = BASE_DIR + "/app/"; global.CON = APP + "/controller/"; global.CORE = APP + "/core/"; global.LIB = BASE_DIR + "/self_modules/"; global.CONF = BASE_DIR + "/conf/"; global.STATIC = BASE_DIR + "/static/"; global.VIEW = BASE_DIR + "/view/"; /** * modules 引入 */ global.lib = { http : require("http"), fs: require("fs"), url: require("url"), querystring: require("querystring"), httpParam: require(LIB + "http_param"), router: require(CORE + "router"), action: require(CORE + "action"), jade: require("jade"), socket: require("socket.io"), path: require("path"), parseCookie: require("connect").utils.parseCookie, session: require(LIN + "node_session"), util: require("util") } global.onlineList = []; //存储socket链接用户信息 global.app = lib.http.createServer(function(req, res){ //jade res.render = function(template, options){ var str = fs.readFileSync(template, 'utf8'); var fn = jade.compile(str, { filename: template, pretty: true }); var page = fn(options); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(page); } lib.router.router(req, res); //调用router的router方法执行url路由处理 }).listen(3000); console.log('Server running at http://127.0.0.1:3000/'); global.io = lib.socket.listen(app);
router.js
- 对url请求处理, 同时分发到相应的controller进行逻辑处理响应HTTP信息
var Login = require(Con + "login"); //登录模块 exports.router = function(req, res) { var pathname = decodeURI(lib.url.parse(req.url).pathname); //解析编码后的url lib.pathname.init(req, res); //初始化HTTP的GET和POST获取参数 global.sessionLib = lib.session.start(res, req); //session start var model = pathname.substr(1), //获取请求的controoler controller = lib.httpParam.GET("c"), //获取请求的controoler中的函数方法 Class = ""; if(pathname == '/favicon.ico') { return; } else if(pathname == '/') { res.render (VIEW + 'index.jade'); return; } try { Class = require(CON + model); //require一个请求的类 } catch (e) { //失败时,默认为请求静态文件 lib.staticModule.getStaticFile(pathname, req, res, BASE_DIR); return; } if(Class) { var login = new Login(req, res); var ret = login.checkSssion(model); //判断用户是否已登录 if(ret) { var object = new Class(req, res); //创建请求controller类的对象object object[controller].call(); //调用object对象的controller方法 } else { res.render(VIEW + "index.jade"); //未登录跳转到index.jade } } else { res.writeHead(404, {"Content-Type": "plain/text"}); res.end("can not find source"); } }
Login类
- 分管理员登录和游客登录,跳转live.jade 和main.jade
- 登录作session处理
- socket连接, 文字直播
module.exports = function() { var _req = arguments[0]; var _res = arguments[1]; /** * 判断用户是否已登录 * @param {string} model 访问模块 'login'时不需要去验证session * @returns bool true登录|false未登录 */ this.checkSession = function(model) { if(model == 'login') { return true; }else if (sessionLib.username && sessionLib.username != '') { return true; } return false; } this.login = function() { //调用HTTP POS参数获取方法 lib.httpParam.POST('username', function(value) { sessionLib.username = value; //设定Session中username值 if(value == 'zula') { //管理员登录直接进入直播模块 _res.render(VIEW + 'live.jade', {'user': value}); } else { //进入观看直播模块 _res.render(VIEW + 'main.jade', {'user': value}); } //socket监听 var time = 0; io.sockets.on("connection", function(socket) { var username = sessionLib.username; if(!onlineList[username]) { onlineList[username] = socket; } //设置刷新在线用户数函数 var refresh_online = function() { var n = []; for (var i in onlineList) { n.push(i); } var message = lib.fs.readFileSync(BASE_DIR + '/live_data.txt', 'utf8'); socket.emit('live_data', message); //如果有用户登录或退出, 广播在线用户列表。 io.sockets.emit("online_list", n); //所有人广播 } refresh_online(); //确保每次发送一个socket消息 if(time > 0) { return; } socket.on("public", function(data) { var insertMsg = "<li><span class='icon-user'></span>"; insertMsg += "<span class='live_user_name text-success'>[zula]</span>"; insertMsg += "<span class='live_message text-info'>" + data.msg + "</span></li>"; writeFile( {'msg': insertMsg, 'data': data}, function(data) { io.sockets.emit('msg', data); } ) }); socket.on("disconnect", function() { delete onlineList[username]; refresh_online(); }); time ++; }) }); } } function writeFile(data, callback) { var message = lib.fs.readFileSync(BASE_DIR + '/live_data.txt', 'utf8'); lib.fs.writeFile(BASE_DIR + '/live_data.txt', message + data.msg, function(err) { if (err) throw err; callback(data.data); }) }
未完待续。。。略粗糙, 继续加功能