Skip to content
0

WebSocket

WebSocket 是一种双向实时通信协议,允许客户端和服务器建立持久连接,以实时传递消息和数据

搭建 WS 服务

NodeJs 中,可以使用 websockets/ws 搭建 WebSocket 服务端

$ npm i ws

const client = new WebSocket('ws://localhost:3000')

client.addEventListener('open', ()=> {
    console.log('Client open')
    client.send('Hello server')
})

client.addEventListener('message', event => {
    console.log('Client receive: ', event)
})

client.addEventListener('close', () => {
    console.log('Client close')
})
import { WebSocketServer }  from 'ws'

const server = new WebSocketServer({ port: 3000 })

server.on('connection', (socket) => {
    console.log('connection')
    socket.on('message', msg => {
        console.log('Server recive: ', msg.toString())
        socket.send('server over')
    })

    socket.on('close', () => {
        console.log('close');
    })
})
<body>
    <script src="./client.ts" type="module"></script>
</body>
$ tsx server.ts
$ vite 
Greeting: Hello World

通过上面的代码,通过搭建 WebSocket 服务,实现了服务端和客户端的通信,这非常不错

但是代入实际场景中,我们会发现另一个问题,在我们已有的 HTTP 服务上添加 webSocket 服务似乎似乎有一些弊端:比如需要开启额外端口,对于开放防火墙端口和代理都不友好

WS over HTTP

WS 是基于 HTTP 的,所以实际上我们有更好的方式,就是 WebSocket over HTTP。这种行为允许在相同的端口上同时提供 HTTP 和 WebSocket 服务

import { WebSocketServer}  from 'ws'
import { createServer } from 'http'

const server = createServer()
const wss = new WebSocketServer({ server })

wss.on('connection', (ws) => {
    console.log('connection')
    ws.on('message', msg => {
        console.log('Server recive: ', msg.toString())
        ws.send('anything')
    })

    ws.on('close', () => {
        console.log('close');
    })
})

server.listen(3000)

这样就实现了一个简易的 WS over HTTP,但是还不够,上面的效果上和我们直接创建 WS 服务没啥区别,所以让我们稍微改一下,基于不同请求路径去响应不同的 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)

对应的客户端代码只需要加一个路径即可

- const client = new WebSocket('ws://localhost:3000')
+ const client1 = new WebSocket('ws://localhost:3000/__api_1__')
+ const client2 = new WebSocket('ws://localhost:3000/__api_2__')

这种方式的好处就是,可以启动任意多个 WS 服务在一个端口上,只需要设置好对应的请求路径即可

参考 Vite 当中,对于 HMR 服务的实现,也是采用类似上面的方式:

Released under the MIT License.