WebSocket
WebSocket 是一种双向实时通信协议,允许客户端和服务器建立持久连接,以实时传递消息和数据
搭建 WS 服务
NodeJs 中,可以使用 websockets/ws 搭建 WebSocket 服务端
$ npm i ws
通过上面的代码,通过搭建 WebSocket 服务,实现了服务端和客户端的通信,这非常不错
但是代入实际场景中,我们会发现另一个问题,在我们已有的 HTTP 服务上添加 webSocket 服务似乎似乎有一些弊端:比如需要开启额外端口,对于开放防火墙端口和代理都不友好
WS over HTTP
但是代入实际场景中,我们会发现另一个问题,在我们已有的 HTTP 服务上添加 webSocket 服务似乎似乎有一些弊端:比如需要开启额外端口,对于开放防火墙端口和代理都不友好
WS 是基于 HTTP 的,所以实际上我们有更好的方式,就是 WebSocket over HTTP。这种行为允许在相同的端口上同时提供 HTTP 和 WebSocket 服务
比如我们留两个 HTTP 接口 __api_1__、__api_2__,用于连接不同的 WS 服务:
比如我们留两个 HTTP 接口 __api_1__、__api_2__,用于连接不同的 WS 服务:
import { WebSocketServer} from 'ws'
import { createServer } from 'http'
const server = createServer()
const wss1 = new WebSocketServer({ noServer: true })
const wss2 = new WebSocketServer({ noServer: true })
wss1.on('connection', (ws) => {
//...
})
wss2.on('connection', (ws) => {
//...
})
// 有 WS 请求来的时候,upgrade 被触发
server.on('upgrade', (req, socket, head) => {
const { pathname } = new URL(req.url || '/', 'http://localhost')
// 当请求路径是 __api_1__ 的时候,升级为 ws 服务,让 wss1 去处理
if(pathname === '/__api_1__') {
wss1.handleUpgrade(req, socket, head, ws => {
wss.emit('connection', ws, req)
})
// 当请求路径是 __api_2__ 的时候,升级为 ws 服务,让 wss2 去处理
} else if(pathname === '/__api_2__') {
wss2.handleUpgrade(req, socket, head, ws => {
wss.emit('connection', ws, req)
})
}
})
server.listen(3000)对应的客户端代码只需要加一个路径即可
参考 Vite 当中,对于 HMR 服务的实现,也是采用类似上面的方式:
vitejs/vite/packages/vite/src/node/server/ws.ts
Lines 112 to 141 #587ad7b
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
const wsServer = hmrServer || (portsAreCompatible && server)
let hmrServerWsListener: (
req: InstanceType<typeof IncomingMessage>,
socket: Duplex,
head: Buffer,
) => void
const customListeners = new Map<string, Set<WebSocketCustomListener<any>>>()
const clientsMap = new WeakMap<WebSocketRaw, WebSocketClient>()
const port = hmrPort || 24678
const host = (hmr && hmr.host) || undefined
if (wsServer) {
let hmrBase = config.base
const hmrPath = hmr ? hmr.path : undefined
if (hmrPath) {
hmrBase = path.posix.join(hmrBase, hmrPath)
}
wss = new WebSocketServerRaw({ noServer: true })
hmrServerWsListener = (req, socket, head) => {
if (
req.headers['sec-websocket-protocol'] === HMR_HEADER &&
req.url === hmrBase
) {
wss.handleUpgrade(req, socket as Socket, head, (ws) => {
wss.emit('connection', ws, req)
})
}
}
wsServer.on('upgrade', hmrServerWsListener)
} else {