Esta es una API de bajo nivel destinada a autores de bibliotecas y casos de uso avanzados.
Iniciar un servidor (Bun.listen())
Para iniciar un servidor TCP con Bun.listen:
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {}, // mensaje recibido del cliente
open(socket) {}, // socket abierto
close(socket, error) {}, // socket cerrado
drain(socket) {}, // socket listo para más datos
error(socket, error) {}, // manejador de errores
},
});Una API diseñada para velocidad
En Bun, un conjunto de manejadores se declara una vez por servidor en lugar de asignar callbacks a cada socket, como con los EventEmitters de Node.js o la API WebSocket estándar web.
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
open(socket) {},
data(socket, data) {},
drain(socket) {},
close(socket, error) {},
error(socket, error) {},
},
});Para servidores sensibles al rendimiento, asignar listeners a cada socket puede causar presión significativa en el recolector de basura y aumentar el uso de memoria. Por el contrario, Bun solo asigna una función manejadora para cada evento y la comparte entre todos los sockets. Esta es una pequeña optimización, pero se acumula.
Se pueden adjuntar datos contextuales a un socket en el manejador 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" };
},
},
});Para habilitar TLS, pasa un objeto tls que contenga los campos key y cert.
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {},
},
tls: {
// puede ser cadena, BunFile, TypedArray, Buffer, o array de los mismos
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
},
});Los campos key y cert esperan el contenido de tu clave TLS y certificado. Esto puede ser una cadena, BunFile, TypedArray, o Buffer.
Bun.listen({
// ...
tls: {
key: Bun.file("./key.pem"), // BunFile
key: fs.readFileSync("./key.pem"), // Buffer
key: fs.readFileSync("./key.pem", "utf8"), // cadena
key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")], // array de lo anterior
},
});El resultado de Bun.listen es un servidor que cumple con la interfaz TCPSocket.
const server = Bun.listen({
/* config*/
});
// dejar de escuchar
// el parámetro determina si las conexiones activas se cierran
server.stop(true);
// permitir que el proceso Bun salga incluso si el servidor sigue escuchando
server.unref();Crear una conexión (Bun.connect())
Usa Bun.connect para conectarse a un servidor TCP. Especifica el servidor al que conectarse con hostname y port. Los clientes TCP pueden definir el mismo conjunto de manejadores que Bun.listen, más un par de manejadores específicos del cliente.
// El cliente
const socket = await Bun.connect({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {},
open(socket) {},
close(socket, error) {},
drain(socket) {},
error(socket, error) {},
// manejadores específicos del cliente
connectError(socket, error) {}, // conexión fallida
end(socket) {}, // conexión cerrada por el servidor
timeout(socket) {}, // conexión agotada
},
});Para requerir TLS, especifica tls: true.
// El cliente
const socket = await Bun.connect({
// ... config
tls: true,
});Recarga en caliente
Tanto los servidores TCP como los sockets se pueden recargar en caliente con nuevos manejadores.
const server = Bun.listen({
/* config */
});
// recarga manejadores para todos los sockets del lado del servidor activos
server.reload({
socket: {
data() {
// nuevo manejador 'data'
},
},
});const socket = await Bun.connect({
/* config */
});
socket.reload({
data() {
// nuevo manejador 'data'
},
});Almacenamiento en búfer
Actualmente, los sockets TCP en Bun no almacenan datos en búfer. Para código sensible al rendimiento, es importante considerar cuidadosamente el almacenamiento en búfer. Por ejemplo, esto:
socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");...tiene un rendimiento significativamente peor que esto:
socket.write("hello");Para simplificar esto por ahora, considera usar ArrayBufferSink de Bun con la opción {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) {
// volver a poner en el sink si el socket está lleno
sink.write(data.subarray(wrote));
}
});NOTE
**Corking**El soporte para corking está planeado, pero mientras tanto la contrapresión debe manejarse manualmente con el manejador drain.