# websocket * [WebSocket:5分钟从入门到精通](https://juejin.cn/post/6844903544978407431) > 支持双向通信 使用场景: * 大屏展示 * 日志监控 * 即时聊天 * 通知系统 服务端向客户端单向通讯: sse ## 语法 ### 创建 ```js const ws = new WebSocket(url[, protocols]) ``` ```js const uid = 123 const url = 'localhost:3000' const ws = new WebSocket(`ws://${url}/ws/${uid}`) ``` ### 属性 * readyState: 链接状态 * 0: 正在尝试建立连接 * 1: 已链接, 可以通讯 * 2: 正在关闭 * 3: 已关闭或无法打开 * url: 绝对路径 ### 事件 > 通过 addEventListener() 监听 * open * message * error * close ```js ws.addEventListener('open', handleOpen) ws.addEventListener('message', handleMessage) ws.addEventListener('error', handleError) ws.addEventListener('close', handleClose) ``` ### 方法 * send * close ```js ws.send('hello') ws.close() ``` ## 示例 ### 前端 ```html Document
连接状态: 未连接
``` ### 后端 ```js 'use strict' const fastify = require('fastify')() // 注册 WebSocket 插件 fastify.register(require('@fastify/websocket'), { options: { maxPayload: 1048576 }, }) // 添加连接映射表 const connections = new Map() // 注册 WebSocket 路由 fastify.register(async function (fastify) { fastify.get('/ws/:uid', { websocket: true }, (socket, req) => { const userId = req.params.uid console.log(`用户 ${userId} 已连接`) // 存储连接 connections.set(userId, socket) socket.send(`欢迎用户 ${userId}`) socket.on('message', message => { console.log(`收到来自用户 ${userId} 的消息:`, message.toString()) socket.send(`服务器已收到消息: ${message}`) }) socket.on('close', () => { console.log(`用户 ${userId} 断开连接`) connections.delete(userId) socket.close() }) }) fastify.get('/send', (req, reply) => { const { msg, uid } = req.query if (!msg || !uid) { return reply.code(400).send({ success: false, message: '缺少必要参数,请确保提供 msg 和 uid', example: '/send?msg=你的消息&uid=用户ID', }) } console.log(`尝试发送消息给用户 ${uid}: ${msg}`) const userSocket = connections.get(uid) if (userSocket && userSocket.readyState === 1) { userSocket.send(msg) reply.send({ success: true, message: `消息已发送给用户 ${uid}: ${msg}` }) } else { reply.code(404).send({ success: false, message: `用户 ${uid} 不在线或连接已断开`, }) } }) }) // 直接在外部注册 `send` 路由 fastify.listen({ port: 3003, host: '0.0.0.0' }, err => { if (err) { fastify.log.error(err) process.exit(1) } console.log('WebSocket 服务器运行在端口 3003') }) fastify.ready(err => { // 打印所有路由 if (err) throw err console.log(fastify.printRoutes()) }) ``` ## SSE (Server-Sent Events) 1. **客户端请求**:客户端通过发送一个HTTP请求到服务器,请求建立SSE连接。这个请求通常是一个`GET`请求,并且包含一个特殊的`Accept`头,表明客户端希望接收`text/event-stream`类型的数据。 2. **服务器响应**:服务器接收到请求后,会保持连接打开,并开始发送事件流。服务器发送的数据格式是 `text/event-stream`,每个事件由一行或多行文本组成,以 `\n\n`(两个换行符)结束。 3. **事件格式**:每个事件可以包含以下字段: * `data:`:事件的数据内容,可以是一行或多行。 * `id:`:事件的唯一标识符,用于断线重连时指定从哪个事件开始接收。 * `event:`:事件的类型,客户端可以根据这个字段来处理不同类型的事件。 * `retry:`:指定客户端在连接断开后重新连接的时间间隔(毫秒)。 4. **客户端处理**:客户端通过`EventSource`对象来接收和处理服务器发送的事件。`EventSource`会自动处理连接的重连和事件的解析。 ### 服务端 ```js const http = require('http'); http.createServer((req, res) => { if (req.url === '/events') { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*' }); let eventId = 0; setInterval(() => { res.write(`id: ${eventId}\n`); res.write(`data: ${JSON.stringify({ message: 'Hello, world!', time: new Date() })}\n\n`); eventId++; }, 1000); } else { res.writeHead(404); res.end(); } }).listen(3000, () => { console.log('Server running at http://localhost:3000'); }); ``` ### 客户端 ```html SSE Example

Server-Sent Events Example

```