Bun

UDP sockets

使用 Bun 的 UDP API 來實作具有進階即時需求的服務,例如語音聊天。

綁定 UDP socket (Bun.udpSocket())

建立新的(已綁定)UDP socket

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 解析,因為其目的是用於低延遲操作。

接收資料包

建立 socket 時,新增一個回呼函式來指定收到封包時應執行的動作

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 通訊(尤其作為客戶端)僅涉及單一對等節點。 在這種情況下,將 socket 連線到該對等節點可能很有益,這會指定所有封包要發送到哪個位址 並將傳入的封包限制為僅來自該對等節點。


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 使這成為可能

對於未連線的 socket,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'])

對於已連線的 socket,sendMany 僅接受一個陣列,其中每個元素代表要發送到對等節點的資料。

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

sendMany 傳回成功發送的封包數量。與 send 相同,sendMany 僅接受有效的 IP 位址 作為目的地,因為它不執行 DNS 解析。

處理背壓

可能會發生您要發送的封包不適合作業系統的封包緩衝區的情況。您可以偵測到這種情況 發生在當

  • send 傳回 false
  • sendMany 傳回的數字小於您指定的封包數量 在這種情況下,一旦 socket 再次可寫入,就會呼叫 drain socket 處理常式。
const socket = await Bun.udpSocket({
  socket: {
    drain(socket) {
      // continue sending data
    }
  }
});