nodejs WebSocket协议实践

简介: nodejs WebSocket协议实践

theme: channing-cyan


前言


学习一下 WebSocket 协议,在这之前我使用 nodejs-websocket 这个包解决,使用比较简单,但是我发现那样不利于自己对该协议的理解,于是我打算用比较官方的依赖 ws 包来进行 WebSocket 协议实践。


创建项目下载依赖

$ mkdir project && cd project
$ npm init -y 
$ npm i ws @types/ws

服务端


首先我们先编服务端代码 webSocket.js。

引入我们的 ws 和协议包 http,并创建 WebSocket 服务。

import {WebSocketServer} from 'ws'
import {createServer} from 'http'
const httpServer = createServer()
// 创建 WebSocket 服务
const wss = new WebSocketServer({
    noServer: true
})

然后我们编写握手事件 upgrade ,我们这里选择当接收到的请求头属性 sec-websocket-protocol 为 conn 时进行连接。

import {WebSocketServer} from 'ws'
import {createServer} from 'http'
const httpServer = createServer()
// 创建 WebSocket 服务
const wss = new WebSocketServer({
    noServer: true
})
// 握手事件
httpServer.on('upgrade', (req, socket, head) => {
    if (req.headers['sec-websocket-protocol'] === 'conn') {
            wss.handleUpgrade(req, socket, head, (ws) => {
            wss.emit('connection', ws, req)
        })
    }
})

然后我们编写连接事件,当连接成功时,我们可以往客户端发送一个连接的标志。

import {WebSocketServer} from 'ws'
import {createServer} from 'http'
const httpServer = createServer()
// 创建 WebSocket 服务
const wss = new WebSocketServer({
    noServer: true
})
// 握手事件
httpServer.on('upgrade', (req, socket, head) => {
    if (req.headers['sec-websocket-protocol'] === 'conn') {
            wss.handleUpgrade(req, socket, head, (ws) => {
            wss.emit('connection', ws, req)
        })
    }
})
// 连接事件
wss.on('connection', (ws) => {
    // 往客户端发送 connected 事件,我们使用 type 来进行事件标识,这样方便客户端处理
    ws.send(JSON.stringify({type: 'connected'}))
})

当然,连接完成我们同时可以在连接事件中添加对客户端传来数据的监听,就实现了客户端往服务端发送事件。

wss.on('connection', (ws) => {
    ws.send(JSON.stringify({type: 'connected'}))
    // message 监听事件事件,客户端传来的消息都走这里。
    ws.on('message', (data, isBinary) => {
        // 由于我们无法确定传过来的数据类型,因此要用 isBinary 区分 buffer 转化为字符串
        const receiveData = isBinary ? data : data.toString()
        console.log(receiveData)
    })
})

在加个对异常错误 error 的监听。

wss.on('error', (e) => {
    const {code} = e
    if (code !== 'EADDRINUSE') {
        console.error(
            `WebSocket server error:\n${e.stack || e.message}`,
        );
    }
});

这样我们就完成了 httpServer 的 WebSocket 协议服务端内容,但是假如我们有多个 httpServer 呢?我们不如把 WebSocket 协议内容封装成一个函数,方便复用。

import {WebSocketServer} from 'ws'
import {createServer} from 'http'
const createWebSocketServer = (httpServer) => {
    const wss = new WebSocketServer({
        noServer: true
    })
    // 握手事件
    httpServer.on('upgrade', (req, socket, head) => {
        if (req.headers['sec-websocket-protocol'] === 'conn') {
            wss.handleUpgrade(req, socket, head, (ws) => {
                wss.emit('connection', ws, req)
            })
        }
    })
    wss.on('connection', (ws) => {
        ws.send(JSON.stringify({type: 'connected'}))
        ws.on('message', (data, isBinary) => {
            const receiveData = isBinary ? data : data.toString()
            console.log(receiveData)
        })
    })
    wss.on('error', (e) => {
        const {code} = e
        if (code !== 'EADDRINUSE') {
            console.error(
                `WebSocket server error:\n${e.stack || e.message}`,
            );
        }
    });
    // 返回 发送信息的方法 、 关闭的方法 还有 对应的服务 wss
    return {
        send(message) {
            wss.clients.forEach((ws) => {
                if (ws.readyState === 1) {
                    ws.send(message)
                }
            })
        },
        close() {
            wss.close()
        },
        wss
    }
}

然后我们就可以很容易的复用了。

const httpServer1 = createServer()
const httpServer2 = createServer()
const ws1 = createWebSocketServer(httpServer1)
const ws2 = createWebSocketServer(httpServer2)

我们暂时只需要启动一个服务,我们将它启动在 3000 端口,然后顺便写一个定时器,不停向服务端发送消息来进行测试。

最终服务端完整代码:

