Esta é uma API de baixo nível destinada a autores de bibliotecas e casos de uso avançados.
Iniciar um servidor (Bun.listen())
Para iniciar um servidor TCP com Bun.listen:
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {}, // mensagem recebida do cliente
open(socket) {}, // socket aberto
close(socket, error) {}, // socket fechado
drain(socket) {}, // socket pronto para mais dados
error(socket, error) {}, // handler de erro
},
});Uma API projetada para velocidade
No Bun, um conjunto de handlers é declarado uma vez por servidor ao invés de atribuir callbacks a cada socket, como com EventEmitters do Node.js ou a API WebSocket padrão web.
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
open(socket) {},
data(socket, data) {},
drain(socket) {},
close(socket, error) {},
error(socket, error) {},
},
});Para servidores sensíveis a performance, atribuir listeners a cada socket pode causar pressão significativa no garbage collector e aumentar o uso de memória. Em contraste, o Bun apenas aloca uma função handler para cada evento e compartilha entre todos os sockets. Esta é uma pequena otimização, mas se acumula.
Dados contextuais podem ser anexados a um socket no handler 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, passe um objeto tls contendo campos key e cert.
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {},
},
tls: {
// pode ser string, BunFile, TypedArray, Buffer, ou array disso
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
},
});Os campos key e cert esperam o conteúdo da sua chave TLS e certificado. Isto pode ser uma string, BunFile, TypedArray, ou 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")], // array dos acima
},
});O resultado de Bun.listen é um servidor que conforma com a interface TCPSocket.
const server = Bun.listen({
/* config*/
});
// para escuta
// parâmetro determina se conexões ativas são fechadas
server.stop(true);
// permite processo Bun sair mesmo se servidor ainda está escutando
server.unref();Criar uma conexão (Bun.connect())
Use Bun.connect para conectar a um servidor TCP. Especifique o servidor ao qual conectar com hostname e port. Clientes TCP podem definir o mesmo conjunto de handlers que Bun.listen, mais alguns handlers específicos de cliente.
// O cliente
const socket = await Bun.connect({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {},
open(socket) {},
close(socket, error) {},
drain(socket) {},
error(socket, error) {},
// handlers específicos do cliente
connectError(socket, error) {}, // conexão falhou
end(socket) {}, // conexão fechada pelo servidor
timeout(socket) {}, // conexão teve timeout
},
});Para requerer TLS, especifique tls: true.
// O cliente
const socket = await Bun.connect({
// ... config
tls: true,
});Hot reloading
Tanto servidores TCP quanto sockets podem ser hot reloadados com novos handlers.
const server = Bun.listen({
/* config */
});
// recarrega handlers para todos os sockets do lado do servidor ativos
server.reload({
socket: {
data() {
// novo handler 'data'
},
},
});const socket = await Bun.connect({
/* config */
});
socket.reload({
data() {
// novo handler 'data'
},
});Bufferização
Atualmente, sockets TCP no Bun não bufferizam dados. Para código sensível a performance, é importante considerar a bufferização cuidadosamente. Por exemplo, isto:
socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");...tem performance significativamente pior que isto:
socket.write("hello");Para simplificar isto por enquanto, considere usar ArrayBufferSink do Bun com a opção {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) {
// coloca de volta no sink se o socket está cheio
sink.write(data.subarray(wrote));
}
});NOTE
**Corking**Suporte para corking é planejado, mas enquanto isso backpressure deve ser gerenciado manualmente com o handler drain.