Это низкоуровневый API предназначенный для авторов библиотек и для продвинутых случаев использования.
Запуск сервера (Bun.listen())
Для запуска TCP-сервера с Bun.listen:
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.
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
open(socket) {},
data(socket, data) {},
drain(socket) {},
close(socket, error) {},
error(socket, error) {},
},
});Для серверов чувствительных к производительности назначение слушателей каждому сокету может вызывать значительное давление сборщика мусора и увеличивать использование памяти. В отличие от этого Bun выделяет только одну функцию обработчика для каждого события и разделяет её между всеми сокетами. Это небольшая оптимизация но она накапливается.
Контекстные данные могут быть прикреплены к сокету в обработчике open.
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.
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.
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.
const server = Bun.listen({
/* config*/
});
// остановить прослушивание
// параметр определяет закрываются ли активные соединения
server.stop(true);
// разрешить процессу Bun выйти даже если сервер всё ещё слушает
server.unref();Создание соединения (Bun.connect())
Используйте Bun.connect для подключения к TCP-серверу. Укажите сервер для подключения с hostname и port. TCP-клиенты могут определять тот же набор обработчиков что и Bun.listen плюс несколько клиентских обработчиков.
// Клиент
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.
// Клиент
const socket = await Bun.connect({
// ... config
tls: true,
});Горячая перезагрузка
Как TCP-серверы так и сокеты могут быть горячо перезапущены с новыми обработчиками.
const server = Bun.listen({
/* config */
});
// перезагружает обработчики для всех активных сокетов на стороне сервера
server.reload({
socket: {
data() {
// новый обработчик 'data'
},
},
});const socket = await Bun.connect({
/* config */
});
socket.reload({
data() {
// новый обработчик 'data'
},
});Буферизация
В настоящее время TCP-сокеты в Bun не буферизируют данные. Для кода чувствительного к производительности важно тщательно учитывать буферизацию. Например это:
socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");...работает значительно хуже чем это:
socket.write("hello");Для упрощения этого на данный момент рассмотрите возможность использования ArrayBufferSink в Bun с опцией {stream: true}:
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.