import {WebSocketServer} from 'ws'
import {createServer} from 'http'
const createWebSocketServer = (httpServer) => {
    const wss = new WebSocketServer({
        noServer: true
    })
    // 握手事件
    httpServer.on('upgrade', (req, socket, head) => {
        if (req.headers['sec-websocket-protocol'] === 'conn') {
            wss.handleUpgrade(req, socket, head, (ws) => {
                wss.emit('connection', ws, req)
            })
        }
    })
    // 连接事件
    wss.on('connection', (ws) => {
        // 往客户端发送 connected 事件,我们使用 type 来进行事件标识,这样方便客户端处理
        ws.send(JSON.stringify({type: 'connected'}))
        // message 监听事件事件,客户端传来的消息都走这里。
        ws.on('message', (data, isBinary) => {
            // 由于我们无法确定传过来的数据类型,因此要用 isBinary 区分 buffer 转化为字符串
            const receiveData = isBinary ? data : data.toString()
            console.log(receiveData)
        })
    })
    wss.on('error', (e) => {
        const {code} = e
        if (code !== 'EADDRINUSE') {
            console.error(
                `WebSocket server error:\n${e.stack || e.message}`,
            );
        }
    });
    // 返回 发送信息的方法 、 关闭的方法 还有 对应的服务 wss
    return {
        send(message) {
            wss.clients.forEach((ws) => {
                if (ws.readyState === 1) {
                    ws.send(message)
                }
            })
        },
        wss,
        close() {
            wss.close()
        }
    }
}
const httpServer = createServer()
const ws = createWebSocketServer(httpServer)
const sendMessage = (type, data) => {
    ws.send(JSON.stringify({type, data}))
}
setInterval(() => {
    sendMessage('console', `服务端定时向客户端发送消息 时间:${new Date().toLocaleString()}`)
}, 3000)
httpServer.listen(3000, () => {
    console.log('服务器开启')
})
$ node webSocket.js
服务器开启

客户端


首先我们需要检测当前客户端浏览器是否支持 WebSocket ,如果支持我们尝试连接。


http 协议下我们使用 ws: 连接,https 可能需要使用 wss: 。


还记得我们在服务端设置了请求头属性 sec-websocket-protocol 为 conn 时进行连接,所以我们要传递一个字符串 conn

<body>
<script>
    if ('WebSocket' in window) {
        const ws = new WebSocket('ws://localhost:3000', 'conn');
    }
</script>
</body>

完成连接的步骤之后就是加入对服务端事件的监听,该监听事件为 message ,还可以顺便加上对连接关闭 close 的监听。

<body>
<script>
    if ('WebSocket' in window) {
        const ws = new WebSocket('ws://localhost:3000', 'conn');
        let pingTimer = null
        ws.addEventListener('message', async ({data}) => {
            const json = JSON.parse(data)
            // 我们通过和服务端商量的使用 type 的方式处理不同的事件。
            if (json.type === 'connected') {
                console.log('[webSocket] connected.')
                pingTimer = setInterval(() => ws.send('心跳'), 30000)
            }
            // 定时对服务端发送消息告诉服务端客户端还 “活着”
            if (json.type === 'console') {
                console.log(json.data)
            }
        })
        ws.addEventListener('close', async () => {
            if (pingTimer) clearInterval(pingTimer);
            console.info('[webSocket] disconnected.');
        });
    }
</script>
</body>

我们还可以加一个主动向服务端发送消息的测试按钮。

最后完整的客户端代码:

<body>
<button onclick="send()">
    向服务器发送消息
</button>
<script>
    if ('WebSocket' in window) {
        const ws = new WebSocket('ws://localhost:3000', 'conn');
        let pingTimer = null
        ws.addEventListener('message', async ({data}) => {
            const json = JSON.parse(data)
            // 我们通过和服务端商量的使用 type 的方式处理不同的事件。
            if (json.type === 'connected') {
                console.log('[webSocket] connected.')
                pingTimer = setInterval(() => ws.send('心跳'), 30000)
            }
            // 定时对服务端发送消息告诉服务端客户端还 “活着”
            if (json.type === 'console') {
                console.log(json.data)
            }
        })
        ws.addEventListener('close', async () => {
            if (pingTimer) clearInterval(pingTimer);
            console.info('[webSocket] disconnected.');
        });
        function send() {
            ws.send(`客户端向服务端发送消息 时间:${new Date().toLocaleString()}`)
        }
    }
</script>
</body>

进行测试


image.png

image.png

尾言


如果觉得文章对你有帮助的话,欢迎点赞收藏哦,有什么错误或者意见建议也可以留言,感谢~

相关文章
|
13天前
|
JavaScript 网络协议 前端开发
【Nodejs】WebSocket 全面解析+实战演练——(Nodejs实现简易聊天室)
【Nodejs】WebSocket 全面解析+实战演练——(Nodejs实现简易聊天室)
47 0
|
2月前
|
监控 JavaScript API
局域网监控软件的实时通知系统:利用Node.js和WebSocket实现即时消息推送
本文介绍了如何使用Node.js和WebSocket构建局域网监控软件的实时通知系统。实时通知对于网络安全和家庭监控至关重要,能即时发送监控数据变化的通知,提高响应速度。通过Node.js创建WebSocket服务器,当数据变化时,监控软件发送消息至服务器,服务器随即推送给客户端。此外,还展示了如何利用Node.js编写API,自动将监控数据提交到网站,便于用户查看历史记录,从而提升监控体验。
117 3
|
12天前
|
网络协议 Java 应用服务中间件
|
2天前
|
JavaScript 前端开发 Shell
深入Node.js的进程与子进程:从文档到实践
深入Node.js的进程与子进程:从文档到实践
|
25天前
WebSocket 协议
【6月更文挑战第6天】
11 1
|
29天前
|
负载均衡 安全 Java
Java一分钟之-WebSocket:实时通信协议
【6月更文挑战第1天】WebSocket是实现客户端与服务器长连接、双向通信的协议,简化实时数据传输。Java中的WebSocket实现基于JSR 356。本文涵盖WebSocket基础(持久连接、双向通信、低延迟)、工作流程、常见问题(安全、连接管理、数据编码)及Java实现示例,强调错误处理、心跳机制和资源管理的最佳实践。
61 6
|
4天前
|
监控 网络协议 Java
Java中的WebSocket应用与实践
Java中的WebSocket应用与实践
|
6天前
|
网络协议 Java 应用服务中间件
WebSocket协议
WebSocket协议
9 0
|
2月前
|
网络协议 前端开发 Java
Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)
Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)
33 4
|
2月前
WebSocket 协议
“【5月更文挑战第28天】”
19 1