Nodejs和WebSocket在线聊天室开发(2)

    在上一篇中,我们对websocket的通信方法进行了简单的了解,搭建了最简单的服务器。在本篇中就进行最终的简易聊天室的开发吧。

1. 最终效果预览



2. 过程中使用到的websocket方法简单总结

在JS中以下 API 用于创建 WebSocket 对象。第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

var Socket = new WebSocket(url, [protocol] );

比如我们在程序中使用的:

var ws = new WebSocket('ws://127.0.0.1:12345');

HTML(前端)处理函数:
WebSocket 事件

以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

WebSocket 方法

以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

方法描述
Socket.send()

使用连接发送数据

Socket.close()

关闭连接


举个栗子:在建立连接时会出发ws.onopen函数,在收到服务器发来的消息时会自动触发ws.onmessage函数,在用户关闭网页或者手动关闭连接时会触发ws.onclose函数。发送数据的时候使用ws.send函数即可,发送的内容作为参数。具体的使用方法在后面会讲到。


服务器(后端)处理函数:

上一篇讲过,在服务器端的app.js使用类似如下方法来实现服务器端监听,具体的逻辑代码在该函数中实现,服务器端发送和接收消息均在该函数中实现。

  1. var server = ws.createServer(function(conn) {  
  2.     ...... 
  3. }).listen(12345);  

使用

conn.on('text', function(str){ ... });

来处理接收的消息(str里的内容),这里我们均使用json格式数据交互。

使用

conn.on('close', function(){ ... });

来处理客户端关闭。

另外

conn.on('error', function(){ ... });

也必须写出来,否则每当客户端关闭时,后端服务器会崩溃自动关闭。


3. 逻辑简单说明

  • 用户新打开一个页面,不会自动向服务器发起连接请求,只有用户按下设置按钮之后才会发起连接(若没有输入用户名,默认用户名为“defaultN”);
  • 第一次连接发送的json数据为:{name: "哈哈哈", type: "setname"}
  • 服务器端收到类型为“setname”的数据时,会广播该消息,之后再广播一条用户列表json数据,用于更新各个客户端的用户列表。
  • 每当有用户按下发送按钮时,若输入框中内容不为空,会向服务器端发送json数据: {message: "你好啊", type: "chat"}
  • 之后服务器端同样的广播该条消息给所有客户端。


4. 附上前后端代码

app.js

var ws = require('nodejs-websocket');

var server = ws.createServer(function(conn) {

    conn.on('text', function(str) {

        var data = JSON.parse(str);
        console.log(data);

        switch (data.type) {
            case 'setname':
                conn.nickname = data.name;
                
                boardcast(JSON.stringify({
                    type: 'serverInformation',
                    message: data.name + ' 加入房间'
                }));

                boardcast(JSON.stringify({
                    type: 'chatterList',
                    list: getAllChatter()
                }))
                break;
        
            case 'chat':
                boardcast(JSON.stringify({
                    type: 'chat',
                    name: conn.nickname,
                    message: data.message
                }));
                break;

            default:
                break;
        }
    });

    conn.on('close', function() {
        boardcast(JSON.stringify({
            type: 'serverInformation',
            message: conn.nickname + ' 离开房间'
        }));

        boardcast(JSON.stringify({
            type: 'chatterList',
            list: getAllChatter()
        }))
    });

    conn.on('error', function(err) {
        console.log(err);
    });
}).listen(12345);

function boardcast(str) {
    server.connections.forEach(function(conn) {
        conn.sendText(str);
    })
}

function getAllChatter() {

    var chatterArr = [];

    server.connections.forEach(function(conn) {
        chatterArr.push({name: conn.nickname});
    })

    return JSON.stringify(chatterArr);
}


index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ChatRoom</title>
    <link href="css/default.css" rel="stylesheet" type="text/css"></link>
</head>

