实时通讯 nodejs-websocket 模块使用介绍
Socket 是一种网络通信协议,一般用来进行实时通信会使用到,而我们的聊天室就可以使用 Websocket 来进行通信,比起传统的轮询更加高效和节约资源。
nodejs-websocket 是一个 NodeJS 模块,用于创建 Websocket 的客户端和服务端。
使用方法
使用命令行安装模块
npm install nodejs-websocket
创建一个 websocket 的服务:
var ws = require("nodejs-websocket") // Scream server example: "hi" -> "HI!!!" var server = ws.createServer(function (conn) { console.log("New connection") conn.on("text", function (str) { console.log("Received "+str) conn.sendText(str.toUpperCase()+"!!!") }) conn.on("close", function (code, reason) { console.log("Connection closed") }) }).listen(8001)
文档地址:https://www.npmjs.com/package/nodejs-websocket
node 创建的 websocket 服务,主要包含三个概念
WS: 引入 nodejs-websocket
后的主要对象
- ws.createServer([options], [callback]):创建一个 server 对象
- ws.connect(URL, [options], [callback]):创建一个 connect 对象,一般由客户端链接服务端 websocket 服务时创建
- ws.setBinaryFragmentation(bytes):设置传输二进制文件的最小尺寸,默认 512kb
- setMaxBufferLength:设置传输二进制文件的最大尺寸,默认 2M
Server:通过 ws.createServer 创建
Function
- server.listen(port, [host], [callback]): 传入端口和主机地址后,开启一个 websocket 服务
- server.close([callback]): 关闭 websocket 服务
- server.connections: 返回包含所有 connection 的数组,可以用来广播所有消息
// 服务端广播 function broadcast(server, msg) { server.connections.forEach(function(conn) { conn.sendText(msg) }) }
Event
可以通过server.on('event', (res) => {console.log(res)})
调用
- Event: 'listening()':调用
server.listen
会触发当前事件 - Event: 'close()': 当服务关闭时触发该事件,如果有任何一个 connection 保持链接,都不会触发该事件
- Event: 'error(errObj)':发生错误时触发,此事件后会直接调用 close 事件
- Event: 'connection(conn)':建立新链接(完成握手后)触发,conn 是连接的实例对象
Connection:每一个客户端创建连接时的实例
Function
- connection.sendText(str, [callback]):发送字符串给另一侧,可以由服务端发送字符串数据给客户端
- connection.beginBinary():要求连接开始传输二进制,返回一个
WritableStream
- connection.sendBinary(data, [callback]): 发送一个二进制块,类似
connection.beginBinary().end(data)
- connection.send(data, [callback]): 发送一个字符串或者二进制内容到客户端,如果发送的是文本,类似于
sendText()
,如果发送的是二进制,类似于sendBinary()
,callback
将监听发送完成的回调 - connection.close([code, [reason]]):开始关闭握手(发送一个关闭指令)
- connection.server:如果服务是 nodejs 启动,这里会保留 server 的引用
- connection.readyState:一个常量,表示连接的当前状态
connection.CONNECTING:值为 0,表示正在连接
connection.OPEN:值为 1,表示连接成功,可以通信了
connection.CLOSING:值为 2,表示连接正在关闭。
connection.CLOSED:值为 3,表示连接已经关闭,或者打开连接失败。
- connection.outStream: 存储
connection.beginBinary()
返回的OutStream
对象,没有则返回 null - connection.path:表示建立连接的路径
- connection.headers:只读请求头的 name 的 value 对应的 object 对象
- connection.protocols:客户端请求的协议数组,没有则返回空数组
- connection.protocol:同意连接的协议,如果有这个协议,它会包含在
connection.protocols
数组里面
Event
- Event: 'close(code, reason)': 连接关闭时触发
- Event: 'error(err)':发生错误时触发,如果握手无效,也会发出响应
- Event: 'text(str)':收到文本时触发,str 时收到的文本字符串
- Event: 'binary(inStream)':收到二进制内容时触发,
inStream
时一个ReadableStream
var server = ws.createServer(function(conn) { console.log('New connection') conn.on('binary', function(inStream) { // 创建空的buffer对象,收集二进制数据 var data = new Buffer(0) // 读取二进制数据的内容并且添加到buffer中 inStream.on('readable', function() { var newData = inStream.read() if (newData) data = Buffer.concat([data, newData], data.length + newData.length) }) inStream.on('end', function() { // 读取完成二进制数据后,处理二进制数据 process_my_data(data) }) }) conn.on('close', function(code, reason) { console.log('Connection closed') }) }).listen(8001)
- Event: 'connect()':连接完全建立后发出
const ws = require('nodejs-websocket') // 可以通过不同的code可以表示要后端实现的不同逻辑 const { RECEIEVE_MESSAGE, SAVE_USER_INFO, CLOSE_CONNECTION } = require('../constants/config') // 当前聊天室的用户 let chatUsers = [] // 广播通知 const broadcast = (server, info) => { console.log('broadcast', info) server.connections.forEach(function(conn) { conn.sendText(JSON.stringify(info)) }) } // 服务端获取到某个用户的信息通知到所有用户 const broadcastInfo = (server, info) => { let count = server.connections.length let result = { code: RECEIEVE_MESSAGE, count: count, ...info } broadcast(server, result) } // 返回当前剩余的在线用户 const sendChatUsers = (server, user) => { let chatIds = chatUsers.map(item => item.chatId) if (chatIds.indexOf(user.chatId) === -1) { chatUsers.push(user) } let result = { code: SAVE_USER_INFO, count: chatUsers.length, chatUsers: chatUsers } broadcast(server, result) } // 触发关闭连接,在离开页面或者关闭页面时,需要主动触发关闭连接 const handleCloseConnect = (server, user) => { chatUsers = chatUsers.filter(item => item.chatId !== user.chatId) let result = { code: CLOSE_CONNECTION, count: chatUsers.length, chatUsers: chatUsers } console.log('handleCloseConnect', user) broadcast(server, result) } // 创建websocket服务 const createServer = () => { let server = ws.createServer(connection => { connection.on('text', function(result) { let info = JSON.parse(result) let code = info.code if (code === CLOSE_CONNECTION) { handleCloseConnect(server, info) // 某些情况如果客户端多次触发连接关闭,会导致connection.close()出现异常,这里try/catch一下 try { connection.close() } catch (error) { console.log('close异常', error) } } else if (code === SAVE_USER_INFO) { sendChatUsers(server, info) } else { broadcastInfo(server, info) } }) connection.on('connect', function(code) { console.log('开启连接', code) }) connection.on('close', function(code) { console.log('关闭连接', code) }) connection.on('error', function(code) { // 某些情况如果客户端多次触发连接关闭,会导致connection.close()出现异常,这里try/catch一下 try { connection.close() } catch (error) { console.log('close异常', error) } console.log('异常关闭', code) }) }) // 所有连接释放时,清空聊天室用户 server.on('close', () => { chatUsers = [] }) return server } const server = createServer() module.exports = server
创建WebSocket
连接后,在onopen
事件触发时,初始化用户的一些信息,比如每个用户包含唯一的chatId
之类的,以及保持用户昵称,用户头像啥的,再就是监听onmessage
事件,通过后端返回的 message 信息执行对应的操作,建议前后端约定一些 code 来表示某一种类似的 message 信息,然后就是监听页面的一些触发事件,将信息通过send
方法发送给服务端。
let websocket = new WebSocket(wsConfig.WS_ROOT_PATH) websocket.onopen = () => { console.log('websocket连接开启...') if (!this.chatId) { this.initChatId() } this.sendUserName() } websocket.onmessage = event => { let data = event.data let result = JSON.parse(data) let code = result.code let count = result.count this.updateChatCount(count) if (code === RECEIEVE_MESSAGE) { this.pushMessage(result) this.onMessageScroll() } else if (code === SAVE_USER_INFO || code === CLOSE_CONNECTION) { this.updateChatUser(result.chatUsers) } console.log('数据已接收...', code, result) } websocket.onclose = this.onWebsocketClose websocket.onerror = this.onWebsocketError // 发送message sendMessage(info) { if (this.websocket && typeof this.websocket.send === 'function') { this.websocket.send(JSON.stringify(info)) } }
如果浏览器进入其它页面或者关闭浏览器,链接会异常关闭,经常会导致后端出现异常报错,所以可以按照下面的方式处理:
// 前端代码监听页面关闭或者刷新 window.onunload = () => { this.closeConnect() } // vue里跳转到其它页面 beforeRouteLeave(to, from, next) { this.closeConnect() next() }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论