Table of Contents

    WebSockets, SSE and long polling

    NETWORKING AND WEB REQUEST LIFECYCLE

    WebSockets, SSE and Long Polling

    Learn how web applications deliver live updates through full-duplex WebSocket connections, one-way Server-Sent Event streams and repeated long-polling requests.

    Introduction

    Traditional HTTP communication is request-driven. A client sends a request, and a server returns a response. This model works well when the client knows when information is required.

    Some applications need the server to deliver updates shortly after events occur, without waiting for a user to refresh the page.

    Examples include:

    • Chat messages
    • Notifications
    • Live dashboards
    • Job-progress updates
    • Delivery tracking
    • Collaborative editing
    • Monitoring events
    • Live scores
    • Market-data updates
    • Online multiplayer interactions

    Three common browser communication techniques are:

    • WebSockets for persistent bidirectional communication
    • Server-Sent Events for persistent server-to-client event streaming
    • Long polling for server-delayed responses followed by repeated requests

    Core idea: Choose a real-time communication technique from the required direction of communication, update frequency, reliability model, infrastructure support, reconnection behaviour, message size and expected number of concurrent clients.

    In the System Design curriculum, WebSockets, SSE and Long Polling is Topic 3.7 and completes the Networking and Web Request Lifecycle module. The accompanying practical work includes building a WebSocket echo service.

    Prerequisites

    # Prerequisite Why It Is Needed
    1 TCP/IP and UDP Real-time techniques depend on underlying transport behaviour.
    2 DNS and routing The client must locate and reach the communication endpoint.
    3 TLS Production connections require appropriate confidentiality, integrity and authentication.
    4 HTTP/1.1, HTTP/2 and HTTP/3 SSE and long polling use HTTP, while WebSocket establishment is related to HTTP communication.
    5 Cookies and sessions Persistent connections often need authenticated session context.
    6 JavaScript event handling Browser clients process asynchronous connection and message events.

    The Real-time Communication Problem

    A server receives new information.
    
    Question:
    
    How does the browser learn about the update
    without requiring the user to refresh the page?

    Traditional Polling

    Client -> Is new data available?
    Server -> No
    
    Wait for polling interval.
    
    Client -> Is new data available?
    Server -> No
    
    Wait for polling interval.
    
    Client -> Is new data available?
    Server -> Yes, here is the update.

    Traditional polling sends requests at fixed intervals. It is simple but can create many responses containing no new data, while update latency depends on the polling interval.

    Communication Models

    Technique Communication Direction Connection Model
    WebSocket Bidirectional Persistent framed connection
    Server-Sent Events Server to client Long-lived HTTP response stream
    Long polling Primarily server to client through repeated client requests One delayed HTTP response per polling cycle
    Traditional polling Client requests updates Repeated HTTP requests at fixed intervals

    WebSockets

    The WebSocket protocol provides two-way communication between a client and server. After connection establishment, both endpoints can send framed messages independently.

    WebSocket Lifecycle
    connect → opening handshake → exchange messages → closing handshake
    Client                                   Server
      |                                         |
      | Opening handshake                       |
      |---------------------------------------->|
      |                                         |
      | Handshake accepted                      |
      |<----------------------------------------|
      |                                         |
      | Client message                          |
      |---------------------------------------->|
      |                                         |
      | Server message                          |
      |<----------------------------------------|
      |                                         |
      | Messages can flow in either direction   |
      |<=======================================>|

    WebSockets are appropriate when:

    • Both client and server frequently send messages
    • Low message-delivery latency is important
    • A persistent interactive channel is required
    • Text and binary messages are needed
    • Repeated HTTP request overhead should be avoided

    WebSocket Opening Handshake

    A WebSocket connection begins with an opening handshake. In the traditional HTTP/1.1 form, the client requests a protocol upgrade.

    Client Request

    GET /realtime HTTP/1.1
    Host: app.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: random-base64-value
    Sec-WebSocket-Version: 13
    Origin: https://app.example.com

    Server Response

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: calculated-response-value

    After a successful handshake, communication follows WebSocket message framing rather than ordinary HTTP request-response semantics on that connection.

    Origin validation: Browser cookies can accompany a WebSocket handshake when cookie rules permit. A server should validate the expected Origin and authentication context rather than assuming that a successful network connection is authorized.

    ws and wss

    Scheme Meaning
    ws:// WebSocket communication without TLS protection
    wss:// WebSocket communication protected through TLS

    Production browser applications should normally use wss:// so connection authentication, confidentiality and integrity are appropriately protected.

    WebSocket Frames and Messages

    WebSocket data is transferred through frames. An application message can occupy one frame or be fragmented across several frames.

    Application Message
            |
            +--> Frame 1: beginning
            +--> Frame 2: continuation
            +--> Frame 3: final fragment

    WebSocket frame categories include:

    • Text data frames
    • Binary data frames
    • Continuation frames
    • Ping control frames
    • Pong control frames
    • Close control frames

    Browser WebSocket Client

    const socket =
        new WebSocket(
            "wss://app.example.com/realtime"
        );
    
    socket.addEventListener(
        "open",
        () => {
            const message = {
                type: "subscribe",
                channel: "job-status"
            };
    
            socket.send(
                JSON.stringify(message)
            );
        }
    );
    
    socket.addEventListener(
        "message",
        (event) => {
            const message =
                JSON.parse(event.data);
    
            console.log(
                "Received:",
                message
            );
        }
    );
    
    socket.addEventListener(
        "error",
        () => {
            console.error(
                "WebSocket error."
            );
        }
    );
    
    socket.addEventListener(
        "close",
        (event) => {
            console.log(
                "Connection closed:",
                event.code,
                event.reason
            );
        }
    );

    Production code should validate every received message before using its fields.

    WebSocket Application Protocol

    WebSocket supplies message transport but does not define the meaning of application messages.

    {
        "type": "chat.message",
        "messageId": "msg-1042",
        "conversationId": "room-27",
        "payload": {
            "text": "Example message"
        }
    }

    An application protocol should define:

    • Message types
    • Required and optional fields
    • Message identifiers
    • Request-response correlation
    • Versioning
    • Error messages
    • Authorization rules
    • Size limits
    • Delivery and ordering behaviour

    Ping, Pong and Heartbeats

    A persistent connection can appear open even when an intermediary or remote endpoint is no longer usable. Heartbeat mechanisms help detect stalled or abandoned connections.

    Server sends ping
            |
            v
    Client responds with pong
            |
            +--> Response received:
            |       connection remains active
            |
            +--> Response missing by deadline:
                    close connection
                    release resources

    Browser WebSocket APIs do not expose protocol-level ping and pong frames directly to ordinary JavaScript. Applications can implement an application-level heartbeat where necessary.

    Application Heartbeat

    const heartbeatInterval =
        30000;
    
    const timer =
        setInterval(
            () => {
                if (
                    socket.readyState ===
                    WebSocket.OPEN
                ) {
                    socket.send(
                        JSON.stringify({
                            type: "heartbeat",
                            sentAt:
                                new Date().toISOString()
                        })
                    );
                }
            },
            heartbeatInterval
        );
    
    socket.addEventListener(
        "close",
        () => {
            clearInterval(timer);
        }
    );

    The interval and timeout should be selected from infrastructure idle timeouts, expected network conditions and resource requirements.

    WebSocket Reconnection

    The browser WebSocket API does not automatically restore application state after a disconnected connection. The application should use a bounded reconnection policy.

    let reconnectAttempt = 0;
    let activeSocket = null;
    
    function calculateDelay(
        attempt
    ) {
        const maximumDelay = 30000;
        const baseDelay = 1000;
    
        const exponentialDelay =
            Math.min(
                maximumDelay,
                baseDelay * (2 ** attempt)
            );
    
        const randomJitter =
            Math.floor(
                Math.random() * 500
            );
    
        return exponentialDelay +
            randomJitter;
    }
    
    function connect() {
        activeSocket =
            new WebSocket(
                "wss://app.example.com/realtime"
            );
    
        activeSocket.addEventListener(
            "open",
            () => {
                reconnectAttempt = 0;
            }
        );
    
        activeSocket.addEventListener(
            "message",
            handleMessage
        );
    
        activeSocket.addEventListener(
            "close",
            () => {
                const delay =
                    calculateDelay(
                        reconnectAttempt
                    );
    
                reconnectAttempt += 1;
    
                setTimeout(
                    connect,
                    delay
                );
            }
        );
    }

    Reconnection should restore subscriptions and recover missed events according to the application's delivery model.

    Server-Sent Events

    Server-Sent Events, commonly abbreviated as SSE, allow a server to stream text events to a browser over a long-lived HTTP response.

    Browser
       |
       | GET /events
       v
    Server
       |
       | HTTP response remains open
       |
       +--> Event 1
       +--> Event 2
       +--> Event 3
       +--> Event 4

    SSE is suitable when:

    • Updates primarily flow from server to client
    • Text event data is sufficient
    • Automatic browser reconnection is useful
    • Normal HTTP authentication and infrastructure are preferred
    • The client can send occasional commands through separate HTTP requests

    SSE Response

    An SSE endpoint returns content using the text/event-stream media type.

    HTTP/1.1 200 OK
    Content-Type: text/event-stream
    Cache-Control: no-cache
    Connection: keep-alive
    
    id: 101
    event: job-progress
    data: {"jobId":"42","progress":25}
    
    id: 102
    event: job-progress
    data: {"jobId":"42","progress":50}

    Events are separated by a blank line. SSE event streams are interpreted as UTF-8 text.

    SSE Fields

    Field Purpose
    data Contains the event data
    event Defines a custom event type
    id Updates the client's last event identifier
    retry Suggests a reconnection delay in milliseconds
    Comment line A line beginning with a colon can be used for comments or connection activity

    Browser SSE Client

    const events =
        new EventSource(
            "/events"
        );
    
    events.addEventListener(
        "open",
        () => {
            console.log(
                "Event stream opened."
            );
        }
    );
    
    events.addEventListener(
        "job-progress",
        (event) => {
            const update =
                JSON.parse(event.data);
    
            console.log(
                update.jobId,
                update.progress
            );
        }
    );
    
    events.addEventListener(
        "message",
        (event) => {
            console.log(
                "Default event:",
                event.data
            );
        }
    );
    
    events.addEventListener(
        "error",
        () => {
            console.error(
                "Event stream interrupted."
            );
        }
    );

    Close the Stream

    events.close();

    PHP SSE Endpoint

    <?php
    
    declare(strict_types=1);
    
    session_start();
    
    if (!isset(
            $_SESSION['user_id']
        )) {
    
        http_response_code(401);
    
        exit;
    }
    
    header(
        'Content-Type: text/event-stream'
    );
    
    header(
        'Cache-Control: no-cache'
    );
    
    header(
        'X-Accel-Buffering: no'
    );
    
    $eventId =
        (string)time();
    
    $payload = [
        'type' => 'job-progress',
        'jobId' => '42',
        'progress' => 50
    ];
    
    echo 'id: ' .
        $eventId .
        "\n";
    
    echo "event: job-progress\n";
    
    echo 'data: ' .
        json_encode(
            $payload,
            JSON_THROW_ON_ERROR
        ) .
        "\n\n";
    
    if (
        ob_get_level() > 0
    ) {
        ob_flush();
    }
    
    flush();

    This example emits one event and then completes. A long-lived production stream requires controlled waiting, disconnect detection, heartbeats, deadlines, bounded event delivery and graceful shutdown.

    SSE Reconnection

    The browser's EventSource implementation reconnects when the connection is interrupted, unless the stream is deliberately closed or the server returns a response that stops reconnection according to the SSE rules.

    Connection opens
          |
          v
    Server sends events
          |
          v
    Connection is interrupted
          |
          v
    Browser waits
          |
          v
    Browser reconnects
          |
          v
    Event delivery continues

    Automatic reconnection restores the transport connection, but the application must define how missed events are recovered.

    Last-Event-ID

    An SSE server can assign an identifier to each event.

    id: 501
    event: notification
    data: {"message":"Build started"}
    
    id: 502
    event: notification
    data: {"message":"Build completed"}

    After a disconnection, the client can provide the last event identifier during reconnection. The server can use it to resume after the last acknowledged event when the application retains replayable event history.

    Client last received:
    Event 501
    
    Connection interrupted.
    
    Client reconnects with:
    Last-Event-ID: 501
    
    Server resumes with:
    Event 502

    Delivery rule: Event identifiers do not automatically create reliable delivery. The server needs an ordered event log, retention policy and replay logic when missed-event recovery is required.

    SSE Heartbeat Comments

    Periodic comment lines can keep the stream active through intermediaries and help detect connection failure.

    : heartbeat
    
    id: 601
    event: status
    data: {"status":"running"}
    
    : heartbeat

    Heartbeat frequency should account for proxy, load-balancer and server idle timeout policies.

    Long Polling

    In long polling, the client sends an HTTP request and the server delays the response until an event becomes available or a timeout is reached.

    Client sends request
           |
           v
    Server checks for new events
           |
           +--> Event available:
           |       respond immediately
           |
           +--> No event:
                   hold request open
                   |
                   +--> Event becomes available:
                   |       return event
                   |
                   +--> Timeout:
                           return empty result
    
    Client immediately sends next request.

    Long-polling Browser Client

    let stopped = false;
    let cursor = null;
    
    async function longPoll() {
        while (!stopped) {
            const parameters =
                new URLSearchParams();
    
            if (cursor !== null) {
                parameters.set(
                    "after",
                    cursor
                );
            }
    
            try {
                const response =
                    await fetch(
                        `/events/poll?${parameters}`,
                        {
                            method: "GET",
                            credentials: "same-origin",
                            headers: {
                                Accept:
                                    "application/json"
                            }
                        }
                    );
    
                if (!response.ok) {
                    throw new Error(
                        `HTTP ${response.status}`
                    );
                }
    
                const result =
                    await response.json();
    
                for (
                    const event of
                    result.events
                ) {
                    handleEvent(event);
                    cursor = event.id;
                }
            } catch (error) {
                console.error(
                    "Polling failed:",
                    error
                );
    
                await new Promise(
                    (resolve) => {
                        setTimeout(
                            resolve,
                            2000
                        );
                    }
                );
            }
        }
    }
    
    function stopLongPolling() {
        stopped = true;
    }
    
    longPoll();

    The cursor allows the client to request events after the last successfully processed item.

    Conceptual PHP Long-polling Endpoint

    <?php
    
    declare(strict_types=1);
    
    session_start();
    
    if (!isset(
            $_SESSION['user_id']
        )) {
    
        http_response_code(401);
    
        exit;
    }
    
    header(
        'Content-Type: application/json'
    );
    
    $after =
        $_GET['after'] ?? null;
    
    $deadline =
        microtime(true) + 25.0;
    
    $events = [];
    
    do {
        /*
         * Replace with a bounded event-store query.
         */
        $events =
            findEventsAfter(
                userId:
                    (int)$_SESSION['user_id'],
                cursor:
                    is_string($after)
                        ? $after
                        : null
            );
    
        if ($events !== []) {
            break;
        }
    
        usleep(
            250000
        );
    } while (
        microtime(true) < $deadline
    );
    
    echo json_encode(
        [
            'events' => $events
        ],
        JSON_THROW_ON_ERROR
    );

    Repeated database queries inside each waiting request can create heavy load. Production implementations should use appropriate event notification, queueing or asynchronous waiting mechanisms rather than unrestricted repeated queries.

    WebSockets vs SSE vs Long Polling

    Area WebSocket SSE Long Polling
    Direction Bidirectional Server to client Client requests, server returns available events
    Data model Text, binary and control frames UTF-8 text events Normal HTTP response content
    Connection Persistent WebSocket connection Persistent HTTP response Repeated delayed HTTP requests
    Browser reconnection Application-managed Built into EventSource Application-managed
    Missed-event recovery Application-defined Can use event IDs and replay Can use cursors and replay
    Per-update HTTP overhead No ordinary HTTP request per message after establishment No new HTTP request per event while stream remains open New HTTP request-response cycle after each completion
    Typical use Interactive two-way applications Notifications, feeds and progress streams Compatibility-oriented updates and simpler legacy integration

    Choosing the Right Technique

    Requirement Likely Choice
    Frequent bidirectional messages WebSocket
    Server sends a continuous text event stream SSE
    Client sends commands through normal HTTP while receiving updates SSE plus ordinary HTTP requests
    Existing infrastructure supports only ordinary request-response HTTP reliably Long polling
    Binary real-time messages are required WebSocket
    Automatic browser reconnection and event IDs are useful SSE
    Updates are infrequent and modest delay is acceptable Long polling or periodic polling can be considered

    Design strategy: Prefer the simplest technique that satisfies the communication requirement. Do not introduce a bidirectional stateful protocol when the application needs only a one-way notification stream.

    Event Delivery Semantics

    None of these communication mechanisms automatically guarantees exactly-once business processing.

    Delivery Model Application Behaviour
    At most once An event is not retried after uncertain delivery, so it can be missed
    At least once An event can be retried, so the consumer must handle duplicates
    Effectively once Retries are permitted, while identifiers and idempotent processing prevent duplicate business effects

    Reliable event processing can require:

    • Unique event identifiers
    • Ordered sequence numbers
    • Durable event storage
    • Client acknowledgement
    • Replay from a cursor
    • Duplicate detection
    • Idempotent event handling

    Backpressure

    Backpressure is required when the event producer can generate data faster than a client can receive or process it.

    Fast Producer
          |
          v
    Bounded Event Queue
          |
          +--> Client keeps up:
          |       continue delivery
          |
          +--> Client falls behind:
                  coalesce events
                  drop permitted events
                  disconnect client
                  or apply another defined policy

    A backpressure policy should define:

    • Maximum queued messages per connection
    • Maximum queued bytes
    • Events that can be combined
    • Events that can be discarded
    • Client-disconnection threshold
    • Replay behaviour after reconnection

    Horizontal Scaling

    Clients
       |
       v
    Load Balancer
       |
       +--> Realtime Server A --+
       |                        |
       +--> Realtime Server B --+--> Shared Event Broker
       |                        |
       +--> Realtime Server C --+
                                |
                                v
                         Application Events

    In a scaled architecture, an event can be generated on one application instance while the target client is connected to another instance.

    A scalable design can require:

    • A shared event broker
    • Subscription management
    • Connection ownership tracking
    • Per-user or per-channel routing
    • Event replay storage
    • Graceful connection draining
    • Connection-count and queue metrics

    Sticky Connections

    A long-lived connection remains attached to the server instance that accepted it. Load balancer affinity can influence which instance receives later connections, but a shared event-distribution mechanism is often still needed.

    Affinity alone does not solve:

    • Instance failure
    • Deployment draining
    • Missed-event recovery
    • Uneven connection distribution
    • Events generated by another instance

    Graceful Shutdown

    Deployment or shutdown begins
            |
            v
    Stop accepting new connections
            |
            v
    Mark instance as draining
            |
            v
    Notify or close existing clients
            |
            v
    Allow reconnection to healthy instances
            |
            v
    Release subscriptions and buffers
            |
            v
    Terminate instance

    A shutdown policy should define connection-drain duration, close reasons, reconnection behaviour and treatment of queued messages.

    Authentication

    Authentication should be completed when the connection is established and re-evaluated when the security context changes.

    Authentication options can include:

    • Secure session cookies
    • Short-lived access credentials
    • Mutual TLS for selected service clients
    • A one-time connection credential
    Credential in URL
    wss://app.example.com/realtime?token=long-lived-secret
    Controlled authentication design
    Use an approved protected authentication channel.
    
    Avoid exposing reusable credentials through:
    
    - URLs
    - Browser history
    - Proxy logs
    - Analytics
    - Referrer information
    - Diagnostic screenshots

    Authorization

    A connected client must be authorized for every subscription, channel, command and protected event.

    Authenticated connection
            |
            v
    Client requests subscription to channel A
            |
            v
    Server evaluates authorization
            |
            +--> Allowed:
            |       subscribe
            |
            +--> Denied:
                    reject request

    Do not authorize a subscription only from a client-supplied room, account, user or tenant identifier.

    Security Considerations

    Real-time endpoints should protect against:

    • Unauthorized connections
    • Unauthorized channel subscriptions
    • Cross-site WebSocket hijacking
    • Oversized messages
    • Malformed payloads
    • Connection floods
    • Event-subscription abuse
    • Slow consumers
    • Unbounded outbound queues
    • Replay of sensitive commands
    • Information leakage between users or tenants

    Recommended Controls

    • Use TLS-protected communication.
    • Validate WebSocket Origin values.
    • Authenticate every connection.
    • Authorize every channel and command.
    • Limit connections per account and source.
    • Apply message and event-size limits.
    • Validate message schemas.
    • Use bounded queues.
    • Apply idle and maximum-lifetime policies.
    • Redact credentials and event payloads from logs.

    Important Metrics

    Metric What It Helps Explain
    Active connections Current persistent-connection demand
    Connection establishment rate New and reconnecting clients
    Connection duration Normal lifecycle and premature disconnections
    Messages or events per second Real-time workload volume
    Delivery latency Time from event creation to client receipt
    Reconnect rate Network, deployment or idle-timeout instability
    Outbound queue depth Slow clients and backpressure
    Dropped or coalesced events Overload-policy activity
    Authentication failures Invalid or expired connection credentials
    Authorization rejections Denied subscription or command attempts
    Long-poll timeout rate Requests completed without new events
    Missed-event replay count Recovery activity after reconnecting

    Test a WebSocket Endpoint

    A WebSocket-capable command-line client can connect to an approved endpoint. Exact commands depend on the installed client.

    websocat wss://app.example.com/realtime

    Do not place production credentials directly in shell history. Tool availability and options vary by environment.

    Test an SSE Endpoint

    curl -N \
      -H 'Accept: text/event-stream' \
      https://app.example.com/events

    The -N option disables curl's output buffering so streamed events can be displayed as they arrive.

    Test a Long-polling Endpoint

    curl -v \
      'https://app.example.com/events/poll?after=500'

    Observe response time, returned cursor, event identifiers and timeout behaviour.

    Inspect Connections

    ss -tnp
    ss -tn state established

    Persistent WebSocket and HTTP streaming connections can appear as established transport connections. Identifying the application protocol can require process, TLS and application evidence.

    Packet Capture

    sudo tcpdump -i any -nn host SERVER_ADDRESS and port 443

    TLS normally prevents direct payload inspection without separately approved diagnostic key material. Packet captures can still show connection establishment, timing, packet loss and closure.

    Diagnostic warning: Real-time traffic can contain private messages, tokens and user activity. Protect packet captures, event logs and test credentials according to the applicable data-handling policy.

    Troubleshooting Workflow

    1. Confirm the endpoint URL and expected communication technique.
    2. Resolve the hostname and verify the selected route.
    3. Test transport and TLS connectivity.
    4. Check authentication and Origin validation.
    5. Confirm proxy and load-balancer support for long-lived connections.
    6. Inspect idle-timeout configuration.
    7. Verify message or event framing.
    8. Inspect reconnection and replay behaviour.
    9. Check connection counts and outbound queues.
    10. Verify channel-level authorization.
    11. Inspect application and broker health.
    12. Test graceful shutdown and reconnection.

    Common Mistakes

    1

    Using WebSockets for One-way Infrequent Updates

    SSE or long polling can provide a simpler design when the client does not require frequent bidirectional messages.

    2

    Assuming WebSocket Provides an Application Protocol

    The application must define message types, schemas, errors, identifiers, authorization and versioning.

    3

    Ignoring Origin Validation

    Browser authentication cookies can accompany a handshake. Validate the expected Origin and authentication context.

    4

    Keeping Unbounded Outbound Queues

    A slow client can cause continuous memory growth unless queue size and backpressure are controlled.

    5

    Reconnecting Every Client Immediately

    Immediate retries after an outage can create a reconnection storm. Use exponential backoff with jitter.

    6

    Assuming SSE Reconnection Prevents Missed Events

    Reliable recovery requires retained event history and replay from an identifier or cursor.

    7

    Allowing Proxy Buffering for SSE

    Buffered intermediaries can delay events instead of forwarding them as they are produced.

    8

    Polling a Database Repeatedly per Waiting Request

    Large numbers of long-polling clients can create continuous database query load.

    9

    Sending Credentials in Query Strings

    URLs can appear in logs, diagnostics and browser history.

    10

    Authorizing Only When the Connection Opens

    Each subscription and sensitive command requires an authorization decision.

    11

    Ignoring Deployment Draining

    Terminating instances without graceful connection handling can disconnect every attached client simultaneously.

    12

    Assuming Delivery Means Processing

    Receiving an event at the client does not prove that the client applied the event successfully or exactly once.

    Recommended Test Cases

    Test Expected Evidence
    WebSocket handshake An authorized client establishes the intended protocol connection
    Invalid Origin The WebSocket handshake is rejected
    Unauthorized subscription The client cannot subscribe to another user's or tenant's channel
    Oversized message The message is rejected without uncontrolled allocation
    Slow WebSocket client The bounded backpressure policy is applied
    SSE disconnect The client reconnects according to policy
    SSE replay Events after the last processed identifier are returned
    Long-poll timeout The request completes cleanly without fabricating an event
    Duplicate event Idempotent client processing prevents duplicate effects
    Instance shutdown Connections drain or reconnect to healthy instances
    Broker outage The system fails safely and reports event-delivery degradation
    Reconnection storm Backoff, jitter and admission controls protect the service

    Best Practices

    Recommended Practices

    • Choose the simplest technique that meets the direction and latency requirements.
    • Use secure transport for production connections.
    • Authenticate every connection.
    • Authorize every subscription, channel and command.
    • Validate WebSocket Origin values.
    • Define a versioned application message protocol.
    • Assign unique identifiers to replayable events.
    • Make event processing idempotent where duplicates are possible.
    • Use bounded per-connection queues.
    • Apply message-size and event-size limits.
    • Use heartbeat and idle-timeout policies.
    • Reconnect with exponential backoff and jitter.
    • Design missed-event replay explicitly.
    • Disable inappropriate intermediary buffering for SSE.
    • Use a shared broker for events across application instances.
    • Drain persistent connections during deployments.
    • Monitor connections, queues, delivery latency and reconnect rates.
    • Protect credentials, messages and diagnostic captures.

    Practice Exercise

    Build a secure WebSocket echo service and compare its behaviour with SSE and long polling.

    Requirements

    1. Use wss:// for the WebSocket endpoint.
    2. Authenticate the connection.
    3. Validate the browser Origin.
    4. Accept text messages up to a defined maximum size.
    5. Validate every message as structured JSON.
    6. Return the message with a server-generated identifier and timestamp.
    7. Support application-level heartbeat messages.
    8. Reject unsupported message types.
    9. Close idle connections according to policy.
    10. Reconnect the browser with exponential backoff and jitter.
    11. Create an SSE endpoint that streams server-generated status events.
    12. Create a long-polling endpoint using an event cursor.
    13. Test disconnect and replay behaviour.
    14. Compare connection count, event latency and request volume.

    Comparison Template

    Measurement WebSocket SSE Long Polling
    Communication direction Record evidence Record evidence Record evidence
    Connection lifecycle Record evidence Record evidence Record evidence
    Average event latency Record measurement Record measurement Record measurement
    Requests per event Record measurement Record measurement Record measurement
    Reconnect behaviour Record evidence Record evidence Record evidence
    Missed-event recovery Record evidence Record evidence Record evidence
    Slow-client behaviour Record evidence Record evidence Record evidence
    Infrastructure compatibility Record evidence Record evidence Record evidence

    Frequently Asked Questions

    1

    What is a WebSocket?

    A WebSocket is a persistent framed communication protocol that allows client and server to send messages in both directions.

    2

    What is SSE?

    Server-Sent Events provide a long-lived HTTP response through which a server streams UTF-8 text events to a client.

    3

    What is long polling?

    Long polling keeps an HTTP request open until an event becomes available or a timeout occurs, after which the client sends another request.

    4

    Which technique supports bidirectional communication?

    WebSocket provides bidirectional message communication over one persistent connection.

    5

    Can an SSE client send data to the server?

    The EventSource stream carries events from server to client. The client can send commands through separate HTTP requests.

    6

    Does WebSocket automatically reconnect?

    No. Browser applications normally implement their own reconnection, backoff and state-restoration logic.

    7

    Does SSE automatically reconnect?

    EventSource includes browser-managed reconnection behaviour, but missed-event recovery still requires an application replay design.

    8

    Can WebSocket send binary data?

    Yes. WebSocket supports text and binary application messages.

    9

    Can SSE send binary data directly?

    SSE event streams are UTF-8 text. Binary information requires an application encoding or another transport.

    10

    Does message delivery guarantee exactly-once processing?

    No. Applications need event identifiers, replay rules, duplicate handling and idempotent processing when delivery can be retried.

    11

    When should long polling be used?

    Long polling can be considered when ordinary HTTP compatibility is more important than the efficiency of a persistent streaming or bidirectional protocol.

    12

    What comes after this topic?

    This topic completes Networking and Web Request Lifecycle. The next module is API Design and Service Contracts, beginning with REST.

    Key Takeaway

    WebSockets provide persistent bidirectional message communication. SSE provides a simpler HTTP-based server-to-client text event stream with browser-managed reconnection. Long polling delays each HTTP response until an event or timeout and then repeats the request. Regardless of the technique, production systems need authentication, per-channel authorization, bounded queues, heartbeat and timeout policies, reconnection with backoff, missed-event replay, idempotent processing, shared event distribution and graceful connection draining.