WebSocket --- Node.js实现简单聊天室

1,587 阅读2分钟

聊天室的原理:客户端A发送消息->服务器接收消息->服务器主动向客户端B发送消息->客户端B接收消息

HTTP请求不适用的原因:客户端/服务端的请求与响应通常基于HTTP协议是实现,但HTTP协议的局限:只能由客户端发送请求,服务器响应请求并返回数据,进行单向的数据传输

WebSocket适用的原因:在单个TCP连接上进行全双工通信的协议。通俗理解:实现客户端/服务端双向数据传输的协议

WebSocket特点:
	1.无同源限制,客户端可与任意服务器通信;
    	2.协议头为ws(wss为加密标识符),如:ws://localhost:8080;
        3.目前主流浏览器已支持WebSocket

客户端的实现:

1.创建WebSocket对象,打开WebSocket

var webSocket = new WebSocket(url,[protocal])	//url --- 指定连接的url	protocal --- 可接受的子协议

2.触发WebSocket对象的事件,实现连接

webSocket.open = function (){	//同服务器连接时触发
	webSocket.send()	//向服务器发送数据
}

webSocket.message = function (){	//成功接收到服务器数据时触发
	……
}

webSocket.error = function (){	//通信错误时触发
	……
}

webSocket.close = function (){		//同服务器连接断开时触发
	webSocket.close()	//关闭连接
}

Node.js的ws模块实现服务器:

1.导入ws模块,引用Server类,并实例化

const WebSocket = require('ws').Server

wss = new WebSocket({host:'loaclhost',port:8080})

2.调用webSocket对象方法,实现数据传输

wss.on('connection',function(ws){	//连接成功

    ws.on('message',function(message){	//接收客户端的数据
            
            client.send()	//向客户端发送数据
    
    })
    
})

具体实现代码:

//入口html文件,确定用户名

<style>
        img {
            width: 100px;
            height: 100px;
            border-radius: 50%;
            position: absolute;
            top: 30%;
            left: 50%;
            margin-top: -50px;
            margin-left: -50px;
        }

        input {
            width: 200px;
            border: 1px solid #ccc;
            padding: 7px 0px;
            border-radius: 3px;
            padding-left: 5px;
            box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
            position: absolute;
            top: 42%;
            left: 50%;
            margin-left: -100px;
        }

        input:focus {
            border: none;
            outline: 0;
            -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
            box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6)
        }


        .ant-btn {
         
            line-height: 1.499;
            position: relative;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
            cursor: pointer;
            transition: all .3s cubic-bezier(.645, .045, .355, 1);
            user-select: none;
            touch-action: manipulation;
            height: 32px;
            padding: 0 15px;
            font-size: 14px;
            border-radius: 4px;
            color: rgba(0, 0, 0, 0.65);
            background-color: #fff;
            border-color: #d9d9d9;
        }

        .ant-btn-primary {
            width: 60px;
            color: #fff;
            background-color: #1890ff;
            border-color: #1890ff;
            text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
            box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
            position: absolute;
            top: 50%;
            left: 50%;
            margin-left: -30px;
        }
    </style>
    
<body>
    <img src="./avatar.jpg" alt="">
    <input type="text">
    <button class="ant-btn ant-btn-primary" onclick='toChat()' >Go</button>
</body>
<script>
    function toChat(){
        var name = document.getElementsByTagName('input')[0].value
        window.location.href=`./webSocket.html?name=${name}`
    }
</script>

//获取url参数

function getQueryString(name) {
    let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
    let r = window.location.search.substr(1).match(reg);
    if (r != null) {
        return decodeURIComponent(r[2]);
    };
    return null;
 }
聊天室html文件,用于发送和接收消息

<body>
    <div class="chat">
        <div class="inputBox">
            <input type="text" />
            <button class="ant-btn ant-btn-primary" onclick='send()'>发送</button>
        </div>
    </div>

</body>
<script src='./getUrl.js'></script>
<script>
    var user = {
        name: getQueryString('name'),
        message: [],
    }

    var flag = true;

    var webSocket = new WebSocket("ws://localhost:8080");

    function send() {
        let value = document.getElementsByTagName('input')[0].value
        webSocket.onopen(event, value)
        return document.getElementsByTagName('input')[0].value = ''
    }

    webSocket.onopen = function (event, value) {
        if (value == undefined) {
            webSocket.send(JSON.stringify(user.name))
            return flag = true
        } else {
            user.message.push(value)
            webSocket.send(JSON.stringify(user))
            return flag = false
        }
    }

    webSocket.onmessage = function (evt) {

        var box = document.createElement("div")
        box.setAttribute("class", "box")

        //定义头像
        var avatar = document.createElement("img")
        avatar.src = './avatar.jpg'

        var ele = document.createElement("div")
        ele.setAttribute("class", "message")

        if (flag && typeof JSON.parse(evt.data) == 'string') {
            var welcome = document.createElement("div")
            welcome.setAttribute('class', 'welcome')
            document.body.appendChild(welcome)
            welcome.innerHTML = `${JSON.parse(evt.data)}加入聊天室`
        } else {
            ele.innerHTML = JSON.parse(evt.data).message[JSON.parse(evt.data).message.length - 1]

            if (user.name == JSON.parse(evt.data).name) {
                box.style.justifyContent = 'flex-end'
                box.appendChild(ele)
                box.appendChild(avatar)
            } else {
                box.appendChild(avatar)
                box.appendChild(ele)
            }
        }

        document.body.appendChild(box)
    }

    webSocket.onclose = function (evt) {
        console.log("connection closed")
    }
    
</script>

最简单的基本实现就是如此,完整的聊天室需要考虑的细节更多,暂时就不一一实现