Protokol

WebSocket Deep Dive

Panduan mendalam protokol WebSocket β€” upgrade handshake, binary framing, ping/pong heartbeat, close handshake, strategi scaling horizontal, dan load balancing untuk koneksi jutaan client

1. Apa Itu WebSocket?

WebSocket (RFC 6455) adalah protokol komunikasi full-duplex yang beroperasi di atas satu koneksi TCP. Berbeda dengan HTTP yang bersifat request/response, WebSocket memungkinkan client dan server mengirim data secara simultan dan asynchronous setelah koneksi terbentuk.

WebSocket vs HTTP Polling

AspekHTTP PollingLong PollingServer-Sent EventsWebSocket
DirectionClient β†’ ServerClient β†’ ServerServer β†’ ClientBidirectional
Overhead~800 bytes per request~800 bytes per pollHTTP header sekali2-6 bytes per frame
LatencyInterval-basedRendahRendahSangat rendah
Real-timeTidakMendekatiYa (satu arah)Ya (dua arah)
ProtocolHTTP/1.1+HTTP/1.1+HTTP/1.1+ws:// atau wss://
Binary supportBase64 (boros)Base64Base64Native binary
Use caseData jarang berubahChat ringanNotifikasi, feedChat, gaming, trading
πŸ’‘ Kapan Menggunakan WebSocket?

WebSocket ideal untuk: chat apps (WhatsApp Web), real-time dashboards (monitoring), multiplayer games, collaborative editing (Google Docs), live trading (crypto/forex), dan IoT telemetry. Jika Anda hanya butuh server-to-client, SSE lebih sederhana.

2. WebSocket Handshake

WebSocket menggunakan HTTP Upgrade mechanism untuk memulai koneksi. Client mengirim HTTP request dengan header Upgrade: websocket, server merespons 101 Switching Protocols, dan koneksi berubah dari HTTP ke WebSocket.

Upgrade Handshake Detail

HTTP
# === Client Request (HTTP Upgrade) ===
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

# === Server Response (101 Switching Protocols) ===
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

# === Penjelasan Header ===
# Sec-WebSocket-Key: random 16-byte nonce, base64-encoded
# β†’ Digenerate client baru setiap koneksi
# β†’ BUKAN security measure, hanya mencegah cache proxy

# Sec-WebSocket-Accept: server menghitung hash dari key client
# β†’ SHA1(Key + "258EAFA5-E914-47DA-95CA-5AB40401B2E7")
# β†’ "magic string" ini adalah GUID tetap dari RFC 6455
# β†’ Client harus verifikasi hash ini!

# Sec-WebSocket-Extensions: permessage-deflate
# β†’ Kompresi message (opsional)
# β†’ Mengurangi bandwidth 50-80% untuk payload text

# Sec-WebSocket-Protocol: subprotocol negotiation
# β†’ Client menawarkan beberapa, server memilih satu

Verifikasi Handshake di JavaScript

JavaScript
// Browser API β€” handshake dilakukan otomatis
const ws = new WebSocket('wss://server.example.com/chat', ['chat', 'superchat']);

ws.onopen = (event) => {
    console.log('WebSocket connected!');

    // Browser sudah melakukan:
    // 1. Generate random Sec-WebSocket-Key
    // 2. Kirim HTTP Upgrade request
    // 3. Verifikasi Sec-WebSocket-AcAccept dari server
    // 4. Switch ke WebSocket mode

    ws.send(JSON.stringify({ type: 'join', room: 'general' }));
};

ws.onmessage = (event) => {
    console.log('Received:', event.data);
};

ws.onerror = (error) => {
    console.error('WebSocket error:', error);
};

ws.onclose = (event) => {
    console.log(`Closed: code=${event.code}, reason=${event.reason}`);
};

// Server-side (Node.js dengan 'ws' library):
const WebSocket = require('ws');
const crypto = require('crypto');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws, req) => {
    console.log('New connection from:', req.socket.remoteAddress);

    // WebSocket library sudah handle handshake otomatis
    // Tapi kita bisa akses raw headers:
    console.log('Origin:', req.headers['origin']);
    console.log('Protocol:', ws.protocol);

    ws.on('message', (data) => {
        console.log('Received:', data.toString());
        ws.send(`Echo: ${data}`);
    });
});

3. Frame Format & Data Types

