搞懂浏览器和服务器之间是怎么实时传递消息的。这是聊天界面、实时邮件更新、看板刷新的基础。

第6课:WebSocket 实时通信 —— 前后端怎么"打电话"

本课目标

搞懂浏览器和服务器之间是怎么实时传递消息的。这是聊天界面、实时邮件更新、看板刷新的基础。


HTTP vs WebSocket

你可能知道 HTTP——浏览器向服务器发请求,服务器回应。但 HTTP 有个问题:服务器不能主动给浏览器发消息

code
HTTP 模式(传统):
  浏览器: "有新邮件吗?"
  服务器: "没有"
  (1秒后)
  浏览器: "有新邮件吗?"
  服务器: "没有"
  (1秒后)
  浏览器: "有新邮件吗?"
  服务器: "有!给你"
  ← 这叫"轮询",浪费资源且有延迟

WebSocket 模式(本项目):
  浏览器: "建立 WebSocket 连接"
  服务器: "OK,通道已开"
  (什么都不做,安静等待)
  服务器: "嘿!新邮件来了,给你数据"   ← 服务器主动推送!
  浏览器: "收到"
  服务器: "AI 回复来了,给你"           ← 又一条推送

WebSocket = 一根持续保持的双向通道。建立连接后,双方都能随时发消息。


在邮件助手里,WebSocket 传什么?

服务器 → 浏览器(推送)

消息类型 什么时候发 携带什么数据
inbox_update 邮件列表变化 最新邮件列表
assistant_message AI 生成回复 文字内容
tool_use AI 在调用工具 工具名和参数
tool_result 工具执行完成 执行结果
result 对话结束 会话 ID
listener_notification 监听器被触发 触发详情
ui_state_update 看板数据变化 新的数据
component_instance 需要渲染组件 组件类型和数据
listeners_update 监听器列表变化 活跃监听器列表

浏览器 → 服务器(请求)

消息类型 什么时候发 携带什么数据
chat 用户发送聊天消息 文字内容
subscribe 页面加载时 会话 ID
request_inbox 打开收件箱标签
component_action 用户点击看板按钮 操作类型和参数

消息流实例:聊天过程

当你在聊天框输入"帮我找上周的发票",数据是这样流动的:

sequenceDiagram participant B as 浏览器 participant S as 服务器 participant AI as Claude Agent SDK B->>S: { type: "chat", content: "帮我找上周的发票" } S->>AI: 传给 AI 处理 AI-->>S: tool_use: email_search S-->>B: { type: "tool_use", name: "email_search" }<br>告诉前端: AI 正在搜索邮件 Note over S: 工具执行搜索 S-->>B: { type: "tool_result", result: [...] }<br>搜索结果返回 AI-->>S: 整理答案 S-->>B: { type: "assistant_message", content: "我找到了3封发票邮件..." } S-->>B: { type: "result", session_id: "abc123" }<br>这轮对话结束

前端根据不同的消息类型,实时更新界面——显示"AI 正在搜索..."、显示搜索结果、显示最终回复。


前端怎么处理 WebSocket?

位置:client/hooks/useWebSocket.ts

code
// 简化版
function useWebSocket() {
    const [messages, setMessages] = useState([]);
    const ws = useRef(null);

    useEffect(() => {
        // 建立连接
        ws.current = new WebSocket('ws://localhost:3000/ws');

        // 收到消息时
        ws.current.onmessage = (event) => {
            const data = JSON.parse(event.data);

            switch (data.type) {
                case 'assistant_message':
                    // 把 AI 回复加到聊天列表
                    setMessages(prev => [...prev, {
                        role: 'assistant',
                        content: data.content
                    }]);
                    break;

                case 'inbox_update':
                    // 刷新收件箱
                    refreshInbox(data.emails);
                    break;

                case 'ui_state_update':
                    // 更新看板数据
                    updateState(data.stateId, data.data);
                    break;

                case 'listener_notification':
                    // 显示通知
                    showNotification(data.message);
                    break;
            }
        };
    }, []);

    // 发送消息的方法
    const sendMessage = (content) => {
        ws.current.send(JSON.stringify({
            type: 'chat',
            content: content
        }));
    };

    return { messages, sendMessage };
}

后端怎么管理 WebSocket?

位置:ccsdk/websocket-handler.ts

后端需要处理多个客户端同时连接的情况:

code
// 简化版
class WebSocketHandler {
    private clients = new Set();  // 所有连接的客户端

    handleConnection(ws) {
        this.clients.add(ws);

        ws.on('message', (data) => {
            const msg = JSON.parse(data);

            if (msg.type === 'chat') {
                // 用户发了聊天消息 → 传给 AI
                this.handleChat(msg.content, ws);
            }
        });

        ws.on('close', () => {
            this.clients.delete(ws);
        });
    }

    // 广播:给所有连接的客户端发送消息
    broadcast(message) {
        for (const client of this.clients) {
            client.send(JSON.stringify(message));
        }
    }
}

broadcast 很重要——当 Listener 在后台自动处理了一封邮件(比如贴了标签),它通过 broadcast 通知所有打开着浏览器的人,实时刷新界面。


为什么不全用 HTTP?

场景 用 HTTP 用 WebSocket
加载邮件列表 ✅ 合适 没必要
AI 聊天(流式回复) ❌ 等太久 ✅ 实时推送每个字
新邮件到达通知 ❌ 得不停轮询 ✅ 服务器主动推
看板数据实时更新 ❌ 手动刷新 ✅ 自动更新

这个 demo 的策略是:初始加载用 HTTP,后续更新用 WebSocket


本课小结

  1. WebSocket 是双向实时通道,服务器能主动给浏览器推消息
  2. 9种推送消息 + 4种请求消息 覆盖了所有场景
  3. 聊天过程是多步骤流式推送:工具调用 → 结果 → AI回复
  4. broadcast 让所有客户端实时同步
  5. HTTP 负责初始加载,WebSocket 负责后续更新

沿着当前专题继续,或返回课程目录重新整理阅读顺序。

返回课程目录