Node.js 基于socket.io聊天室的BUG解决经过

Node.js 基于socket.io聊天室及BUG解决经过

          这两天的的·node.js学习中,我在简单的的socket.io聊天室的构建过程中,遇上了N多BUG。经过2天的奋战,终于解决了问题。

          socket聊天室的代码

          首先是服务器talkserver.js的构建,其中引用了《Node.js开发实战讲解》的httpParam模块、staticModule模块(稍作修改,加入了回调函数返回模块内部的值)。
          此外,我在不使用staticModule的模块下采用了mime模块以代替项目根目录conf文件夹中的mmie_type.json文件。这段被我在  talkserver.js   的路由选择switch上注释了。
        
         
  talkserver.js        
var app = require('http').createServer(handler),
    io = require('socket.io').listen(app),
    fs = require('fs'),
    url=require('url'),
   mime = require("mime"),
    path  = require('path'),
httpParam=require('./http_param'),
    staticModule = require('./static_module'),
    Filepath=__dirname+'/text'+'/t.txt',
    STATIC=__dirname+'/static';
app.listen(8012); 
  
function handler (req, res) {
    var pathname=decodeURI(url.parse(req.url).pathname);
    /*初始化httpParam模块*/
    httpParam.init(req,res);
    if(pathname == '/favicon.ico'){
        return;
    }
    switch(pathname){
        case '/'       :  defaultIndex(res);
            break;
        case '/index'  :  defaultIndex(res);
            break;
        default        :  
        {
           /* Response(res,pathname);*/
            staticModule.getStaticFile(pathname.slice(1),res,req,function(e){
                console.log(e);
            });

            break;
            
        }
    }
}
io.sockets.on('connection', function (socket) {
  socket.emit('success', { 'hello': 'world' });
  socket.on('my other event', function (data) {
      socket.emit('news', { 'srvdata': data });
      console.log(data);
      fs.writeFile(Filepath, data, function(err){
          if (err) throw err;
      });
      socket.broadcast.emit('news', { 'srvdata': data });
  });
});
function defaultIndex(res){
    fs.readFile(__dirname + '/talk.html', function (err, data) {
        if (err) {
            res.writeHead(500);
            return res.end('Error loading index.html');
        }
        res.writeHead(200);
        res.end(data);
    });
}
//函数Response,将HTML、css、js等文件响应给客户端
function Response(res,filePath) {
    //读取文件,读取完成后给客户端响应
    console.log(filePath);
    fs.readFile(__dirname+filePath, function (err, data) {
        if (err) {
            res.writeHead(200,{'Content-type':'text/plain'});
            res.end('404');
        } else {
            /**
             * 第二种方法:通过使用mime模块设定mime类型
             */
            res.writeHead(200, {                     //响应客户端,将文件内容发回去
                'Content-type': mime.lookup(filePath)
            });    //通过后缀名指定mime类型
            res.end(data);

        }
    });
}
       接下来是staticModule模块。
       
/**
 *
 * 定义全局常用变量
 */
var BASE_DIR = __dirname,
	CONF     = BASE_DIR + '/conf/',
    STATIC   = BASE_DIR+'/static/',
	CACHE_TIME = 60*60*24*365,
	mmieConf;
var output;
/**
 *
 * require本模块需要的Node.js模块
 */
var sys = require('util'),
	http = require('http'), 
	fs    = require('fs'),
	url   = require('url'),
	path  = require('path');
	mmieConf = getMmieConf();

/**
 *
 * 响应静态资源请求
 * @param string pathname
 * @param object res
 * @return null
 */