WebSocket menggunakan binary framing yang sangat efisien. Setiap frame memiliki overhead hanya 2-6 bytes (tanpa masking key), jauh lebih kecil dari HTTP header.

WebSocket Frame Structure

Text
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (jika payload len=126/127) |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data (continued)                  |
+---------------------------------------------------------------+

# Penjelasan Field:
# FIN (1 bit)     : 1 = frame terakhir dari message
# RSV1-3 (3 bit)  : Reserved (untuk extensions seperti permessage-deflate)
# OPCODE (4 bit)  : Tipe frame
# MASK (1 bit)    : 1 = payload di-mask (client→server WAJIB mask)
# Payload Length  : 0-125 langsung, 126 = 2 byte extended, 127 = 8 byte extended

# Opcodes:
# 0x0 = Continuation frame
# 0x1 = Text frame (UTF-8)
# 0x2 = Binary frame
# 0x8 = Connection close
# 0x9 = Ping
# 0xA = Pong

Contoh Frame Encoding

Python
import struct
import os

def encode_ws_frame(payload, opcode=0x1, mask=True):
    """Encode payload menjadi WebSocket frame"""
    frame = bytearray()

    # Byte pertama: FIN (1) + RSV (000) + Opcode
    frame.append(0x80 | opcode)  # 0x80 = FIN bit set

    # Byte kedua: MASK bit + payload length
    length = len(payload)
    if length < 126:
        frame.append((0x80 if mask else 0) | length)
    elif length < 65536:
        frame.append((0x80 if mask else 0) | 126)
        frame.extend(struct.pack('>H', length))
    else:
        frame.append((0x80 if mask else 0) | 127)
        frame.extend(struct.pack('>Q', length))

    # Masking key (4 bytes random)
    if mask:
        masking_key = os.urandom(4)
        frame.extend(masking_key)

        # Mask payload
        masked = bytearray(len(payload))
        for i in range(len(payload)):
            masked[i] = payload[i] ^ masking_key[i % 4]
        frame.extend(masked)
    else:
        frame.extend(payload)

    return bytes(frame)

# Contoh: Text frame "Hello"
frame = encode_ws_frame(b'Hello')
print(f"Frame size: {len(frame)} bytes")  # 11 bytes (2 header + 4 mask + 5 payload)
print(f"Overhead: {len(frame) - len(b'Hello')} bytes")  # 6 bytes overhead

# Contoh: Binary frame dengan data 1000 bytes
import json
data = json.dumps({"temp": 25.3, "humidity": 62}).encode()
frame = encode_ws_frame(data, opcode=0x2)
print(f"Binary frame: {len(frame)} bytes for {len(data)} bytes payload")

4. Ping/Pong & Heartbeat

WebSocket mendukung mekanisme Ping/Pong untuk mendeteksi koneksi yang mati (liveness check) dan menjaga koneksi tetap hidup melalui proxy/firewall yang mungkin akan menutup idle connections.

Mekanisme Ping/Pong

Text
# Ping/Pong Flow:

# 1. Bisa dikirim oleh KEDUA belah pihak (client dan server)
# 2. Pong WAJIB dikirim sebagai respons terhadap Ping
# 3. Ping bisa membawa payload (application data)
# 4. Pong HARUS meng-copy payload dari Ping

# Contoh flow:
Client                          Server
  |                               |
  |--- Ping (frame 0x9) -------->|
  |    payload: "keepalive"       |
  |                               |
  |<-- Pong (frame 0xA) ---------|
  |    payload: "keepalive"       |  ← HARUS sama dengan Ping payload
  |                               |
  |     ... connection healthy ...|
  |                               |
  |--- Ping ---------------------->|
  |                               |
  |    (timeout, tidak ada Pong)  |
  |     β†’ Koneksi dianggap MATI  |
  |     β†’ Trigger close/onerror  |

# Best Practice:
# - Ping interval: 30 detik (normal), 15 detik (high-reliability)
# - Pong timeout: 10 detik (jika tidak ada Pong dalam 10s β†’ close)
# - Jangan terlalu sering (boros bandwidth) atau terlalu jarang (lambat deteksi)

Implementasi Heartbeat di Node.js

JavaScript
const WebSocket = require('ws');

