美文网首页
少年,进来学习开发socket聊天室吧【下】

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

作者: 壮哉我大前端 | 来源:发表于2017-04-08 02:30 被阅读0次

    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写个视频聊天室吗?能!

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

    相关文章

      网友评论

          本文标题:少年,进来学习开发socket聊天室吧【下】

          本文链接:https://www.haomeiwen.com/subject/uvhkattx.html