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

# UDP

> 使用 Bun 的 UDP API 实现具有高级实时需求的服务，例如语音聊天。

## 绑定一个 UDP 套接字 (`Bun.udpSocket()`)

要创建一个新的（绑定的）UDP 套接字：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({});
console.log(socket.port); // 由操作系统分配
```

指定端口：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({
  port: 41234, // [!code ++]
});

console.log(socket.port); // 41234
```

### 发送数据报

指定要发送的数据，以及目标端口和地址。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
socket.send("Hello, world!", 41234, "127.0.0.1");
```

注意，地址必须是有效的 IP 地址 —— `send` 不执行 DNS 解析，因为它旨在用于低延迟操作。

### 接收数据报

创建套接字时，添加回调以指定接收数据包时应执行的操作：

```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const server = await Bun.udpSocket({
  socket: {
    data(socket, buf, port, addr) {
      console.log(`来自 ${addr}:${port} 的消息:`);
      console.log(buf.toString());
    },
  },
});

const client = await Bun.udpSocket({});
client.send("Hello!", server.port, "127.0.0.1");
```

### 连接

虽然 UDP 没有连接的概念，但许多 UDP 通信（尤其是作为客户端时）只涉及一个对等方。
在这种情况下，将套接字“连接”到该对等方可能会带来好处，这指定了所有数据包发送的地址，
并限制只接收来自该对等方的数据包。

```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const server = await Bun.udpSocket({
  socket: {
    data(socket, buf, port, addr) {
      console.log(`来自 ${addr}:${port} 的消息:`);
      console.log(buf.toString());
    },
  },
});

const client = await Bun.udpSocket({
  connect: {
    port: server.port,
    hostname: "127.0.0.1",
  },
});

client.send("Hello");
```

由于连接是在操作系统层面实现的，你也可能观察到性能上的提升。

### 使用 `sendMany()` 一次发送多个数据包

如果你想一次发送大量数据包，合并发送可以避免为每个包调用系统调用的开销。
这可以通过 `sendMany()` API 实现：

对于未连接套接字，`sendMany` 接受一个数组作为唯一参数。每三项描述一个数据包：
第一项是要发送的数据，第二项是目标端口，第三项是目标地址。

```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({});

// 一次操作中发送 'Hello' 到 127.0.0.1:41234，以及 'foo' 到 1.1.1.1:53
socket.sendMany(["Hello", 41234, "127.0.0.1", "foo", 53, "1.1.1.1"]);
```

With a connected socket, `sendMany` takes an array, where each element represents the data to be sent to the peer.

```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({
  connect: {
    port: 41234,
    hostname: "localhost",
  },
});

socket.sendMany(["foo", "bar", "baz"]);
```

`sendMany` 返回成功发送的数据包数。与 `send` 一样，`sendMany` 只接受有效的 IP 地址作为目标，
因为它不执行 DNS 解析。

### 处理背压

可能会发生你发送的数据包无法放入操作系统的数据包缓冲区。当出现以下情况时，你可以检测到这种情况：

* `send` 返回 `false`
* `sendMany` 返回的数字小于你指定的数据包数量。在这种情况下，一旦套接字变得可写，
  `drain` 套接字处理程序会被调用：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({
  socket: {
    drain(socket) {
      // 继续发送数据
    },
  },
});
```

### 套接字选项

UDP 套接字支持设置各种套接字选项：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({});

// 启用广播，发送数据包到广播地址
socket.setBroadcast(true);

// 设置出站数据包的 IP TTL（生存时间）
socket.setTTL(64);
```

### 多播

Bun 支持 UDP 套接字的多播操作。使用 `addMembership` 和 `dropMembership` 加入和离开多播组：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const socket = await Bun.udpSocket({});

// 加入多播组
socket.addMembership("224.0.0.1");

// 指定接口加入多播组
socket.addMembership("224.0.0.1", "192.168.1.100");

// 离开多播组
socket.dropMembership("224.0.0.1");
```

其他多播选项：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
// 设置多播包的 TTL（网络跳数）
socket.setMulticastTTL(2);

// 控制多播包是否回环到本地套接字
socket.setMulticastLoopback(true);

// 指定用于出站多播包的接口
socket.setMulticastInterface("192.168.1.100");
```

对于源特定多播（SSM），使用 `addSourceSpecificMembership` 和 `dropSourceSpecificMembership`：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
socket.addSourceSpecificMembership("10.0.0.1", "232.0.0.1");
socket.dropSourceSpecificMembership("10.0.0.1", "232.0.0.1");
```