class HeartbeatWebSocket {
    constructor(url, options = {}) {
        this.url = url;
        this.pingInterval = options.pingInterval || 30000; // 30 detik
        this.pongTimeout = options.pongTimeout || 10000;   // 10 detik
        this.isAlive = false;
        this.pingTimer = null;
        this.pongTimer = null;

        this.connect();
    }

    connect() {
        this.ws = new WebSocket(this.url);

        this.ws.on('open', () => {
            console.log('Connected');
            this.isAlive = true;
            this.startHeartbeat();
        });

        this.ws.on('pong', (data) => {
            // Pong diterima β†’ koneksi hidup
            this.isAlive = true;
            clearTimeout(this.pongTimer);
            console.log(`Pong received: ${data.toString()}`);
        });

        this.ws.on('close', () => {
            console.log('Disconnected');
            this.stopHeartbeat();
            // Reconnect setelah 3 detik
            setTimeout(() => this.connect(), 3000);
        });
    }

    startHeartbeat() {
        this.pingTimer = setInterval(() => {
            if (!this.isAlive) {
                console.log('No pong received, terminating...');
                return this.ws.terminate();
            }

            this.isAlive = false;
            this.ws.ping('heartbeat');

            // Set timeout untuk pong
            this.pongTimer = setTimeout(() => {
                if (!this.isAlive) {
                    console.log('Pong timeout, closing...');
                    this.ws.close(1000, 'Pong timeout');
                }
            }, this.pongTimeout);

        }, this.pingInterval);
    }

    stopHeartbeat() {
        clearInterval(this.pingTimer);
        clearTimeout(this.pongTimer);
    }
}

// Server-side heartbeat
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    ws.isAlive = true;

    ws.on('pong', () => {
        ws.isAlive = true;
    });
});

// Periodic check β€” setiap 30 detik
const heartbeatInterval = setInterval(() => {
    wss.clients.forEach((ws) => {
        if (!ws.isAlive) return ws.terminate();
        ws.isAlive = false;
        ws.ping();
    });
}, 30000);

wss.on('close', () => clearInterval(heartbeatInterval));

5. Close Handshake

WebSocket memiliki mekanisme graceful close yang memungkinkan kedua belah pihak menutup koneksi dengan terhormat, termasuk mengirim alasan penutupan.

Close Frame Format & Status Codes

Text
# Close Frame Structure:
# OPCODE: 0x8
# Payload: 2-byte status code (big-endian) + optional UTF-8 reason string

# Close Handshake:
# 1. Peer A mengirim Close frame
# 2. Peer B menerima Close frame
# 3. Peer B mengirim Close frame balasan (echo status code)
# 4. TCP connection ditutup

Client                          Server
  |                               |
  |--- Close (1000, "Normal") -->|
  |                               |
  |<-- Close (1000) --------------|
  |                               |
  | TCP FIN ───────────────────> |
  | <───────────────────────── FIN ACK

# Status Codes:
# 1000 β€” Normal closure
# 1001 β€” Going away (server shutdown, browser navigate away)
# 1002 β€” Protocol error
# 1003 β€” Unsupported data type (text vs binary mismatch)
# 1005 β€” No status received (internal, tidak dikirim di frame)
# 1006 β€” Abnormal closure (internal, tidak dikirim di frame)
# 1007 β€” Invalid frame payload data (misal: UTF-8 encoding error)
# 1008 β€” Policy violation
# 1009 β€” Message too big
# 1010 β€” Mandatory extension not negotiated
# 1011 β€” Internal server error
# 1012 β€” Service restart
# 1013 β€” Try again later
# 1014 β€” Bad gateway
# 3000-3999 β€” Registered extensions
# 4000-4999 β€” Application-defined

6. Scaling & Load Balancing

Scaling WebSocket server adalah tantangan tersendiri karena sifat stateful dari koneksi. Setiap client terikat ke satu server instance, berbeda dengan HTTP stateless yang bisa di-route ke mana saja.

Tantangan Scaling WebSocket

TantanganPenjelasanSolusi
Sticky SessionsClient harus terhubung ke server yang samaSticky load balancing berdasarkan IP/cookie
State ManagementState harus di-share antar serverRedis Pub/Sub sebagai message bus
Connection MemorySetiap koneksi butuh RAM ~20-50KBOptimasi buffer, horizontal scaling
Health CheckLoad balancer perlu cek koneksiCustom health endpoint, ping/pong
Graceful ShutdownServer update tanpa memutus koneksiDrain connections sebelum restart

