> ## Documentation Index
> Fetch the complete documentation index at: https://bun.ll1025.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 在 WebSocket 上设置每个套接字的上下文数据

WebSocket 服务器通常需要为每个连接的客户端存储一些标识信息或上下文。

使用 [Bun.serve()](/runtime/http/websockets#contextual-data)，通过在升级连接时向 `server.upgrade()` 传递 `data` 参数来设置此"上下文数据"。

```ts server.ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
Bun.serve({
  fetch(req, server) {
    const success = server.upgrade(req, {
      data: {
        socketId: Math.random(),
      },
    });
    if (success) return undefined;

    // 正常处理 HTTP 请求
    // ...
  },
  websocket: {
    // TypeScript：像这样指定 ws.data 的类型
    data: {} as { socketId: number },

    // 定义 websocket 处理程序
    async message(ws, message) {
      // 上下文数据作为 `data` 属性存在于 WebSocket 实例上
      console.log(`从 ${ws.data.socketId} 收到：${message}`);
    },
  },
});
```

***

通常的做法是从传入请求中读取 cookie/头部信息来识别连接的客户端。

```ts server.ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
type WebSocketData = {
  createdAt: number;
  token: string;
  userId: string;
};

Bun.serve({
  async fetch(req, server) {
    // 使用某个库解析 cookie
    const cookies = parseCookies(req.headers.get("Cookie"));
    const token = cookies["X-Token"];
    const user = await getUserFromToken(token);

    const upgraded = server.upgrade(req, {
      data: {
        createdAt: Date.now(),
        token: cookies["X-Token"],
        userId: user.id,
      },
    });

    if (upgraded) return undefined;
  },
  websocket: {
    // TypeScript：像这样指定 ws.data 的类型
    data: {} as WebSocketData,

    async message(ws, message) {
      // 将消息保存到数据库
      await saveMessageToDatabase({
        message: String(message),
        userId: ws.data.userId,
      });
    },
  },
});
```
