Skip to content

이는 라이브러리 작성자와 고급 사용 사례를 위한 저수준 API 입니다.

서버 시작 (Bun.listen())

Bun.listen 으로 TCP 서버를 시작하려면:

ts
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 와 같이 각 소켓에 콜백을 할당하는 대신 서버당 한 번 선언됩니다.

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 를 활성화하려면 keycert 필드가 포함된 tls 객체를 전달합니다.

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"), 
  },
});

keycert 필드는 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 서버에 연결합니다. hostnameport 로 연결할 서버를 지정합니다. 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' 핸들러
  },
});

버퍼링

현재 Bun 의 TCP 소켓은 데이터를 버퍼링하지 않습니다. 성능에 민감한 코드의 경우 버퍼링을 주의 깊게 고려하는 것이 중요합니다. 예를 들어:

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

...이는 다음보다 성능이 훨씬 떨어집니다.

ts
socket.write("hello");

이를 위해 현재는 {stream: true} 옵션과 함께 Bun 의 ArrayBufferSink 를 사용하는 것을 고려하세요.

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.write(data.subarray(wrote));
  }
});

NOTE

**코킹 (Corking)**

코킹 지원이 계획되어 있지만 당분간은 백프레셔를 drain 핸들러로 수동으로 관리해야 합니다.

Bun by www.bunjs.com.cn 편집