<body>
    <div id="header">
        <input type="text" id="name" placeholder="输入用户名">
        <button id="setname" >设置</button>
        <input type="text" id="text" placeholder="输入发送内容">
        <button id="btn">发送</button>
    </div>
    <div id="body">
        <div id="container"></div>
        <div id="chatterlist">
            <p class="chatterlist-head">在线用户(0)</p>
        </div>
    </div>
    <script>
        var ws = null;

        document.getElementById('setname').onclick = function () {
            var name = document.getElementById('name').value;
            if (name === '') {
                name = "DefaultN";
            }

            ws = new WebSocket('ws://127.0.0.1:12345');

            ws.onopen = function () {
                ws.send(JSON.stringify({
                    name: name,
                    type: 'setname'
                }));
            }

            document.getElementById('btn').onclick = send;
            document.getElementById('text').onkeyup = function(e) {
                if (e.keyCode !== 13) return;
                send();
            }

            ws.onmessage = function (e) {
                var data = JSON.parse(e.data);

                if(data.type === 'chatterList') {
                    var list = JSON.parse(data.list);

                    var oldList = document.getElementById('chatterlist');
                    oldList.innerHTML = '';

                    var p_list_head = document.createElement('p');
                    p_list_head.setAttribute('class', 'chatterlist-head');
                    p_list_head.innerHTML = "在线用户(" + list.length + ")";
                    oldList.appendChild(p_list_head);

                    for(var i = 0; i < list.length; i++) {
                        var p_user = document.createElement('p');
                        p_user.innerHTML = list[i].name;
                        p_user.setAttribute('class', 'userlist-item');
                        oldList.appendChild(p_user);
                    }
                } else {
                    console.log(data);
                    var oldContent = document.getElementById('container');
                    oldContent.insertBefore(createChatDiv(data), oldContent.children[0]);
                }
            }

            var p_name = document.getElementById('name');
            var p_setname = document.getElementById('setname');
            p_name.innerHTML = name;
            p_setname.setAttribute('disabled', true);
            p_name.setAttribute('disabled', true);
            p_setname.style.display = "none";
            p_name.style.border = "none";
        }

        function createChatDiv(data) {
            var div = document.createElement('div');
            var p_time = document.createElement('p');
            var p_content = document.createElement('p');

            switch (data.type) {
                case 'serverInformation':
                    p_time.innerHTML = new Date().Format("yyyy-MM-dd hh:mm:ss");  
                    p_content.innerHTML = data.message;
                    break;
                
                case 'chat':
                    p_time.innerHTML = new Date().Format("yyyy-MM-dd hh:mm:ss");  
                    p_content.innerHTML = data.name + ': ' + data.message;
                    break;

                default:
                    break;
            }
            
            p_time.setAttribute('class', 'time');
            p_content.setAttribute('class', 'content');

            div.appendChild(p_time);
            div.appendChild(p_content);
            return div;
        }

        Date.prototype.Format = function (fmt) { //author: meizz 
            var o = {
                "M+": this.getMonth() + 1, //月份 
                "d+": this.getDate(), //日 
                "h+": this.getHours(), //小时 
                "m+": this.getMinutes(), //分 
                "s+": this.getSeconds(), //秒 
                "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
                "S": this.getMilliseconds() //毫秒 
            };
            if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (var k in o)
            if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            return fmt;
        }

        function send() {
            if(document.getElementById('text').value === '') return;
            ws.send(JSON.stringify({
                message: document.getElementById('text').value,
                type: 'chat'
            }));
            document.getElementById('text').value = '';
        }

    </script>
</body>

</html>


default.css

body{
    height: 100%;
    clear: both;
    margin: 0;
}

#header{
    background-color: white;
    position: fixed;
    padding: 10px;
    width: 100%;
    border-bottom: 1px solid #ddd;
}

#body{
    padding-top: 51px;
    height: 100%;
    width: 100%;
    overflow: hidden;
}

#container{
    min-width: 500px;
    padding-left: 10px;
    padding-right: 10px;
    width: 80% - 20px;
    height: 100%;
    float: left;
}

#chatterlist{
    position: fixed;
    right: 0;
    top: 51;
    width: 20%;
    background-color: #f6f6f6;
    height: 100%;
    overflow-x: hidden;
    overflow-y: scroll;
}

.chatterlist-head{
    margin: 0;
    padding: 5px 0 5px 10px;
    width: 100% - 10px;
    color: white;
    background-color: #00a1b6;
}

.userlist-item{
    margin: 0;
    padding-left: 10px;
    padding-top: 5px;
    padding-bottom: 5px;
    font-size: 14px;
    border-bottom: 1px solid #ddd;
}

.time{
    width: fit-content;
    padding: 2px 8px 2px 8px;
    font-size: 8px;
    color: gray;
    background-color: #f6f6f6;
    border-radius: 8px;
    margin: 0;
    margin-top: 10px;
}

.content{
    width: fit-content;
    padding: 2px 8px 2px 8px;
    font-size: 14px;
    color: #f6f6f6;
    background-color: #00a1b6;
    border-radius: 8px;
    max-width: 70%;
    margin: 0;
    margin-top:5px;
}

#btn, #setname{
    height: 30px;
    font-size: 14px;
    border-radius: 10px;
    border: 1px solid #00a1b6;
    color: white;
    padding: 2px 10px 2px 10px;
    background-color: #00a1b6;
    border: none;
    outline: none;
    cursor: pointer;
}

#btn:hover{
    color: #00a1b6;
    background-color: white;
    border: 1px solid #00a1b6;
    transition: 0.4s;
}

#setname:hover{
    color: #00a1b6;
    background-color: white;
    border: 1px solid #00a1b6;
    transition: 0.4s;
}

#name, #text{
    height: 25px;
    width: 100px;
    font-size: 14px;
    border-radius: 10px;
    border: 1px solid #9fafb1;
    box-shadow: #ddd;
    padding-left: 10px;
    outline: none;
}

#text{
    width: 400px;
}

#name:focus{
    border: 1px solid #00a1b6;
    transition: 1s;
}

#text:focus{
    border: 1px solid #00a1b6;
    transition: 1s;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值