运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

UDP

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

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

要创建一个新的(绑定的)UDP 套接字:

TypeScript
const socket = await Bun.udpSocket({});
console.log(socket.port); // 由操作系统分配

指定端口:

TypeScript
const socket = await Bun.udpSocket({
  port: 41234, // [!code ++]
});

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

发送数据报

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

TypeScript
socket.send("Hello, world!", 41234, "127.0.0.1");

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

接收数据报

创建套接字时,添加一个 data 回调来处理传入的数据包:

server.ts
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 通信(尤其是客户端通信)只涉及一个对等方。 在这种情况下,你可以将套接字连接到该对等方,这会为你发送的每个数据包设置目标地址, 并将传入的数据包限制为仅接收来自该对等方的数据包。

server.ts
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() 对它们进行批处理。

对于未连接的套接字,sendMany 将数组作为唯一参数。数组中的每组三个元素描述一个数据包: 要发送的数据、目标端口和目标地址。

server.ts
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"]);

对于已连接的套接字,sendMany 接收一个数组,数组中的每个元素都是要发送给对等方的数据。

server.ts
const socket = await Bun.udpSocket({
  connect: {
    port: 41234,
    hostname: "localhost",
  },
});

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

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

处理背压

你发送的数据包可能无法放入操作系统的数据包缓冲区。当出现以下情况时,你可以检测到这一点:

  • send 返回 false
  • sendMany 返回的数字小于你指定的数据包数量

在这两种情况下,套接字再次变为可写时,Bun 都会调用 drain 套接字处理程序:

TypeScript
const socket = await Bun.udpSocket({
  socket: {
    drain(socket) {
      // 继续发送数据
    },
  },
});

套接字选项

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

TypeScript
const socket = await Bun.udpSocket({});

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

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

多播

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

TypeScript
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");

其他多播选项:

TypeScript
// 设置多播包的 TTL(网络跳数)
socket.setMulticastTTL(2);

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

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

对于源特定多播(SSM),使用 addSourceSpecificMembership 和 dropSourceSpecificMembership:

TypeScript
socket.addSourceSpecificMembership("10.0.0.1", "232.0.0.1");
socket.dropSourceSpecificMembership("10.0.0.1", "232.0.0.1");