Sistem Bulletin Board System (BBS) era 90-an seperti FirstClass Network berhasil menyajikan antarmuka GUI kolaboratif yang responsif di atas koneksi modem berlatensi tinggi dan rawan diskoneksi. Prinsip utamanya bukan sekadar streaming teks mentah via soket telnet, melainkan pemisahan yang ketat antara transport framing, state tracking berbasis urutan (sequence number), dan persistensi sesi parsial di sisi server. Pola ini relevan diterapkan kembali pada aplikasi modern berlatensi rendah yang beroperasi pada konektivitas seluler tidak stabil.

Arsitektur Paket Framing Biner Minimal

Saat beroperasi di atas transport stream seperti raw TCP atau WebSocket biner, pemisahan batas data (framing) wajib dilakukan agar aplikasi tidak terkena masalah packet fragmentation atau concatenation. Skema binary frame fixed-header menyediakan cara paling efisien dalam hal parsing overhead.

+---------------+---------------+-------------------+---------------------+-------------------------+
| Magic (2B)    | Opcode (1B)   | Payload Len (2B)  | Stream ID (2B)      | Payload (N Bytes)       |
| 0xBB 0x53     | 0x01 = DATA   | uint16 Big Endian | uint16 Big Endian   | JSON or Protobuf Data   |
+---------------+---------------+-------------------+---------------------+-------------------------+

Header 7-byte ini memungkinkan runtime menentukan panjang buffer sebelum parsing dilakukan, memutus ketergantungan pada delimiter string seperti \r\n yang rentan terhadap payload injection.

Struktur Kontrak Payload State Sync

Setelah frame diekstrak, payload berisi kontrak kontrol state. Sinkronisasi dua arah membutuhkan pelacakan sekuensial yang ketat agar client dan server memiliki pemahaman state yang deterministik.

{
  "session_id": "bbs-sess-9f82c1a0",
  "seq": 1042,
  "ack_seq": 512,
  "idempotency_key": "msg-client-uuid-771a",
  "action": "DISPATCH_MESSAGE",
  "data": {
    "channel_id": "tech-forum",
    "content": "Halo dari terminal BBS"
  }
}

Atribut seq dinaikkan secara monotonik oleh pengirim, sementara ack_seq mengonfirmasi nomor sekuens terakhir yang berhasil diproses secara lokal dari lawan bicaranya. Ini menyediakan mekanisme sliding-window acknowledgment di level aplikasi.

Reconnection Token dan Replay Buffer

Ketika koneksi transport terputus mendadak, server tidak boleh langsung membuang status memori user. Server menahan state dalam tabel sesi aktif yang diproteksi TTL singkat (misalnya 60 detik) dan menerbitkan reconnection token bertanda tangan kriptografis.

Struktur Reconnection Token

Reconnection token harus memuat data terenkripsi atau HMAC yang memvalidasi identitas sesi tanpa perlu login ulang dari awal:

token = HMAC_SHA256(session_id + ":" + last_acked_seq + ":" + expires_at, secret_key)

Mekanisme Resync Handshake

  1. Client mendeteksi koneksi terputus dan melakukan rekoneksi socket.
  2. Client mengirim frame sinkronisasi awal: { "action": "RESUME", "token": token, "last_received_seq": 512 }.
  3. Server memvalidasi token dan memeriksa ring buffer sesi.
  4. Jika last_received_seq masih ada dalam buffer server, server mengirimkan ulang (replay) seluruh frame dari sequence 513 hingga posisi terkini.
  5. Jika buffer sudah terhapus akibat TTL habis, server mengembalikan status SESSION_EXPIRED, memaksa client melakukan re-autentikasi dan clean state pull.

Validasi Boundary dan Idempotency

Berikut implementasi validasi boundary dan penanganan rekoneksi stateful pada server menggunakan Node.js:

class SessionState {
  constructor(sessionId) {
    this.sessionId = sessionId;
    this.serverSeq = 0;
    this.expectedClientSeq = 1;
    this.replayBuffer = []; // { seq: number, payload: any }
    this.processedKeys = new Set();
  }

  processIncoming(frame) {
    // 1. Boundary Check: Cek duplikasi via Idempotency Key
    if (this.processedKeys.has(frame.idempotency_key)) {
      return { status: "DUPLICATE_IGNORED", seq: this.serverSeq };
    }

    // 2. Sequence Gap Check
    if (frame.seq !== this.expectedClientSeq) {
      return {
        status: "OUT_OF_ORDER",
        expected: this.expectedClientSeq,
        received: frame.seq
      };
    }

    // 3. Update State
    this.processedKeys.add(frame.idempotency_key);
    this.expectedClientSeq++;

    // 4. Prune idempotency set (simpan maks 1000 entri)
    if (this.processedKeys.size > 1000) {
      const firstKey = this.processedKeys.values().next().value;
      this.processedKeys.delete(firstKey);
    }

    // 5. Append ke Replay Buffer server
    this.serverSeq++;
    const outPacket = {
      seq: this.serverSeq,
      ack_seq: frame.seq,
      action: "ACK",
      result: "PROCESSED"
    };
    
    this.replayBuffer.push(outPacket);
    if (this.replayBuffer.length > 256) {
      this.replayBuffer.shift();
    }

    return outPacket;
  }

  getReplayFrames(fromSeq) {
    return this.replayBuffer.filter(frame => frame.seq > fromSeq);
  }
}

Trade-off dan Limitasi

Menerapkan stateful connection buffer memiliki batasan memori yang jelas. Menyimpan ring buffer untuk ribuan koneksi konkuren membutuhkan estimasi kapasitas RAM yang ketat:

  • Memory Overhead: Buffer sebanyak 256 frame berukuran rata-rata 1KB per user berarti 256MB RAM per 1.000 user aktif terputus. Solusi: Batasi TTL sesi terputus di bawah 120 detik.
  • Horizontal Scaling: Model stateful seperti ini membutuhkan distributed cache (seperti Redis Streams) atau routing sticky session (consistent hashing) pada API Gateway agar rekoneksi mendarat di node server yang memegang ring buffer yang sama.

Pola BBS klasik ini menstabilkan aplikasi dari interupsi jaringan mikro tanpa membebani throughput dengan proses otentikasi penuh berulang kali.