远程通信方式
通信方式:
- Stdio: 推荐,高效、简洁、本地
- Streamable HTTP: 远程
前置知识
SSE 全称 Server-Sent Events,中文是“服务器发送事件”。是一种基于 HTTP 的单向通信协议,由浏览器发起连接,服务器可以持续不断地向客户端推送数据。
你可以把它想象成:“浏览器打开一个通道,然后服务器不断地往里面发消息。”
SSE 特点
- 协议:基于 HTTP(长连接)
- 方向:单向:服务器 -> 客户端
- 格式:文本流,内容类型为
text/event-stream
- 浏览器支持:所有现代浏览器都支持(IE 除外)
- 应用场景:单方面需要推送的时候。实时通知、消息流、状态更新、股票/天气数据等
消息格式
SSE 协议规定,服务器以 text/event-stream
格式不断推送消息,每条消息格式如下:
event: 事件名 # 可选,默认是 message 事件
id: 唯一ID # 可选
retry: 3000 # 客户端断线重连间隔,单位毫秒,可选
data: 内容 # 必需,可以多行
每条消息用空行 \n\n
作为结尾。
事件类型
如果服务器发送的数据中没有指定事件类型,浏览器端会将其作为默认事件类型 message
来处理:
data: 这是默认消息(data 代表要发送的消息)
客户端监听方式:
eventSource.addEventListener("message", (e) => {console.log("默认事件:", e.data);
});
可以自定义事件名:使用 event:
字段
event: update(事件名)
data: 新的更新内容
客户端监听方式:
eventSource.addEventListener("update", (e) => {console.log("收到 update 事件:", e.data);
});
课堂练习
SSE 服务器推送信息示例
import express from "express";
import { watch } from "chokidar";
import { join } from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);const app = express();const clients = new Set(); // 存储所有的客户端连接app.use(express.static(join(__dirname, "public")));app.get("/mcp", (req, res) => {res.setHeader("Content-Type", "text/event-stream");res.setHeader("Cache-Control", "no-cache");res.setHeader("Connection", "keep-alive");// 假设现在客户端连接过来,那么这里就给客户端推送一个消息res.write("event:connected\n"); // 事件名res.write("data:你已经连接上SSE服务器\n\n"); // 推送的数据clients.add(res);// 在客户端断开连接的时候,会触发 close 事件req.on("close", () => {clients.delete(res);});
});// 监听目录
const watcher = watch(join(__dirname, "watched"), {persistent: true,ignoreInitial: true,
});// 需要在目录发生变化的时候,通知所有的客户端
watcher.on("all", (event, path) => {// 将监听到的变化的信息,通知所有的客户端// 1. 组装要发送给客户端的信息const payload = JSON.stringify({event, // 当前资源发生变化 1. 新增 add 2. 删除 unlink 3. 修改 changepath, // 文件的路径time: new Date().toLocaleString(),});// 2. 通知所有的客户端for (const client of clients) {client.write(`event: resource_changed\n`);client.write(`data: ${payload}\n\n`);}console.log(`【发生了变更】${event} -> ${path}`);
});app.listen(3000, () => {console.log(`服务器已启动, 监听3000端口`);
});