nodejs 基于socket.io实现聊天室

页面导航:首页 > 网络编程 > JavaScript > nodejs 基于socket.io实现聊天室

nodejs 基于socket.io实现聊天室

来源: 作者: 时间:2016-01-19 18:32 【

由于之后要做的网页视频直播项目要用到socket io模块,所以特地花时间研究了下,参照网上的代码做了些改进,自己写了个聊天室代码。不得不承认后端事实推送能力有点厉害,这是以

由于之后要做的网页视频直播项目要用到socket.io模块,所以特地花时间研究了下,参照网上的代码做了些改进,自己写了个聊天室代码。不得不承认后端事实推送能力有点厉害,这是以前我用php一直苦恼的事情。下面简单介绍下我的项目,顺带讲解下nodejs。

事实上,在看别人写的代码之前,我一直不知道nodejs是干嘛的,直到真正接触到才明白这也可以算作是服务端代码,丰富的第三方库使其功能极其强大。它可以像golang的beego一样直接通过命令行开启服务器,不过要用到express模块。加载模块的方式类似php,如下:

var express = require('express'); //引用express
要想开启一个服务器只需以下几行代码即可:

 

 

var express = require('express'); //引用express
var app = express();
var server = require('http').Server(app);
//监听服务器启动
server.listen(3000, function() {
	console.log(Express server listening on port  + app.get('port'));
});
在服务器开启成功后用console.log在命令行打印消息,及其方便。

 

接下来就该定义路由了,事实上nodejs有路由模块,然而我还没研究过那模块具体实现了哪些功能,只借用了别人的代码来写基本的路由,大致形式如下:

 

app.set('views', __dirname + '/views');
app.get('/', function(req, res) {
	//res.send('hello world');
	res.sendFile(app.get(views) + '/login.html');
	//res.redirect('/login');
});

req即request请求,res即response返回,这与javaweb有些类似。res.send大概是直接输出字符串内容到页面上,而sendFile则是将指定文件的内容输出到页面,redirect即重定向。倘若我在3000端口开启服务器,那么以上代码则规定当我通过get方法访问localhost:3000/时,将views/login.html的内容输出到页面上,其实也就是访问了views/login.html这个页面。app.get中的get即get方法。同样,若要写post请求的接口,则用app.post即可。注:这个app对象即文章开始定义的express对象。

 

介绍完路由,再来介绍模板。既然nodejs能作为服务端语言来开发,那么自然少不了模板模块。当然,nodejs有很多模板模块,这边我只了解了ejs模块,就先只介绍这。

调用方式如下:

 

app.set(view engine, ejs);
//聊天室首页
app.get('/index', function(req, res) {
	res.render(index, {
		user: req.session.user
	});
});
调用res.render方法来传值到页面,第一个参数即模板页面的名称即views/index.ejs(注意后缀是ejs),第二个参数是附带的数据,既然是js,那附带的数据自然是js下的json数据。注:在nodejs中views和view似乎都有规定,定义页面路径时app.set('views', __dirname + '/views');程序运行成功,而app.set('view', __dirname + '/views');则会报错,错误原因忘了,反正我查看了英文下的答复才知道是这个问题,定义页面路径时最好还是用views吧。

 

在模板页面调用也很方便,直接<%=user%>即可。如下:

 


 id=user />req.session.user是我使用了session模块。因为我的聊天室做了登陆页的,顺便用了下session模块熟悉熟悉。调用方式如下:

 

 

var session = require('express-session'); //如果要使用session,需要单独包含这个模块
app.use(session({
	secret: 'ScumVirus',
	name: 'sv_chat', //这里的name值得是cookie的name,默认cookie的name是:connect.sid
	cookie: {
		maxAge: 3600000
	}, //设置maxAge是3600000ms,即1h后session和相应的cookie失效过期
	resave: false,
	saveUninitialized: true,
}));
//设置session
req.session.user = ScumVirus;
//获取session
var name = req.session.user;
nodejs还提供了加密模块,可以很方便的加密字符串,我一般都用32位md5加密,只介绍获取32位md5加密的方法,如下:

 

 

var crypto = require('crypto'); //加密
//获取md5加密后的值
var getMD5 = function(str) {
	var md5 = crypto.createHash('md5');
	md5.update(str);
	var d = md5.digest('hex');
	return d;
}

