美文网首页前端社团
使用Socket.IO仿微信聊天应用

使用Socket.IO仿微信聊天应用

作者: Zoemings | 来源:发表于2016-10-20 00:39 被阅读1604次

    页面布局的构建

    根据网页版的微信,利用flex框架进行重建。手机版根据实际情况将聊天页面隐藏,点击相应用户的聊天室显示相应的聊天页面。

    socket.io

    基本原理

    • 是一个开源的WebSocket库,它通过Node.js实现WebSocket服务端,同时也提供客户端JS库。
    • 原理是基于事件的推送实现双向通讯。对于数据的处理就是一个个的事件触发。

    基本语法

    // send to current request socket client
    socket.emit('message', "this is a test");
    // sending to all clients except sender
    socket.broadcast.emit('message', "this is a test");
    // sending to all clients, include sender
    io.sockets.emit('hi', 'everyone');
    // sending to individual socketid
    io.sockets.socket(socketId).emit('message', 'for your eyes only');
    

    服务端代码实现

    var server = http.createServer(app);
    //// 在线用户名单
    var onlineList = [];
    // 创建socket服务
    var io = require('socket.io').listen(server);
    io.on('connection', function(socket) {
      var usr;
      //加入聊天室
      socket.on('online', function(uid) {
        //用户加入
        usr = uid;
        socket.join(usr);
        //把当前用户加入在线用户名单中
        if ( onlineList.indexOf(uid) === -1) {
          onlineList.push(uid);
        }
        console.log(usr + ' online...');
      });
    
      // 处理用户消息
      socket.on('message', function (uid,fid,msg) {
        var type;
        // 判断接收方是否在线
        if (onlineList.indexOf(fid) === -1) {
          //不在线将消息保存到数据库
          type = config.site.OFFLINE;//OFFLINE是1
          io.to(uid).emit('msg', uid, fid, msg);//user
        }else{
        //接收方在线,将消息发送给对方
          type = config.site.ONLINE;
          io.to(fid).emit('msg', uid, fid, msg);//(_id,fid,msg);
          io.to(uid).emit('msg', uid, fid, msg);
        }
    
        var data = {
          "uid":uid,
          "from":uid,
          "to":fid,
          "type":type,
          "msg":msg
        };
        console.log(data);
        //数据库存入
        dbHelper.addMsg(data, function (success, data) {
          console.log(uid + ' -> ' + fid + 'msg saved...');
        })
      });
    
       //非正常下线
         socket.on('disconnect', function() {
           // 从在线名单中移除
           var index = onlineList.indexOf(usr);
           if (index !== -1) {
             onlineList.splice(index,1);
             socket.leave(usr);
           }
           console.log(usr + ' offline...');
         });
    
      //正常下线
      socket.on('leave', function() {
        socket.emit('disconnect');
      });
    
    });
    

    客户端代码

    <section class="chat">
        <nav>
            //显示当前用户的基本消息
            <div class="header">
                <img class="avatar" src="{{user.userThumb}}">
                <h3 class="nickname">
                    {{user.username}}
                </h3>
            </div>
            //搜索用户
            <div class="search_bar">
                <i class="wechat_search"></i>
                <input class="frm_search" type="text"  placeholder="搜索">
            </div>
            //功能选择
            <ul id="myTab" class="nav nav-tabs tab">
                <li class="active">
                    <a class="" title="好友列表" href="#chat" data-toggle="tab">
                        <i class="web_wechat_chat"></i>
                    </a>
    
                </li>
                <li>
                    <a class="" title="传输文件" href="#read" data-toggle="tab">
                        <i class="web_wechat_public" ></i>
                    </a>
    
                </li>
                <li>
                    <a class=""  title="用户列表" href="#user" data-toggle="tab">
                        <i class="web_wechat_friends" ></i>
                    </a>
    
                </li>
            </ul>
            //好友列表
            <div id="" class="chats tab-content">
                <div class="tab-pane fade in active" id="chat">
                    <div class="wrapper-content">
                        <section class="list">
                            </section>
                        </div>
                </div>
                <div class="tab-pane fade" id="read">
                </div>
            //获取数据库用户列表
            <div class="tab-pane fade" id="user" data-button="addFrd">
                 {{#each entries}}
                     <p data-toggle="select" data-id="{{_id}}" class="active chat_item">
                        <img class="avatar" src="{{userThumb}}">
                           <div class="info">
                            <span class="nickname_text">{{username}}</span>
                            </div>
                            <div class="ext">
                                <a href='#' data-id="{{_id}}" class='addFriendBtn'>添加</a>
                            </div>
    
                    {{/each}}
                   </div>
                </div>
    </nav>
        //聊天区域
        <div id="chatArea" data-id="">
            <div class="title_wrap">
                <a class="friend" data-id=""></a>
                <div class="message-content">
                    <div  class="box-content">
                        hello
                    </div>
                </div>
                <form action="">
    
                    <div class="box_ft box-send">
                        <div class="toolbar">
                            <a class="web_wechat_face"  title="表情"></a>
                            <a  class="web_wechat_screencut" href="" title="截屏"></a>
                            <a  class="web_wechat_pic" title="图片和文件"></a>
                        </div>
                        <div class="content">
                            <input class="chat-input input-ctn" id="editArea">
    
                        </div>
                        <div class="action">
                            <span class="desc ng-scope">按下enter发送</span>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </section>
    <script src="/chat/chat.js"></script>
    <script src="/chat/common.js"></script>
    <script src="/lib/socket.io/socket.io.js"></script>
    

    上面的html内容本身没有什么好说的,我们主要看看里面的4个文件请求
    1./lib/socket.io/socket.io.js
    2.common.js
    3.chat.js
    第1个JS是Socket.IO提供的客户端JS文件,当npm安装完socket.io并搭建起WebServer后,这个JS文件就可以正常访问了
    第2个common.js主要提供一些格式处理,路径获取,消息提示这里就不再赘述
    第3个chat.js是完整的客户端的业务逻辑实现代码,它的内容如下:

    $(init);
    
    function init() {
      initSocket();
      initFriendList();
    
      $('body').on('click', '.addFriendBtn' , doAddFreind);
      $('body').on('click', '.wrapper-content .list li' , doChatSession);
      $('body').on('keydown', '#editArea' , doSend);
    }
    //初始化socketio
    function initSocket() {
      socket = io();
      socket.emit('online', _id);
    
      // 监听消息
      socket.on('msg', function(uid, fid, msg) {
        var fromID = (_id == fid)?uid:fid;
        var message;
        //当前用户如果是好友的话显示在发送方
        if (_id == fid) {
          console.log(uid+"发送信息给"+fid );
          friendThumb = $('#'+uid).children('img').attr('src');
          //前端显示接收信息
          var html = '<li class="message you"><img class="avatar" src="friendThumb"><p class="you">' +msg +'</p></li>';
            $('#messages').append(html);
        } else {
            //前端显示发送消息
           var html = '<li class="message me"><img class="avatar" src="$.cookie('userThumb')><p class="you">' +msg +'</p></li>';
            $('#messages').append(html);
          console.log("from");
        }
      });
    }
    
    //计算sessionid将较大id放置前面小的加后面,这样两个人聊天的sessionid就是唯一的
    function calcuSessionId(uid,fid) {
      return (uid>fid)?(uid+fid):(fid+uid);
    }
    //新建会话过程
    function doChatSession() {
      fid = $(this).attr("id");
      //得到sessionid
      var sessionId = calcuSessionId(_id,fid);
      console.log(sessionId);
      //第一次点击的话,发送初始化session消息
      if (chatSession.indexOf(sessionId) === -1) {
        chatSession.push(sessionId);
        socket.emit('join', sessionId);
      }
      //切换界面
      toggleChatView(fid);
    }
    
    //切换聊天窗口
    function toggleChatView(fid) {
      if ($("#message"+fid).length == 0) {
        $(".title_wrap .message-content").prepend('<div class="box-content" id="message'+fid+'"></div>');
      }
      $(".box-content").hide();
      $("#message"+fid).show();
    }
    
    //发送消息给朋友
    function doSend(e) {
      if (e.which  === 13) {//获取enter键
        e.preventDefault();
        var msg = $(this).val();
        console.log(msg);
        $(this).val('');
        // 发送消息
        socket.send(_id,fid,msg);
      }
    }
    

    最近入的坑

    • format的constructor说undefined,通常是自己传进的参数与定义的的不一致,该函数本身是没错的
    • 加入session支持,认证机制必须依赖cookie,所以还应该同时安装一个cookie-parser,然后再app.js中导入这两个中间件,
      定义cookie解析器,注意,该定义必须写在路由分配之前.
    • 用户头像的显示
      在route文件夹下添加user: req.session.user,这样相应页面就可以调用user内的数据,通过{{user.userThumb}}和`{{user.username}}显示用户名及头像

    相关文章

      网友评论

        本文标题:使用Socket.IO仿微信聊天应用

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