第九课:WebSocket与实时通信技术解析

发布于:2025-03-13 ⋅ 阅读:(13) ⋅ 点赞:(0)

在现代的网络应用中,实时通信的需求日益增加。无论是即时聊天应用、在线协作工具还是实时数据更新的仪表板,实时通信都显得至关重要。WebSocket作为一种在双向通信上表现卓越的协议,能够在客户端和服务器之间建立全双工的通信桥梁。本文将详细介绍如何在Node.js中使用WebSocket实现实时通信,并通过socket.io库实现一个聊天室示例,进一步探讨广播消息与房间管理,最后展示一个在线协作白板应用的实战案例。

1. WebSocket协议与HTTP对比

WebSocket基础概念

WebSocket是一种在单个TCP连接上提供全双工通信能力的协议。它允许客户端和服务器互相推送数据,无需重复建立连接,大大提升了交互效率和实时性。

WebSocket与HTTP的区别
  • 通信模式:HTTP是请求-响应模型,每次通信都需要建立新的连接;而WebSocket建立持久连接,支持双向实时通信。
  • 数据格式:HTTP通常传输文本数据,每次请求和响应都包含完整的头部信息;WebSocket可以传输文本和二进制数据,头部信息较小,减少了数据传输量。
  • 应用场景:HTTP适用于传统的网页浏览、API调用等场景;WebSocket适用于实时性要求较高的场景,如在线聊天、实时游戏等。

2. 使用socket.io实现聊天室

为什么选择socket.io

Socket.IO是一个支持客户端与服务器之间实时、双向、基于事件的通信的库。它不仅简化了API接口,使得操作更容易,而且让那些不支持WebSocket协议的浏览器会将WebSocket连接自动降为Ajax连接,最大限度地保证了兼容性。

安装与配置

首先,确保你已经安装了Node.js和npm。然后,在项目根目录下运行以下命令安装socket.io:

npm install socket.io express
服务器端代码

创建一个server.js文件,用于搭建WebSocket服务器:

const socketIo = require('socket.io')
const http = require('http')
const express = require('express')

const app = express()
const server = http.createServer(app)

app.use(express.static('simple-chat'))

const IO = socketIo(server)

IO.on('connection', socket => {
    console.log('一个用户连接上来了')

    socket.on('message', msg => {
        console.log('用户发送了一个信息过来: ' + msg)
    })

    socket.on('disconnect', () => {
        console.log('一个用户断开连接了')
    })
})

server.listen(18080, () => {
    console.log('----------------- 服务器运行成功')
    console.log('----------------- http://localhost:18080')
})
客户端代码

创建一个index.html文件,用于展示聊天界面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple Chat</title>
    <script src="./socket.io.min.js"></script>
</head>
<body>

    <hr>
    <input id="msgBox" type="text" placeholder="输入要发送的消息">
    <hr>
    <button id="connectBtn">连接服务</button>
    <hr>
    <button id="sendBtn">发送消息</button>
    <hr>
    <button id="closeBtn">断开连接</button>

    <script>
        let socket = null

        connectBtn.addEventListener('click', () => {
            socket = io('http://localhost:18080')
        })

        sendBtn.addEventListener('click', () => {
            if (socket) socket.emit('message', msgBox.value)
        })

        closeBtn.addEventListener('click', () => {
            if (socket) {
                socket.close()
                socket = null
            }
        })
    </script>
</body>
</html>

3. 广播消息与房间管理

房间管理

Socket.IO支持将客户端分组到“房间”,这在处理多个并发聊天室或游戏房间时非常有用。例如,你可以让用户加入特定的聊天室,并只接收该聊天室的消息。

服务器示例代码

在服务器端代码中,管理房间的加入和离开:

const socketIo = require('socket.io')
const http = require('http')
const express = require('express')
const crypto = require('crypto')

const app = express()
const server = http.createServer(app)

app.use(express.static('simple-chat'))

const IO = socketIo(server)

IO.on('connection', socket => {
    console.log('一个用户连接上来了')

    socket.on('join-room', obj => {
        console.log(`${obj.userName} 加入聊天室 ${obj.roomName}`)
        socket.join(obj.roomName)
    })

    socket.on('leave-room', obj => {
        console.log(`${obj.userName} 离开聊天室 ${obj.roomName}`)
        socket.leave(obj.roomName)
    })

    socket.on('message', obj => {
        console.log(`${obj.userName} 在聊天室 ${obj.roomName} 发送了消息:${obj.msg}`)
        socket.to(obj.roomName).emit('client-message', obj)
    })

    socket.on('disconnect', () => {
        console.log('一个用户断开连接了')
    })
})

server.listen(18080, () => {
    console.log('----------------- 服务器运行成功')
    console.log('----------------- http://localhost:18080')
})
客户端示例代码

在客户端代码中,让用户选择加入或离开房间:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple Chat</title>
    <script src="./socket.io.min.js"></script>
