Skip to content

Это низкоуровневый API предназначенный для авторов библиотек и для продвинутых случаев использования.

Запуск сервера (Bun.listen())

Для запуска TCP-сервера с Bun.listen:

ts
Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {}, // сообщение получено от клиента
    open(socket) {}, // сокет открыт
    close(socket, error) {}, // сокет закрыт
    drain(socket) {}, // сокет готов к получению большего количества данных
    error(socket, error) {}, // обработчик ошибок
  },
});

API разработанный для скорости

В Bun набор обработчиков объявляется один раз для сервера вместо назначения обратных вызовов каждому сокету как в EventEmitters Node.js или веб-стандартном API WebSocket.

ts
Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    open(socket) {},
    data(socket, data) {},
    drain(socket) {},
    close(socket, error) {},
    error(socket, error) {},
  },
});

Для серверов чувствительных к производительности назначение слушателей каждому сокету может вызывать значительное давление сборщика мусора и увеличивать использование памяти. В отличие от этого Bun выделяет только одну функцию обработчика для каждого события и разделяет её между всеми сокетами. Это небольшая оптимизация но она накапливается.

Контекстные данные могут быть прикреплены к сокету в обработчике open.

ts
type SocketData = { sessionId: string };

Bun.listen<SocketData>({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {
      socket.write(`${socket.data.sessionId}: ack`); 
    },
    open(socket) {
      socket.data = { sessionId: "abcd" }; 
    },
  },
});

Для включения TLS передайте объект tls содержащий поля key и cert.

ts
Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {},
  },
  tls: {
    // может быть строкой BunFile TypedArray Buffer или массивом из них
    key: Bun.file("./key.pem"), 
    cert: Bun.file("./cert.pem"), 
  },
});

Поля key и cert ожидают содержимое вашего TLS-ключа и сертификата. Это может быть строка BunFile TypedArray или Buffer.

ts
Bun.listen({
  // ...
  tls: {
    key: Bun.file("./key.pem"), // BunFile
    key: fs.readFileSync("./key.pem"), // Buffer
    key: fs.readFileSync("./key.pem", "utf8"), // string
    key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")], // массив вышеуказанного
  },
});

Результат Bun.listen — это сервер соответствующий интерфейсу TCPSocket.

ts
const server = Bun.listen({
  /* config*/
});

// остановить прослушивание
// параметр определяет закрываются ли активные соединения
server.stop(true);

// разрешить процессу Bun выйти даже если сервер всё ещё слушает
server.unref();

Создание соединения (Bun.connect())

Используйте Bun.connect для подключения к TCP-серверу. Укажите сервер для подключения с hostname и port. TCP-клиенты могут определять тот же набор обработчиков что и Bun.listen плюс несколько клиентских обработчиков.

ts
// Клиент
const socket = await Bun.connect({
  hostname: "localhost",
  port: 8080,

  socket: {
    data(socket, data) {},
    open(socket) {},
    close(socket, error) {},
    drain(socket) {},
    error(socket, error) {},

    // клиентские обработчики
    connectError(socket, error) {}, // подключение не удалось
    end(socket) {}, // соединение закрыто сервером
    timeout(socket) {}, // истекло время ожидания соединения
  },
});

Для требования TLS укажите tls: true.

ts
// Клиент
const socket = await Bun.connect({
  // ... config
  tls: true, 
});

Горячая перезагрузка

Как TCP-серверы так и сокеты могут быть горячо перезапущены с новыми обработчиками.

ts
const server = Bun.listen({
  /* config */
});

// перезагружает обработчики для всех активных сокетов на стороне сервера
server.reload({
  socket: {
    data() {
      // новый обработчик 'data'
    },
  },
});
ts
const socket = await Bun.connect({
  /* config */
});

socket.reload({
  data() {
    // новый обработчик 'data'
  },
});

Буферизация

В настоящее время TCP-сокеты в Bun не буферизируют данные. Для кода чувствительного к производительности важно тщательно учитывать буферизацию. Например это:

ts
socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");

...работает значительно хуже чем это:

ts
socket.write("hello");

Для упрощения этого на данный момент рассмотрите возможность использования ArrayBufferSink в Bun с опцией {stream: true}:

ts
import { ArrayBufferSink } from "bun";

const sink = new ArrayBufferSink();
sink.start({
  stream: true, 
  highWaterMark: 1024,
});

sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");

queueMicrotask(() => {
  const data = sink.flush();
  const wrote = socket.write(data);
  if (wrote < data.byteLength) {
    // вернуть обратно в sink если сокет полон
    sink.write(data.subarray(wrote));
  }
});

NOTE

**Corking**

Поддержка corking запланирована но тем временем обратное давление должно управляться вручную с обработчиком drain.

Bun от www.bunjs.com.cn