前端架构师破局技能,NodeJS 落地 WebSocket 实践
简介:专注前端工程与架构产出
来源:SegmentFault 思否社区
本文从网络协议,技术背景,安全和生产应用的方向,详细介绍 WebSocket 在 Node.js 中的落地实践。
大纲预览
本文介绍的内容包括以下方面:
-
网络协议进化 -
Socket.IO? -
ws 模块实现 -
Express 集成 -
WebSocket 实例 -
消息广播 -
安全与认证 -
BFF 应用
网络协议进化
-
请求消耗太大 。客户端不断请求,浪费流量和服务器资源,给服务器造成压力。 -
不能保证及时 。客户端需要平衡及时性和性能,请求间隔必然不能太小,因此会有延迟。
## 普通连接
http://localhost:80/test
## 安全连接
https://localhost:80/test
## 普通连接
ws://localhost:80/test
## 安全连接
wss://localhost:80/test
Socket.IO?
ws 模块实现
$ npm install ws
服务端
const { WebSocketServer } = require('ws')
const wss = new WebSocketServer({
port: 8080
})
wss.on('connection', (ws, req) => {
console.log('客户端已连接:', req.socket.remoteAddress)
ws.on('message', data => {
console.log('收到客户端发送的消息:', data)
})
ws.send('我是服务端') // 向当前客户端发送消息
})
$ node ws-server.js
客户端
var ws = new WebSocket('ws://localhost:8080')
ws.onopen = function(mevt) {
console.log('客户端已连接')
}
ws.onmessage = function(mevt) {
console.log('客户端收到消息: ' + evt.data)
ws.close()
}
ws.onclose = function(mevt) {
console.log('连接关闭')
}
const WebSocket = require('ws')
var ws = new WebSocket('ws://localhost:8080')
ws.on('open', () => {
console.log('客户端已连接')
})
ws.on('message', data => {
console.log('客户端收到消息: ' + data)
ws.close()
})
ws.on('close', () => {
console.log('连接关闭')
})
Express 集成
var expressWs = require('express-ws')(app)
app.ws('/test-ws', (ws, req) => {
ws.on('message', msg => {
ws.send(msg)
})
})
// websocket.js
var router = express.Router()
router.ws('/test-ws', (ws, req) => {
ws.on('message', msg => {
ws.send(msg)
})
})
module.exports = router
var express = require('express')
var app = express()
var wsServer = require('express-ws')(app)
var webSocket = require('./websocket.js')
app.ws('/test-ws', (ws, req) => {
ws.on('message', msg => {
ws.send(msg)
})
})
app.use('/websocket', webSocket)
app.listen(3000)
// 客户端的IP地址
req.socket.remoteAddress
// 连接参数
req.query
WebSocket 实例
var ws = new WebSocket('ws://localhost:8080')
app.ws('/test-ws', (ws, req) => {}
浏览器
{
binaryType: 'blob'
bufferedAmount: 0
extensions: ''
onclose: null
onerror: null
onmessage: null
onopen: null
protocol: ''
readyState: 3
url: 'ws://localhost:8080/'
}
-
onopen :连接建立后的函数 -
onmessage :收到服务端推送消息的函数 -
onclose :连接关闭的函数 -
onerror:连接异常的函数
ws.onmessage = mevt => {
console.log('消息:', mevt.data)
}
-
0 : 常量 WebSocket.CONNECTING ,表示正在连接 -
1 : 常量 WebSocket.OPEN ,表示已连接 -
2 : 常量 WebSocket.CLOSING ,表示正在关闭 -
3 : 常量 WebSocket.CLOSED ,表示已关闭
ws.send('要发送的信息')
服务端
// Node.js 环境
ws.onmessage = str => {
console.log('消息:', str)
}
ws.on('message', str => {
console.log('消息:', mevt.data)
})
消息广播
var wss = new WebSocketServer({ port: 8080 })
// 获取所有已连接客户端
wss.clients
var wsServer = expressWebSocket(app)
var wss = wsServer.getWss()
// 获取所有已连接客户端
wss.clients
wss.clients.size
wss.clients.forEach(client => {
if (client.readyState === 1) {
client.send('广播数据')
}
})
安全与认证
-
Token 连接认证 -
wss 支持
Token 连接认证
var server = http.createServer()
var wss = new WebSocketServer({ noServer: true })
server.listen(8080)
server.on('upgrade', (request, socket) => {
// 用 request 获取参数做验证
// 1. 验证不通过判断
if ('验证失败') {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
socket.destroy()
return
}
// 2. 验证通过,继续建立连接
wss.handleUpgrade(request, socket, _, ws => {
wss.emit('connection', ws, request)
})
})
// 3. 监听连接
wss.on('connection', (ws, request) => {
console.log('客户端已连接')
ws.send('服务端信息')
})
-
Basic Auth -
Quary 传参
var ws = new WebSocket('ws://ruims:123456@localhost:8080')
wss.on('connection', (ws, req) => {
if(req.headers['authorization']) {
let auth = req.headers['authorization']
console.log(auth)
// 打印的值:Basic cnVpbXM6MTIzNDU2
}
}
var ws = new WebSocket('ws://localhost:8080?token=cnVpbXM6MTIzNDU2')
wss.on('connection', (ws, req) => {
console.log(req.query.token)
}
wss 支持
location /websocket {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
}
var ws = new WebSocket('wss://[host]/websocket')
BFF 应用
BFF 或许你听说过,全称是 Backend For Frontend,意思是为前端服务的后端,在实际应用架构中属于前端和后端的一个 中间层。
-
查看当前在线人数,在线用户信息 -
登录新设备,其他设备退出登录 -
检测网络连接/断开 -
站内消息,小圆点提示
源码+答疑
- END -