이는 라이브러리 작성자와 고급 사용 사례를 위한 저수준 API 입니다.
서버 시작 (Bun.listen())
Bun.listen 으로 TCP 서버를 시작하려면:
Bun.listen({
hostname: "localhost",
port: 8080,
socket: {
data(socket, data) {}, // 클라이언트로부터 메시지 수신
open(socket) {}, // 소켓 열림
close(socket, error) {}, // 소켓 닫힘
drain(socket) {}, // 소켓이 더 많은 데이터 준비됨
error(socket, error) {}, // 오류 핸들러
},
});속도를 위해 설계된 API
Bun 에서 핸들러는 Node.js EventEmitters 또는 Web 표준 WebSocket API 와 같이 각 소켓에 콜백을 할당하는 대신 서버당 한 번 선언됩니다.
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 를 활성화하려면 key 와 cert 필드가 포함된 tls 객체를 전달합니다.
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' 핸들러
},
});버퍼링
현재 Bun 의 TCP 소켓은 데이터를 버퍼링하지 않습니다. 성능에 민감한 코드의 경우 버퍼링을 주의 깊게 고려하는 것이 중요합니다. 예를 들어:
socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");...이는 다음보다 성능이 훨씬 떨어집니다.
socket.write("hello");이를 위해 현재는 {stream: true} 옵션과 함께 Bun 의 ArrayBufferSink 를 사용하는 것을 고려하세요.
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.write(data.subarray(wrote));
}
});NOTE
**코킹 (Corking)**코킹 지원이 계획되어 있지만 당분간은 백프레셔를 drain 핸들러로 수동으로 관리해야 합니다.