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

尾言


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

相关文章
|
22天前
|
监控 小程序 前端开发
小程序全栈开发中的WebSocket实时通信实践
【10月更文挑战第3天】随着移动互联网的发展,小程序因便捷的用户体验和社交传播能力,成为企业拓展业务的新渠道。本文探讨了小程序全栈开发中的WebSocket实时通信实践,包括其实时通信、长连接及双向通信的特点,并通过实时聊天、推送、游戏和监控等功能的实现,展示了WebSocket在小程序中的应用。开发者需注意安全性、性能及兼容性等问题,以保障小程序的稳定运行和用户体验。
41 7
|
2月前
|
JavaScript 前端开发 开发工具
五子棋小游戏(JS+Node+Websocket)可分房间对战
本文介绍了通过JS、Node和WebSocket实现的五子棋游戏,支持多人在线对战和观战功能。
36 1
五子棋小游戏(JS+Node+Websocket)可分房间对战
|
27天前
|
Web App开发 JavaScript API
构建高效后端系统:Node.js与Express框架的实践之路
【9月更文挑战第37天】在数字化时代的浪潮中,后端开发作为技术架构的核心,承载着数据处理和业务逻辑的重要职责。本文将深入探讨如何利用Node.js及其强大的Express框架来搭建一个高效、可扩展的后端系统。我们将从基础概念讲起,逐步引导读者理解并实践如何设计、开发和维护一个高性能的后端服务。通过实际代码示例和清晰的步骤说明,本文旨在为初学者和有经验的开发者提供一个全面的指南,帮助他们在后端开发的旅途上走得更远。
37 3
|
2月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
187 7
Jmeter实现WebSocket协议的接口测试方法
|
18天前
|
消息中间件 网络协议 安全
C# 一分钟浅谈:WebSocket 协议应用
【10月更文挑战第6天】在过去的一年中,我参与了一个基于 WebSocket 的实时通信系统项目,该项目不仅提升了工作效率,还改善了用户体验。本文将分享在 C# 中应用 WebSocket 协议的经验和心得,包括基础概念、C# 实现示例、常见问题及解决方案等内容,希望能为广大开发者提供参考。
61 0
|
21天前
|
JavaScript 前端开发 API
Node.js 中的 WebSocket 底层实现
Node.js 中的 WebSocket 底层实现
36 0
|
21天前
|
Web App开发 JSON JavaScript
深入浅出:Node.js后端开发入门与实践
【10月更文挑战第4天】在这个数字信息爆炸的时代,了解如何构建一个高效、稳定的后端系统对于开发者来说至关重要。本文将引导你步入Node.js的世界,通过浅显易懂的语言和逐步深入的内容组织,让你不仅理解Node.js的基本概念,还能掌握如何使用它来构建一个简单的后端服务。从安装Node.js到实现一个“Hello World”程序,再到处理HTTP请求,文章将带你一步步走进Node.js的大门。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往后端开发新世界的大门。
|
2月前
|
机器学习/深度学习 自然语言处理 网络协议
为什么ChatGPT采用SSE协议而不是WebSocket?
在探讨大型语言模型ChatGPT的技术实现时,一个引人注目的细节是其选择使用SSE(Server-Sent Events)协议而非WebSocket来实现数据的实时推送。这一选择背后,蕴含着对技术特性、应用场景及资源效率的深思熟虑。本文将深入探讨ChatGPT为何偏爱SSE,以及这一决策背后的技术逻辑。
151 2
|
3月前
|
JavaScript 前端开发 API
深入浅出:使用Node.js搭建RESTful API的实践之旅
【8月更文挑战第31天】本文将带你踏上一次Node.js的探险之旅,通过实际动手构建一个RESTful API,我们将探索Node.js的强大功能和灵活性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的实践经验和深刻的技术洞见。
|
3月前
|
存储 缓存 JavaScript
深入Node.js身份验证:策略与实践
【8月更文挑战第20天】
49 4