1、回顾上一集内容
- 在本地安装和配置node环境
- 用node创建http服务器,配置路由,安装所需的模块
- 前后端都引入socket.io
- 根据socket.io提供的api进行事件的传递
上一集内容已经实现了用户加入到一个公共的空间,并且把对应的socketID给打印到前端页面上。
这一集内容将会带大家一起实现可以实时发送文字流的socket聊天室,为了给后续的课程做铺垫,这里会先讲一下用户登录功能的实现。
2、用户登录
数据库方面笔者这里用的mongodb,所以下面的介绍也是围绕mongodb来介绍的
1)安装mongodb以及robomongo
下载地址
mongodb:https://www.mongodb.com/download-center
robomongodb(mongodb可视化工具):https://robomongo.org/
配置(以win7为例)
- 配置全局环境
笔者的mongo安装路径:D:/mongoDB/
将D:/mongoDB/bin添加到全局环境,按下图从左到右红框点击顺序,将路径添加到path变量值后。
- 读取配置并自动启动
新建文件夹
D:/mongodbData/ --- 存放log和db,日志数据和数据库数据
D:/mongodbData/log --- 存放日志数据
D:/mongodbData/db --- 存放数据库数据
D:/mongodbData/mongodb.cfg --- 存放数据库启动的配置
更改配置(写入mongodb.cfg)
dbpath=D:/mongodbData/db
logpath=D:/mongodbData/log/mongodb.log
auth=true #开启认证
安装mongodb为window Service服务(安装完在services.msc可以找到mongodb的服务)
$ mongod --config "D:/mongodbData/mongodb.cfg" --install --serverName mongodb
尝试
$ net start mongodb #启动
$ net stop mongodb #关闭
- 用户认证
由于上面cfg文件中auth=true,开启了用户认证,所以连接数据库后必须通过认证才能执行敏感操作。未登录时进行敏感操作会出现下图情况。所以我们需要先创建顶级用户
依次输入下面的命令
$ mongo #连接mongo服务器
$ use admin #使用admin数据库
$ db.createUser({ #创建用户root,拥有root权限
user:"root",
password:"123456",
roles:[{role:"root",db:"admin"}]
})
$ db.auth("root","123456") #通过认证
$ use demo #新建mydb数据库
$ db.createUser({ #创建普通用户ze,拥有读写权限
user:"ze",
pwd:"123456",
roles:[{role:"readWrite",db:"demo"}]
})
下一次使用命令行登录时
$ mongo -uze -p123456 demo
使用robomongo进行登录时
Paste_Image.png2)node安装mongodb依赖
$ npm i mongoose --save-dev #一个mongodb的框架
在原先项目目录新建mongo.js,写入
var mongo = require('mongoose');
mongo.connect('mongodb://ze:123456@localhost:27017/demo');
var db = mongo.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.on('connected', function (callback) {
console.log("db has already opened");
});
db.on('disconnected', function (callback) {
console.log("db has already exited");
});
var Schema = mongo.Schema; // 配置每一条记录的模型
var UserSchema = new Schema({
account : { type: String }, //账号
password: {type: String} // 密码
});
module.exports = mongo.model('userlist',UserSchema);
在socket.js中引用
var mongo = require('./mongo');
命令行启动socket.js,出现下图则已经成功连接数据库
Paste_Image.png3)以登录功能为例做分析
- 前端(登录会存用户名到cookie)
var data = {
username: $( "input#username" ).val(),
password: $( "input#password" ).val()
};
$.post( "/login", data, function( data, status ) {
switch( data.result ) {
case 1: // 与后端协定好,1代表登录成功
// 将用户名存入cookie
// 连接到socket服务器,同时发送login事件
var socket = io.connect();
// Your_username是从cookie获取
socket.emit( "login", Your_username );
default:
// 打印无法成功登录的信息;
alert( data.msg );
}
});
// 捕获login事件后,执行回调
socket.on("login",function( names ){
var str = "";
names.forEach( function( item, index) {
str += item + "<br/>";
})
$( "#curUser" ).html( str );
})
- 后端(路由设置和socket事件响应)
路由设置(登录)
server.post('/login', function(req, res) {
var data = []; // 存放post数据
req.on("data", function(chunk) {
data.push(chunk); // 前端post方式发过来的数据都是转成buffer格式再发给后端的
});
req.on("end", function() {
// console.log( data ); --- 可以看到buffer类型数据
// 获取post请求信息
var postData = Buffer.concat(data).toString();
// console.log( postData ); --- 这里通过格式转换成了url参数格式
var loginInfo = querystring.parse(postData);
// console.log( loginInfo ); --- 这里就是将url参数转换成json格式
var usr = {
account: loginInfo.account,
password: loginInfo.password
};
// mongoose的查找方法,result返回一个数组,包含查找的信息
mongo.find(usr, function(err, result) {
var response;
if (err) {
console.log(err);
} else {
if (result.length) {
response = {
result: 1,
msg: "登录成功",
account: loginInfo.account
};
} else {
response = {
result: -1,
msg: "用户或者密码不正确"
};
}
res.end(JSON.stringify(response));
};
});
});
});
socket事件响应
var names = [];
io.on('connection', function(socket) {
socket.on('login',function(name){
names.push( name );
io.sockets.emit('login',names);
})
});
3、文字聊天功能实现
- 这个功能实现的关键在于后端,所有的信息都是通过后端进行接收和转发的,举个例子,clientA想向clientB发一条信息,那么需要告诉后端,clientB的地址以及发送的信息。关于clientB的地址,我们可以在每个客户端登录到socket服务器的时候就将客户端的socketName和socket地址存起来,那么只需要知道socketName就可以遍历出socket地址,就可以进行点对点信息交换了。
- 接下来演示一下前后端的关键代码,这里演示的是信息群发,不针对某个客户端,只要登录到socket服务器的客户端都能接收到
前端输入信息,点击发送:
// 定义信息呈现出来的模板
var msgTpl = "<li><span class='user'>{username}</span><span class='msg'>{msg}</span></li>";
// 点击发送
$( ".btn-send" ).on( "click", function( e ) {
// 信息输入框
var msg = $( "input.message" );
// username可以从cookie获取,也可以存入一个变量
socket.emit( "message", username, msg.val() );
updateMsg( username, msg.val() );
// 发送完将输入框的值清空
msg.val( "" );
});
function updateMsg( username, msg ) {
var msgContainer = $( ".msgContainer" );
var msg = {
username: username,
msg: msg
};
// 最近看到的用正则全局替换模板,比之前的html拼接帅多了
for( var key in msg ) {
var reg = new RegExp( "\\{" + key + "\\}", "g" );
msgTpl = msgTpl.replace( reg, msg.key );
}
msgContainer.append( msgTpl );
}
// 当收到别的客户端发的信息时执行的回调
socket.on( "message", function( username, msg ) {
updateMsg( username, msg );
})
后端进行消息转发:
socket.on('message', function(username, msg) {
// socket.broadcast --- 连接同一个socket服务器,并且同一个namespace,除了自己以外的其他用户
socket.broadcast.emit('message', username, msg );
});
注:对于namespace和room有疑问的参考下方链接
http://blog.csdn.net/lijiecong/article/details/50781417
4、小结和预告
- 用户登录,前后端和数据库如何交接
- 消息转发,其实就是socket事件驱动,类似的实现方法还有h5的新接口eventSource(SSE),一样可以实现事件驱动来推送信息,但是一般不做聊天用。
- 预告下一期介绍基于webRTC的视频聊天室。大家都用flash做视频聊天,可是我又不会actionScript,能用JS写个视频聊天室吗?能!
————
前端·小泽
给点信心自己,就一定能做到
网友评论