Architecture: Multi-Server dengan Redis Pub/Sub

Text
# === Arsitektur Scaling WebSocket ===

# Client A (terhubung ke Server 1) mengirim message ke Client C (terhubung ke Server 2)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Client A    β”‚    β”‚  Client B    β”‚
β”‚  (Server 1)  β”‚    β”‚  (Server 1)  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                   β”‚
       β–Ό                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Load Balancer           β”‚
β”‚   (Sticky by IP / Cookie)       β”‚
β”‚   Nginx / HAProxy / AWS ALB     β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜
     β–Ό            β–Ό           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Server 1β”‚ β”‚ Server 2β”‚ β”‚ Server 3β”‚
β”‚  Node.jsβ”‚ β”‚  Node.jsβ”‚ β”‚  Node.jsβ”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
     β”‚           β”‚           β”‚
     β–Ό           β–Ό           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Redis Pub/Sub         β”‚
β”‚   Channel: "chat:room-1"        β”‚
β”‚   Channel: "notifications"      β”‚
β”‚   Channel: "broadcast"          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

# Flow:
# 1. Client A β†’ Server 1: "Hello everyone!"
# 2. Server 1 β†’ Redis PUBLISH "chat:room-1" {message}
# 3. Server 1, 2, 3 semua SUBSCRIBE "chat:room-1"
# 4. Server 1 β†’ kirim ke Client A, B (yang terhubung ke Server 1)
# 5. Server 2 β†’ kirim ke Client C (yang terhubung ke Server 2)
# 6. Server 3 β†’ kirim ke Client D (yang terhubung ke Server 3)

Implementasi Multi-Server

JavaScript
const WebSocket = require('ws');
const Redis = require('ioredis');
const http = require('http');

// Setup Redis
const pub = new Redis({ host: 'redis-server', port: 6379 });
const sub = new Redis({ host: 'redis-server', port: 6379 });

// Setup HTTP + WebSocket server
const server = http.createServer();
const wss = new WebSocket.Server({ server });

// Track clients per room
const rooms = new Map();

wss.on('connection', (ws, req) => {
    const clientId = generateId();
    ws.clientId = clientId;
    ws.rooms = new Set();

    ws.on('message', (data) => {
        const msg = JSON.parse(data);

        switch (msg.type) {
            case 'join':
                joinRoom(ws, msg.room);
                break;
            case 'chat':
                broadcastToRoom(msg.room, {
                    type: 'chat',
                    from: clientId,
                    message: msg.message,
                    timestamp: Date.now()
                }, ws); // exclude sender
                break;
        }
    });

    ws.on('close', () => {
        // Leave all rooms
        ws.rooms.forEach(room => leaveRoom(ws, room));
    });
});

function joinRoom(ws, room) {
    ws.rooms.add(room);
    if (!rooms.has(room)) {
        rooms.set(room, new Set());
        // Subscribe ke Redis channel untuk room ini
        sub.subscribe(`room:${room}`);
    }
    rooms.get(room).add(ws);

    // Notify semua di room
    broadcastToRoom(room, {
        type: 'join',
        user: ws.clientId,
        room: room
    });
}

function broadcastToRoom(room, message, exclude = null) {
    const localClients = rooms.get(room) || new Set();

    // Kirim ke client lokal
    const payload = JSON.stringify(message);
    localClients.forEach(client => {
        if (client !== exclude && client.readyState === WebSocket.OPEN) {
            client.send(payload);
        }
    });

    // Publish ke Redis agar server lain juga mengirim
    pub.publish(`room:${room}`, JSON.stringify({
        ...message,
        _fromServer: SERVER_ID
    }));
}

// Terima message dari server lain via Redis
sub.on('message', (channel, data) => {
    const message = JSON.parse(data);
    const room = channel.replace('room:', '');

    // Skip jika dari server ini sendiri
    if (message._fromServer === SERVER_ID) return;

    // Kirim ke client lokal
    const clients = rooms.get(room) || new Set();
    clients.forEach(client => {
        if (client.readyState === WebSocket.OPEN) {
            client.send(JSON.stringify(message));
        }
    });
});