</head>
<body>

    <hr>
    <label for="userName">你的名字:</label>
    <input id="userName" type="text" placeholder="输入你的名字">
    <hr>
    <label for="roomName">你要加入的聊天室:</label>
    <input id="roomName" type="text" placeholder="输入你要加入的聊天室名字">
    <button id="joinBtn">加入聊天室</button>
    <hr>
    <ul id="infoBox"></ul>
    <hr>
    <input id="msgBox" type="text" placeholder="输入要发送的消息">
    <hr>
    <button id="sendBtn">发送消息</button>
    <hr>
    <button id="leaveBtn">离开聊天室</button>

    <script>
        let socket = null

        joinBtn.addEventListener('click', () => {
            socket = io('http://localhost:18080')
            socket.emit('join-room', {userName: userName.value.trim(), roomName: roomName.value.trim()})
            socket.on('client-message', obj => {
                let li = document.createElement('li')
                li.innerHTML = `${obj.userName} 发送消息:${obj.msg}`
                infoBox.appendChild(li)
            })
        })

        sendBtn.addEventListener('click', () => {
            if (socket) {
                socket.emit('message', {userName: userName.value.trim(), roomName: roomName.value.trim(), msg: msgBox.value.trim()})
            }
        })

        leaveBtn.addEventListener('click', () => {
            if (socket) {
                socket.emit('leave-room', {userName: userName.value.trim(), roomName: roomName.value.trim()})
                socket.close()
                socket = null
            }
        })
    </script>
</body>
</html>

4. 实战:在线协作白板应用

项目概述

使用Node.js、Socket.IO和Vue.js实现一个实时多人在线协作白板应用。该应用允许多个用户在同一白板上进行实时绘图和聊天。

服务器端代码

创建一个server.js文件,用于处理WebSocket连接和消息转发:

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');

const app = express();
const server = http.createServer(app);
const IO = socketIo(server);

app.use(express.static('white-board'))

IO.on('connection', (socket) => {
    console.log('一个用户加入了会议');

    socket.on('draw', data => {
        IO.emit('draw', data);
    });

    socket.on('message', msg => {
        IO.emit('message', msg);
    });

    socket.on('disconnect', () => {
        console.log('一个用户离开了会议');
    });
});

server.listen(18080, () => {
    console.log('----------------- 服务器运行成功')
    console.log('----------------- http://localhost:18080')
})
前端代码

创建一个Vue项目,并添加以下组件和代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>White Board</title>
    <script src="./socket.io.min.js"></script>
    <style>
        #msgList,
        #msgBox,
        #boardNode {
            display: block;
            box-sizing: border-box;
            width: 600px;
            height: 400px;
            border: solid 1px #ddd;
            border-radius: 16px;
            margin: 15px auto;
        }
        #msgList {
            height: auto;
            padding: 20px 10px;
        }
        #msgList li {
            margin-left: 18px;
        }
        #msgBox {
            height: 36px;
            font-size: 14px;
            color: #111;
            padding: 0 10px;
        }
    </style>
</head>
<body>
    <div>
        <canvas id="boardNode" width="800" height="600"></canvas>
        <ul id="msgList">
            <li>接收到的信息</li>
        </ul>
        <input type="text" id="msgBox" placeholder="输入发送的消息..."/>
    </div>

    <script>
        let socket = io('http://localhost:18080')
        let isDrawing = false
        let context = boardNode.getContext('2d')
        
        socket.on('message', msg => {
            let li = document.createElement('li')
            li.innerHTML = msg
            msgList.appendChild(li)
        })
        msgBox.addEventListener('keyup', evt => {
            if (evt.keyCode == 13) {
                socket.emit('message', msgBox.value.trim())
                msgBox.value = ''
            }
        })

        let x1 = 0
        let y1 = 0
        let x2 = 0
        let y2 = 0
        boardNode.addEventListener('mousedown', evt => {
            isDrawing = true
            context.beginPath()
            x1 = evt.offsetX
            y1 = evt.offsetY
            context.moveTo(x1, y1)
        })
        boardNode.addEventListener('mousemove', evt => {
            if (!isDrawing) return
            x2 = evt.offsetX
            y2 = evt.offsetY
            context.lineTo(x2, y2)
            context.stroke()
            socket.emit('draw', {
                x1,
                y1,
                x2,
                y2
            })
            x1 = evt.offsetX
            y1 = evt.offsetY
        })
        boardNode.addEventListener('mouseup', evt => {
            isDrawing = false
            context.closePath()
        })
        socket.on('draw', data => {
            context.beginPath()
            context.moveTo(data.x1, data.y1)
            context.lineTo(data.x2, data.y2)
            context.stroke()
        })
    </script>
</body>
</html>

将上述代码添加到相应的文件中,并运行项目:

npm run serve

打开多个浏览器窗口访问http://localhost:18080,你将看到多个用户可以在同一白板上进行实时绘图和聊天。

总结

本文通过WebSocket协议与HTTP的对比,介绍了WebSocket在实时通信中的优势。接着,通过socket.io库实现了一个简单的聊天室示例,并展示了如何管理广播消息和房间。最后,实战部分实现了一个在线协作白板应用,展示了WebSocket在实时多人协作场景中的应用。希望这些内容能够帮助你更好地理解和应用WebSocket技术,构建出功能丰富、性能高效的实时应用。

关注我!!🫵 持续为你带来Nodejs相关内容。