exports.getStaticFile = function(pathname, res, req,cb){//当前传入的pathname是style.css
	var extname = path.extname(pathname);
	extname  = extname  ? extname.slice(1) : '';
	var realPath = STATIC + pathname;
	var mmieType = mmieConf[extname] ? mmieConf[extname] : 'text/plain';
	fs.exists(realPath, function (exists) {
        if (!exists) {
            res.writeHead(404, {'Content-Type': 'text/plain'});
            res.write("This request URL " + pathname + " was not found on this server.");
            res.end();
        } else {
			var fileInfo = fs.statSync(realPath);
			var lastModified = fileInfo.mtime.toUTCString();
			/* 设置缓存 */
			if ( mmieConf[extname]) {
				var date = new Date();
				date.setTime(date.getTime() + CACHE_TIME * 1000);
				res.setHeader("Expires", date.toUTCString());
				res.setHeader("Cache-Control", "max-age=" + CACHE_TIME);
			}
			if (req.headers['if-modified-since'] && lastModified == req.headers['if-modified-since']) {
                    res.writeHead(304, "Not Modified");
                    res.end();
            } else {
					fs.readFile(realPath,function(err, file) {
						if (err) {
							res.writeHead(500, {'Content-Type': 'text/plain'});
							res.end(err);
						} else {
						res.setHeader("Last-Modified", lastModified);
						res.writeHead(200, {'Content-Type': mmieType});
                            cb(mmieType);
						res.end(file);
					}
             });
          }
		}
      });
}
//获取MMIE配置信息,读取配置文件
function getMmieConf(){
    var routerMsg = {};
    try{
        var str = fs.readFileSync(CONF + 'mmie_type.json','utf8');
        routerMsg = JSON.parse(str);
    }catch(e){
        sys.debug("JSON parse fails")
    }
    return routerMsg;
}
exports.output;
       然后是httpParam
var _res,_req,
	url = require('url'),
	querystring = require('querystring');
/**
 * 初始化res和req参量
 */
exports.init = function(req, res){
	_res = res;
	_req = req;
}

/**
 * 获取GET参数方法
 */
exports.GET = function(key){
	var paramStr = url.parse(_req.url).query,
		param = querystring.parse(paramStr);
	return param[key] ? param[key]  : '';
}

/**
 * 获取POST参数方法
 */
exports.POST = function(key, callback){
	var postData = '';
	_req.addListener('data', function(postDataChunk) {
        postData += postDataChunk;
    });
    _req.addListener('end', function() {
        // 数据接收完毕,执行回调函数
        var param = querystring.parse(postData);
		console.log(param);
		var value = param[key] ? param[key]  : '';
		callback(value);
    });
}
       最后,是聊天室的代码。
      
<html>
<head>
   <!-- <link rel="stylesheet" href="./static/style.css" type="text/css"/>-->
    <link rel="stylesheet" href="./style.css" type="text/css"/>
    <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io.connect('http://localhost:8012');
        socket.on('success', function (data) {
            $('#messages').append($('<li></li>').text(data.hello));
        });
        socket.on('news', function (data) {
            $('#messages').append($('<li></li>').text(data.srvdata));
        });
        function fn()
        {
            socket.emit('my other event', $('#chatmsg').val());
        };
    </script>
</head>
<body>
 <ul id="messages"></ul>
 <hr>
 <input type="text" id="chatmsg">
 <input type="button" value="发送" id="send" οnclick="fn()"/>
</body>
</html>
这是CSS
#messages {
    padding: 0px;
    list-style-type: none;
}

#messages li {
    padding: 2px 0px;
    border-bottom: 1px solid #ccc;
    color: red
    list-style-type:none
}
我的项目截图


            其中t.txt是一个空的文件,以供服务器写入数据。
            接下来是遇上一个毁灭性的BUG QAQ

            主要BUG及解决方案

1.Node.js中CSS文件静态文件引用路径问题

var pathname=decodeURI(url.parse(req.url).pathname);

       这段代码中的pathname是浏览器获取到的url对象的pathname属性。在浏览器向服务器请求的得到的是html文件中
  <link rel="stylesheet" href="./style.css" type="text/css"/>
      的
/style.css
      而
var extname = path.extname(pathname);
      中的patnname的输入格式是style.css。
      因此,这导致了conten-type格式的错误。

 2. Can't render headers after they are sent to the client

     问题在这里
switch(pathname){
        case '/'       :  defaultIndex(res);
            break;
        case '/index'  :  defaultIndex(res);
            break;
        default        :  
        {
           /* Response(res,pathname);*/
            staticModule.getStaticFile(pathname.slice(1),res,req,function(e){
                console.log(e);
            });

            break;
            
        }
    }
  fs.readFile(__dirname + '/talk.html', function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }
    res.writeHead(200);
    res.end(data);
  });
}

在路由选择之后,由于之前遗留的方法尚未清楚,导致header连续发送显示标题问题。这个问题导致了style.css返回值被覆盖成网页,浏览器console 显示:       Resource interpreted as stylesheet but transferred with MIME type text/plain

所以,在css文件的res响应为html页面时,很可能是我们后续的res返回的页面覆盖了css文件,外加上

浏览器的缓存,导致第一css有效,刷新后失效的问题。

由于时间关系,这篇博文总计写的有点草率,希望大家见谅,在coding中减少不必要的弯路!

         


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值