const SERVER_ID = process.env.SERVER_ID || 'server-1';
server.listen(8080, () => console.log(`WS Server ${SERVER_ID} on :8080`));
⚠️ Nginx Load Balancing untuk WebSocket

Di Nginx, pastikan menambahkan header Upgrade dan Connection di proxy_pass. Tanpa ini, WebSocket handshake akan gagal. Tambahkan juga proxy_read_timeout yang cukup panjang agar Nginx tidak menutup koneksi idle.

Nginx
# Nginx configuration untuk WebSocket load balancing

upstream websocket_backend {
    ip_hash;  # Sticky session berdasarkan IP
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
    server 127.0.0.1:8083;
}

server {
    listen 443 ssl;
    server_name ws.example.com;

    location /ws {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;

        # Header untuk WebSocket upgrade
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeout β€” 1 jam untuk WebSocket
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        # Preserve client info
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Buffering off untuk real-time
        proxy_buffering off;
    }
}

7. Implementasi dengan Node.js & Socket.IO

Socket.IO adalah library populer yang built di atas WebSocket dengan fitur tambahan: automatic reconnection, rooms, namespaces, fallback ke HTTP polling, dan binary support.

JavaScript
// === Server (Express + Socket.IO) ===
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
    cors: { origin: '*' },
    pingInterval: 25000,
    pingTimeout: 20000,
    maxHttpBufferSize: 1e6 // 1MB
});

// Namespace: /chat
const chatNs = io.of('/chat');

chatNs.use((socket, next) => {
    // Middleware: authentication
    const token = socket.handshake.auth.token;
    if (verifyToken(token)) {
        socket.userId = decodeToken(token).id;
        next();
    } else {
        next(new Error('Authentication failed'));
    }
});

chatNs.on('connection', (socket) => {
    console.log(`User ${socket.userId} connected`);

    // Join room
    socket.on('join-room', (roomId) => {
        socket.join(roomId);
        socket.to(roomId).emit('user-joined', {
            userId: socket.userId,
            timestamp: Date.now()
        });
    });

    // Chat message
    socket.on('chat-message', (data) => {
        const { room, message } = data;
        chatNs.to(room).emit('new-message', {
            from: socket.userId,
            message: message,
            timestamp: Date.now()
        });
    });

    // Typing indicator
    socket.on('typing', (room) => {
        socket.to(room).emit('user-typing', { userId: socket.userId });
    });

    // Disconnect
    socket.on('disconnect', (reason) => {
        console.log(`User ${socket.userId} disconnected: ${reason}`);
    });
});

httpServer.listen(3000);

// === Client (Browser) ===
import { io } from 'socket.io-client';

const socket = io('https://server.example.com/chat', {
    auth: { token: 'jwt-token-here' },
    reconnection: true,
    reconnectionDelay: 1000,
    reconnectionAttempts: 10,
    transports: ['websocket', 'polling']
});

socket.on('connect', () => console.log('Connected!'));
socket.on('new-message', (data) => console.log('Message:', data));
socket.emit('join-room', 'room-general');
socket.emit('chat-message', { room: 'room-general', message: 'Halo semua!' });

Quiz Pemahaman

Pertanyaan 1: Berapa ukuran minimum overhead per WebSocket frame?

a) 8 bytes
b) 2 bytes
c) 4 bytes
d) 16 bytes

Pertanyaan 2: HTTP status code apa yang dikembalikan saat WebSocket upgrade berhasil?

a) 200 OK
b) 101 Switching Protocols
c) 301 Moved Permanently
d) 204 No Content

Pertanyaan 3: Apa yang terjadi jika client tidak mengirim masking pada frame ke server?

a) Server tetap menerima
b) Server harus menutup koneksi (protocol violation)
c) Frame di-drop tanpa close
d) Data diterima tapi terbalik

Pertanyaan 4: Mengapa sticky sessions diperlukan untuk WebSocket load balancing?

a) Karena DNS
b) Karena koneksi WebSocket bersifat stateful dan terikat ke server tertentu
c) Karena TLS
d) Karena cookie

Pertanyaan 5: Apa fungsi utama Ping/Pong di WebSocket?

a) Mengirim data
b) Mendeteksi koneksi mati dan menjaga koneksi tetap hidup
c) Mengenkripsi data
d) Kompresi payload
πŸ” Zoom
100%
🎨 Tema