Bun

UDP 套接字

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

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

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

const socket = await Bun.udpSocket({})
console.log(socket.port); // assigned by the operating system

指定端口

const socket = await Bun.udpSocket({
  port: 41234
})
console.log(socket.port); // 41234

发送数据报

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

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

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

接收数据报

创建套接字时,添加一个回调以指定在接收到数据包时应执行的操作

const server = await Bun.udpSocket({
  socket: {
    data(socket, buf, port, addr) {
      console.log(`message from ${addr}:${port}:`)
      console.log(buf.toString());
    } 
  }
})

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

连接

虽然 UDP 没有连接的概念,但许多 UDP 通信(尤其是作为客户端)仅涉及一个对等方。 在这种情况下,将套接字连接到该对等方可能是有益的,这指定所有数据包发送到的地址,并将传入数据包限制为仅来自该对等方。 由于连接是在操作系统级别实现的,因此您也可能会观察到性能优势。


const server = await Bun.udpSocket({
  socket: {
    data(socket, buf, port, addr) {
      console.log(`message from ${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 接受一个数组作为其唯一参数。每组三个数组元素描述一个数据包 第一个项目是要发送的数据,第二个是目标端口,最后一个是目标地址。

const socket = await Bun.udpSocket({})
// sends 'Hello' to 127.0.0.1:41234, and 'foo' to 1.1.1.1:53 in a single operation
socket.sendMany(['Hello', 41234, '127.0.0.1', 'foo', 53, '1.1.1.1'])

对于已连接的套接字,sendMany 仅接受一个数组,其中每个元素代表要发送到对等方的数据。

const socket = await Bun.udpSocket({
  connect: {
    port: 41234,
    hostname: 'localhost',
  }
});
socket.sendMany(['foo', 'bar', 'baz']);

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

处理背压

您发送的数据包可能无法放入操作系统的数据包缓冲区。您可以检测到这种情况 发生于以下情况时

  • send 返回 false
  • sendMany 返回的数字小于您指定的包数量 在这种情况下,一旦套接字再次变为可写状态,将调用 drain 套接字处理程序
const socket = await Bun.udpSocket({
  socket: {
    drain(socket) {
      // continue sending data
    }
  }
});