少年,进来学习开发socket聊天室吧【下】

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变量值后。
Paste_Image.png
  • 读取配置并自动启动
    新建文件夹
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,开启了用户认证,所以连接数据库后必须通过认证才能执行敏感操作。未登录时进行敏感操作会出现下图情况。所以我们需要先创建顶级用户
Paste_Image.png

依次输入下面的命令

$ 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.png

2)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.png

3)以登录功能为例做分析

  • 前端(登录会存用户名到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写个视频聊天室吗?能!

————
前端·小泽
给点信心自己,就一定能做到

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容