另外我还用到了mysql模块,用于连接mysql,实现前台注册登陆,调用如下:

 

 

var  = require(mysql); //数据库模块
//连接数据库
var connPool = mysql.createPool({
	host: '127.0.0.1', //主机
	user: 'root', //MySQL认证用户名
	password: 'admin', //MySQL认证用户密码
	port: '3306', //端口号
	database: 'sv_chat', //数据库
	waitForConnections: true, //当连接池没有连接或超出最大限制时,设置为true且会把连接放入队列
	//connectionLimit:10,//连接数限制
});
//根据用户获取用户信息
var getUserById = function(name, callback) {
	//执行SQL语句
	var sql = 'select * from sv_user where name=?';
	var params = [name];
	connPool.query(sql, params, function(err, result) {
		if (err) {
			console.log('[SELECT ERROR] - ', err.message);
			return;
		}
		return callback(result[0]);
	});
}
//添加新用户
var addUser = function(params, callback) {
	//执行SQL语句
	var sql = 'insert into sv_user(`name`,`pwd`,`email`,`phone`,`create_time`) values(?,?,?,?,?)';
	connPool.query(sql, params, function(err, result) {
		if (err) {
			console.log('[INSERT ERROR] - ', err.message);
			return callback(false);
		} else {
			console.log('INSERT ID:', result.insertId);
			return callback(true);
		}
	});
}
这边有兴趣的话可以自己打印下查询的结果result,是json格式的数据,有insetId,也有affectedRows,同php几乎一样。不过唯一坑爹的一点是,查询数据库是异步执行的,若要将查询结果返回给前台页面,可能值一直获取不到,这里必须善用回调方法。网上也有介绍说可以线性执行,但是我还没仔细研究过,回调我理解的快,便先用回调函数处理了。

 

最后是最关键的通信阶段,socket.io模块,后台代码如下:

 

var io = require('socket.io').listen(server); //socket io模块
//WebSocket连接监听
io.on('connection', function(socket) {
	//socket.emit('open',onlineMember); //通知客户端已连接

	// 打印握手信息
	// console.log(socket.handshake);

	// 构造客户端对象
	var client = {
		name: '',
	}

	// 对message事件的监听
	//登录事件
	socket.on('login', function(name) {
		var time = getTime();
		client.name = name;
		var index = getArrIndex(name,onlineMember);
		if(index == -1){
			onlineMember.push(client.name);
			console.log(time +   + client.name +  login);
		}
		var obj = {
			time: time,
			author: client.name,
			text: '',
			type: 'login',
			member: onlineMember
		};
		socket.emit('system', obj);
		socket.broadcast.emit('system', obj);
	});
	//消息事件
	socket.on('message', function(msg) {
		var obj = {
			time: getTime(),
		};
		obj['msg'] = msg;
		obj['author'] = client.name;
		obj['type'] = 'message';

		// 返回消息(可以省略)
		socket.emit('message', obj);
		// 广播向其他用户发消息
		socket.broadcast.emit('message', obj);
	});

	//监听退出事件
	socket.on('disconnect', function() {
		var index = getArrIndex(client.name, onlineMember);
		if (index > -1) {
			onlineMember.splice(index, 1);
		}
		var time = getTime();
		var obj = {
			time: time,
			author: client.name,
			text: '',
			type: 'loginout',
			member: onlineMember
		};
		console.log(time +   + client.name +  loginout);

		// 广播用户已退出
		socket.broadcast.emit('system', obj);
	});
});
前台连接代码:

 

 

<script src=/socket.io/socket.io.js></script>
<script>
	//建立websocket连接
	socket = io.connect('http://localhost:3000');
	var userName = $(#user).val();
	socket.emit('login', userName);
</script>
socket.emit可以理解成双向通信的方法,若客户端A与服务端B连接上了,那么A调用emit方法,则B收到消息,B调用emit方法,则A收到消息。而socket.broadcast.emit方法则是广播,若客户端A,B,C同时连接上服务端D,A通过emit发送消息给D,D接收消息后调用广播方法,则B,C收到消息,而A则收不到消息。login是我自定义的推送消息类型,message是插件本身就定义了的类型,前台可通过socket.send(msg)触发,,disconnect也是插件定义的类型,在客户端断开连接时调用。

 

Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<