基于Socket.IO的node聊天室,支持私聊

    源码我放在了github上面。【地址在这儿】。首先可以看下【上篇】博客,把环境先搭建好和熟悉socket.io基本用法。整个可以实现下面的这些功能:

    1. 用户登陆进行聊天。
    2. 用户登陆后,即可发送消息。
    3. 对刚登录用户来说,罗列所有在线用户列表
    4. 对已在线的用户来说,通知该用户有新用户登陆或者离开
    5. 支持对所有人广播聊天内容,即群聊
    6. 支持私聊当前在线用户,即私聊
    7. 用户即时获得消息列表
    8. 当服务器断开连接时,提醒所有用户

    整个代码量非常少,可以根据自己的需求修改逻辑。

    没有进行太多的限制,比如没有限制重名登陆,主要是关注逻辑代码。

    没有写任何样式,所以很难看额,以后有时间慢慢的补起来吧,现在考试略多。。。。

    运行:切换到此目录,运行node index.js即可。打开端口3000,localhost:3000


    服务器端代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    var http = require('http');
    var sio = require('socket.io');
    var express = require('express');
    var app = express();
    var server = http.createServer(app);
    var io = sio.listen(server);
    app.get('/',function(req,res){
    res.sendFile(__dirname+'/index.html');
    })
    server.listen('3000',function(){
    console.log('listening on port 3000');
    })
    var onlineUsers = {};
    var onlineCount = 0;
    var userSockets = {};
    io.on('connection',function(socket){
    //当客户端有用户登陆的时候
    socket.on('login',function(userObj){
    socket.uid = userObj.uid;
    userSockets[userObj.uid] = socket;
    //检查是否在当前的用户列表中,如果不在count++
    if(!onlineUsers.hasOwnProperty(userObj.uid)){
    onlineUsers[userObj.uid] = userObj.username;
    onlineCount++;
    }
    //向客户端发送用户登陆的信息
    io.emit('login',{onlineUsers:onlineUsers,onlineCount:onlineCount,userObj:userObj});
    console.log(userObj.username + " 进入了聊天室");
    });
    //当服务器接收到消息时
    socket.on('message',function(data){
    if(data.to !== "所有人"){
    if(data.to in userSockets){
    userSockets[data.to].emit('to'+data.to,data);
    userSockets[data.uid].emit('to'+data.uid,data);
    }
    }else{
    io.emit('message',data);
    console.log(data.username+"对"+data.to+"说:"+data.message);
    }
    });
    //当客户端退出的时候
    socket.on('disconnect',function(){
    if(onlineUsers.hasOwnProperty(socket.uid)){
    //记录要退出用户的信息,发送给客户端
    var userObj = {uid:socket.uid,username:onlineUsers.uid};
    delete onlineUsers[socket.uid];
    delete userSockets[socket.uid];
    onlineCount--;
    io.emit('logout',{onlineUsers:onlineUsers,onlineCount:onlineCount,userObj:userObj});
    console.log(userObj.username+" 退出了聊天室");
    }
    })
    })

    客户端代码

    DOM结构部分
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <header>
    <h3>基于socket.io的聊天室</h3>
    <p id="notice"></p>
    </header>
    <div class="login">
    <h3>聊天前请先登陆</h3>
    <input type="text" placeholder="输入用户名" id="username">
    <input type="submit" value="登陆" onclick="CHAT.submit()">
    </div>
    <div id="main" style="display:none;">
    <aside>
    <p>在线用户列表</p>
    <ul id="onlineUsers">
    </ul>
    </aside>
    <section>
    <p>消息列表</p>
    <ul id="messageList">
    </ul>
    </section>
    </div>
    <footer id="footer" style="display:none">
    <textarea name="" id="message" cols="30" rows="10"></textarea>
    <select name="" id="selectUserList">
    <option value="">所有人</option>
    </select>
    <input type="submit" value="提交" onclick="CHAT.sendMessage()">
    </footer>
    js脚本代码部分
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    <script src="/socket.io/socket.io.js"></script>
    <script>
    (function(){
    window.CHAT = {
    uid:null,
    username:null,
    socket:null,
    submit:function(){
    var username = document.getElementById('username').value;
    if(username!==""){
    this.init(username);
    this.socket.emit('login',{uid:this.uid,username:this.username});
    this.initStyle();
    }
    },
    initStyle:function(){
    var main = document.getElementById('main');
    var footer = document.getElementById('footer');
    main.style.display = 'block';
    footer.style.display = 'block';
    },
    getUid:function(){
    return +new Date();
    },
    sendMessage:function(){
    var message = document.getElementById('message').value;
    var select = document.getElementById('selectUserList');
    var selectIndex = select.selectedIndex;
    var optionValue = select.options[selectIndex].value;
    var optionText = select.options[selectIndex].text;
    if(message != ""){
    this.socket.emit('message',{uid:this.uid,username:this.username,message:message,to:optionValue,toUser:optionText});
    }
    },
    createEleNode:function(ele,text){
    var eleNode = document.createElement(ele);
    var text = document.createTextNode(text);
    eleNode.appendChild(text);
    return eleNode;
    },
    updateUlist:function(data){
    var onlineLists = document.getElementById("onlineUsers");
    var str = "";
    for(var i in data.onlineUsers){
    str += "<li id='"+i+"'>"+data.onlineUsers[i]+"</li>";
    }
    onlineLists.innerHTML = str;
    },
    updateSelect:function(data){
    var selectUserList = document.getElementById('selectUserList');
    var str = "<option value='所有人'>所有人</option>";
    for(var key in data.onlineUsers){
    str += "<option value='"+key+"'>"+data.onlineUsers[key]+"</option>"
    }
    selectUserList.innerHTML = str;
    },
    updateNotice:function(msg){
    var notice = document.getElementById('notice');
    var node = this.createEleNode('li',msg)
    notice.appendChild(node);
    },
    appendMessage:function(data){
    var messageList = document.getElementById("messageList");
    var node = CHAT.createEleNode('li','用户'+data.username+"对"+data.toUser+"说:"+data.message);
    console.log(node);
    messageList.appendChild(node);
    },
    init:function(username){
    this.uid = this.getUid();
    this.username = username;
    this.socket = io.connect();
    this.socket.on('login',function(data){
    //当用户登陆的时候,更新列表
    CHAT.updateUlist(data);
    CHAT.updateSelect(data);
    CHAT.updateNotice(data.userObj.username+"进入了聊天室");
    });
    this.socket.on('logout',function(data){
    //当用户退出时,更新列表
    CHAT.updateUlist(data);
    CHAT.updateSelect(data);
    CHAT.updateNotice(CHAT.username+"退出了聊天室");
    });
    this.socket.on('message',function(data){
    CHAT.appendMessage(data);
    });
    this.socket.on('to'+CHAT.uid,function(data){
    CHAT.appendMessage(data);
    })
    this.socket.on('disconnect',function(){
    CHAT.updateNotice("服务器断开连接");
    });
    }
    }
    })()
    </script>

    这里注意下有个地方,有的时候我们调用方法的时候,不能够直接用this.method调用该方法,因为比如我们使用:this.socket.on的时候,这个内部的this已经不是当前对象了。所以我们要直接使用CHAT.method或者CHAT.属性的方法来调用。


    截图结果

    结果截图


    总结

    socket.io很好用,开发也很简单,花了比较少的时间。但是还有一些功能没有实现,比如如何实现群组,也就是房间聊天?如何给不在线的用户发送聊天记录?如何利用redis和聊天室结合起来?