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

尾言


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

相关文章
|
8天前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
53 7
Jmeter实现WebSocket协议的接口测试方法
|
3月前
|
JavaScript 网络协议 前端开发
【Nodejs】WebSocket 全面解析+实战演练——(Nodejs实现简易聊天室)
【Nodejs】WebSocket 全面解析+实战演练——(Nodejs实现简易聊天室)
327 0
|
4月前
|
监控 JavaScript API
局域网监控软件的实时通知系统:利用Node.js和WebSocket实现即时消息推送
本文介绍了如何使用Node.js和WebSocket构建局域网监控软件的实时通知系统。实时通知对于网络安全和家庭监控至关重要,能即时发送监控数据变化的通知,提高响应速度。通过Node.js创建WebSocket服务器,当数据变化时,监控软件发送消息至服务器,服务器随即推送给客户端。此外,还展示了如何利用Node.js编写API,自动将监控数据提交到网站,便于用户查看历史记录,从而提升监控体验。
154 3
|
19天前
|
机器学习/深度学习 自然语言处理 网络协议
为什么ChatGPT采用SSE协议而不是WebSocket?
在探讨大型语言模型ChatGPT的技术实现时,一个引人注目的细节是其选择使用SSE(Server-Sent Events)协议而非WebSocket来实现数据的实时推送。这一选择背后,蕴含着对技术特性、应用场景及资源效率的深思熟虑。本文将深入探讨ChatGPT为何偏爱SSE,以及这一决策背后的技术逻辑。
38 2
|
20天前
|
JavaScript 前端开发 API
深入浅出:使用Node.js搭建RESTful API的实践之旅
【8月更文挑战第31天】本文将带你踏上一次Node.js的探险之旅,通过实际动手构建一个RESTful API,我们将探索Node.js的强大功能和灵活性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的实践经验和深刻的技术洞见。
|
1月前
|
存储 缓存 JavaScript
深入Node.js身份验证:策略与实践
【8月更文挑战第20天】
44 4
|
29天前
|
JavaScript 安全 前端开发
Node.js身份验证全攻略:策略与实践,打造坚不可摧的Web应用安全防线!
【8月更文挑战第22天】Node.js作为强大的服务器端JavaScript平台,对于构建高效网络应用至关重要。本文探讨其身份验证策略,涵盖从基于token至复杂的OAuth 2.0及JWT。Passport.js作为认证中间件,支持本地账号验证及第三方服务如Google、Facebook登录。同时介绍JWT轻量级验证机制,确保数据安全传输。开发者可根据应用需求选择合适方案,注重安全性以保护用户数据。
31 1
|
1月前
|
监控 小程序 安全
小程序全栈开发中的WebSocket实时通信实践是一种高效的开发模式。
随着移动互联网的发展,小程序成为企业拓展业务的新渠道。WebSocket作为一种实时通信协议,可在小程序中实现如实时聊天、推送、游戏等功能。它支持客户端与服务器间的全双工长连接通信,优于传统HTTP。开发者需注意安全、性能及兼容性等问题,以优化体验并保障稳定运行。掌握WebSocket有助于提升小程序功能性与用户体验。
35 1
|
2月前
|
JavaScript 前端开发 网络协议
从理论到实践:全面剖析Python Web应用中的WebSocket实时通信机制
【7月更文挑战第17天】WebSocket在实时Web应用中扮演重要角色,提供全双工通信,减少延迟。本文详述了Python中使用`websockets`库创建服务器的步骤,展示了一个简单的echo服务器示例,监听8765端口,接收并回显客户端消息。客户端通过JavaScript与服务器交互,实现双向通信。了解WebSocket的握手、传输和关闭阶段,有助于开发者有效利用WebSocket提升应用性能。随着实时需求增长,掌握WebSocket技术至关重要。
209 6
|
2月前
|
监控 前端开发 JavaScript
构建高效实时应用:Python WebSocket在前后端分离架构中的实践
【7月更文挑战第18天】WebSocket助力实时Web应用,通过一次握手建立持久连接,解决HTTP实时性问题。Python中可用Flask-SocketIO创建WebSocket服务器,前端JavaScript使用Socket.IO库连接。确保安全可采用HTTPS、认证及跨域限制。示例代码展示如何实现双向实时通信。
77 4