Back to Technology

Complete Protocols Master Part 11: Real-Time Protocols

January 31, 2026 Wasil Zafar 40 min read

Master real-time communication: WebSockets for bidirectional messaging, WebRTC for peer-to-peer video, SIP/RTP for VoIP, and XMPP for chat applications.

Table of Contents

  1. Introduction
  2. WebSockets
  3. Server-Sent Events
  4. WebRTC
  5. SIP & RTP (VoIP)
  6. XMPP (Chat)
  7. Comparison
  8. Summary

Introduction: Real-Time Communication

HTTP's request-response model doesn't work for real-time applications. When you need instant updates—chat messages, live scores, video calls—you need protocols designed for bidirectional, low-latency communication.

Series Context: This is Part 11 of 20 in the Complete Protocols Master series. Real-time protocols operate at the Application Layer, often building on TCP (WebSockets), UDP (RTP), or both (WebRTC).
Overview

Real-Time Protocol Landscape

ProtocolUse CaseTransportBrowser Support
WebSocketsBidirectional messagingTCP✅ Native
SSEServer push (one-way)HTTP✅ Native
WebRTCP2P audio/videoUDP/TCP✅ Native
SIPCall signalingTCP/UDP❌ SIP.js
RTPMedia streamingUDPVia WebRTC
XMPPIM/PresenceTCPStanza.io
# Real-time vs Request-Response

# HTTP: Request-Response
# Client: "Any new messages?"
# Server: "No"
# (1 second later)
# Client: "Any new messages?"
# Server: "No"
# (Polling wastes bandwidth, adds latency)

# WebSocket: Bidirectional
# Client: "Open connection"
# Server: "Connected"
# Server: "New message: Hello!"  (instant push)
# Client: "Send: Hi back!"
# (Connection stays open)

# Key differences:
comparison = {
    "HTTP Polling": {
        "connection": "Open, close, repeat",
        "latency": "High (request interval)",
        "bandwidth": "High (headers each time)",
        "bidirectional": False
    },
    "WebSocket": {
        "connection": "Persistent",
        "latency": "Low (instant)",
        "bandwidth": "Low (minimal framing)",
        "bidirectional": True
    }
}

for proto, features in comparison.items():
    print(f"\n{proto}:")
    for key, value in features.items():
        print(f"  {key}: {value}")

WebSockets

WebSockets (RFC 6455) provide full-duplex communication over a single TCP connection. They're the foundation of real-time web applications—chat, gaming, live updates.

Diagram showing the WebSocket HTTP upgrade handshake flow from initial GET request to full-duplex connection
WebSocket connection upgrade: HTTP handshake transitions to persistent full-duplex communication
Handshake

WebSocket Connection Upgrade

WebSocket starts as HTTP, then upgrades:

CLIENT REQUEST:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

SERVER RESPONSE:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

After this handshake:
• No more HTTP headers
• Binary framing only
• Full-duplex communication
• Either side can send anytime
# WebSocket server with Python (websockets library)

async def websocket_server_example():
    """WebSocket server demonstration"""
    
    print("WebSocket Server Example")
    print("=" * 50)
    
    print("""
    import asyncio
    import websockets
    
    # Store connected clients
    clients = set()
    
    async def handler(websocket, path):
        # Register client
        clients.add(websocket)
        try:
            async for message in websocket:
                print(f"Received: {message}")
                
                # Broadcast to all clients
                for client in clients:
                    if client != websocket:
                        await client.send(f"User said: {message}")
        finally:
            clients.remove(websocket)
    
    # Start server
    async def main():
        async with websockets.serve(handler, "localhost", 8765):
            print("WebSocket server running on ws://localhost:8765")
            await asyncio.Future()  # Run forever
    
    asyncio.run(main())
    """)
    
    print("\nInstall: pip install websockets")

asyncio.run(websocket_server_example()) if 'asyncio' in dir() else websocket_server_example()
// WebSocket client (JavaScript)

// Browser WebSocket API
const socket = new WebSocket('wss://server.example.com/chat');

// Connection opened
socket.addEventListener('open', (event) => {
    console.log('Connected!');
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', (event) => {
    console.log('Message from server:', event.data);
});

// Connection closed
socket.addEventListener('close', (event) => {
    console.log('Disconnected');
});

// Handle errors
socket.addEventListener('error', (error) => {
    console.error('WebSocket error:', error);
});

// Send message
function sendMessage(text) {
    if (socket.readyState === WebSocket.OPEN) {
        socket.send(text);
    }
}

// Close connection
function disconnect() {
    socket.close();
}
Socket.io

Socket.io: WebSocket with Fallbacks

// Socket.io adds: reconnection, rooms, namespaces

// Server (Node.js)
const io = require('socket.io')(3000);

io.on('connection', (socket) => {
    console.log('Client connected:', socket.id);
    
    // Join a room
    socket.join('room-123');
    
    // Listen for events
    socket.on('chat message', (msg) => {
        // Broadcast to room
        io.to('room-123').emit('chat message', msg);
    });
    
    socket.on('disconnect', () => {
        console.log('Client disconnected');
    });
});

// Client (Browser)
const socket = io('http://localhost:3000');

socket.on('connect', () => {
    console.log('Connected!');
});

socket.emit('chat message', 'Hello everyone!');

socket.on('chat message', (msg) => {
    console.log('Message:', msg);
});

Server-Sent Events (SSE)

SSE provides one-way server-to-client streaming over HTTP. Simpler than WebSockets when you only need server push (notifications, live feeds).

Diagram comparing SSE unidirectional server-to-client streaming vs WebSocket bidirectional communication
Server-Sent Events: unidirectional push from server to client over standard HTTP
SSE vs WebSocket

When to Use SSE

SSE Advantages:
• Simpler than WebSocket
• Works over HTTP/2 (multiplexing)
• Auto-reconnection built-in
• Event IDs for resume after disconnect

SSE Limitations:
• One-way only (server → client)
• Text data only (no binary)
• Limited connections per browser (6 HTTP/1.1)

Use SSE for:
✅ Live scores
✅ News feeds
✅ Notifications
✅ Stock tickers

Use WebSocket for:
✅ Chat (bidirectional)
✅ Gaming (low latency)
✅ Collaborative editing
# SSE server with Python (Flask)

from flask import Flask, Response
import time

app = Flask(__name__)

def generate_events():
    """Generator for SSE events"""
    count = 0
    while True:
        count += 1
        # SSE format: data: message\n\n
        yield f"data: Event {count} at {time.strftime('%H:%M:%S')}\n\n"
        time.sleep(1)

@app.route('/events')
def events():
    return Response(
        generate_events(),
        mimetype='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive'
        }
    )

# Run: flask run
# Client connects to /events and receives stream
// SSE client (JavaScript)

// Create EventSource
const eventSource = new EventSource('/events');

// Listen for messages
eventSource.onmessage = (event) => {
    console.log('Received:', event.data);
};

// Named events
eventSource.addEventListener('notification', (event) => {
    console.log('Notification:', event.data);
});

// Error handling (auto-reconnects)
eventSource.onerror = (error) => {
    console.error('SSE error:', error);
};

// Close connection
// eventSource.close();

WebRTC: Browser Peer-to-Peer

WebRTC (Web Real-Time Communication) enables peer-to-peer audio, video, and data directly between browsers—no plugins needed. Powers video calls, screen sharing, and file transfer.

WebRTC peer-to-peer connection architecture showing signaling server, STUN/TURN servers, and direct media flow between browsers
WebRTC architecture: signaling server exchanges SDP, STUN/TURN traverses NAT, media flows peer-to-peer
P2P But Needs Servers: WebRTC is peer-to-peer for media, but still needs: (1) Signaling server to exchange connection info, (2) STUN/TURN servers to traverse NAT/firewalls.
Architecture

WebRTC Connection Flow

WebRTC Connection Steps:

1. SIGNALING (via WebSocket/HTTP)
   • Exchange SDP (Session Description Protocol)
   • Describes media capabilities

2. ICE CANDIDATE GATHERING
   • STUN: Discover public IP/port
   • TURN: Relay if direct P2P fails
   
3. ICE CONNECTIVITY CHECKS
   • Try all candidate pairs
   • Select best path (usually direct P2P)

4. DTLS HANDSHAKE
   • Secure the connection
   • Exchange encryption keys

5. MEDIA/DATA FLOW
   • SRTP for audio/video (encrypted RTP)
   • SCTP for data channels

STUN vs TURN:
STUN: "What's my public IP?" (cheap, just discovery)
TURN: "Relay my traffic" (expensive, when P2P fails)
// WebRTC peer connection example

// Create peer connection
const config = {
    iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { 
            urls: 'turn:turn.example.com',
            username: 'user',
            credential: 'pass'
        }
    ]
};

const pc = new RTCPeerConnection(config);

// Handle ICE candidates
pc.onicecandidate = (event) => {
    if (event.candidate) {
        // Send candidate to peer via signaling server
        sendToSignaling({ type: 'candidate', candidate: event.candidate });
    }
};

// Handle incoming tracks (audio/video)
pc.ontrack = (event) => {
    document.getElementById('remoteVideo').srcObject = event.streams[0];
};

// Add local media
async function startCall() {
    const stream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true
    });
    
    // Show local video
    document.getElementById('localVideo').srcObject = stream;
    
    // Add tracks to peer connection
    stream.getTracks().forEach(track => pc.addTrack(track, stream));
    
    // Create and send offer
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    sendToSignaling({ type: 'offer', sdp: offer });
}

// Handle incoming offer
async function handleOffer(offer) {
    await pc.setRemoteDescription(new RTCSessionDescription(offer));
    const answer = await pc.createAnswer();
    await pc.setLocalDescription(answer);
    sendToSignaling({ type: 'answer', sdp: answer });
}

// Handle incoming answer
async function handleAnswer(answer) {
    await pc.setRemoteDescription(new RTCSessionDescription(answer));
}

// Handle incoming ICE candidate
async function handleCandidate(candidate) {
    await pc.addIceCandidate(new RTCIceCandidate(candidate));
}
Data Channels

WebRTC for Data (Not Just Video)

// WebRTC Data Channels: P2P data transfer

const pc = new RTCPeerConnection(config);

// Create data channel (initiator)
const dataChannel = pc.createDataChannel('chat', {
    ordered: true,      // Guarantee order
    maxRetransmits: 3   // Reliability
});

dataChannel.onopen = () => {
    console.log('Data channel open');
    dataChannel.send('Hello peer!');
};

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

// Handle incoming data channel (responder)
pc.ondatachannel = (event) => {
    const channel = event.channel;
    channel.onmessage = (e) => console.log('Received:', e.data);
};

// Use cases:
// • P2P file transfer (no server storage!)
// • Low-latency gaming
// • Collaborative editing

SIP & RTP: VoIP Protocols

SIP (Session Initiation Protocol) handles call signaling—ringing, answering, hanging up. RTP (Real-time Transport Protocol) carries the actual audio/video.

SIP call signaling flow with INVITE, 180 Ringing, 200 OK and parallel RTP media stream between endpoints
SIP handles call setup signaling while RTP carries the actual audio/video media stream
SIP Basics

SIP Call Flow

SIP Call Setup:

Alice                 Server                 Bob
  |                     |                     |
  |------ INVITE ------>|                     |
  |                     |------ INVITE ------>|
  |                     |<----- 180 Ringing --|
  |<---- 180 Ringing ---|                     |
  |                     |<----- 200 OK -------|
  |<---- 200 OK --------|                     |
  |------- ACK -------->|                     |
  |                     |------- ACK -------->|
  |                     |                     |
  |<========== RTP Media Flow =============>|
  |                     |                     |
  |------- BYE -------->|                     |
  |                     |------- BYE -------->|
  |                     |<----- 200 OK -------|
  |<---- 200 OK --------|                     |

SIP Methods:
INVITE    - Start call
ACK       - Confirm INVITE
BYE       - End call
CANCEL    - Cancel pending INVITE
REGISTER  - Register with server
OPTIONS   - Query capabilities
# SIP INVITE message example

INVITE sip:bob@example.com SIP/2.0
Via: SIP/2.0/UDP alice-pc.example.com:5060
From: "Alice" <sip:alice@example.com>;tag=1234
To: "Bob" <sip:bob@example.com>
Call-ID: 987654321@alice-pc.example.com
CSeq: 1 INVITE
Contact: <sip:alice@alice-pc.example.com:5060>
Content-Type: application/sdp
Content-Length: 200

v=0
o=alice 2890844526 2890844526 IN IP4 alice-pc.example.com
s=Phone Call
c=IN IP4 alice-pc.example.com
t=0 0
m=audio 49170 RTP/AVP 0 8 97
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:97 opus/48000/2
RTP

RTP: Real-time Transport Protocol

RTP Header (12 bytes minimum):

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       Sequence Number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             SSRC                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Payload                            |

Key Fields:
V  = Version (2)
PT = Payload Type (codec: 0=PCMU, 8=PCMA, etc.)
Sequence = Order packets (detect loss)
Timestamp = Synchronize playback
SSRC = Source identifier

RTP uses UDP because:
• Low latency (no retransmission delay)
• Packet loss acceptable (brief glitch vs delay)
• Timing critical (can't wait for retransmit)

XMPP: Extensible Messaging

XMPP (Extensible Messaging and Presence Protocol) is an open standard for instant messaging, presence, and multi-user chat. Used by WhatsApp (modified), Google Talk, and many enterprise chat systems.

XMPP federated messaging architecture with XML stanza routing between clients and servers
XMPP federated architecture: XML stanzas route between distributed servers for messaging and presence
XMPP Stanzas

XMPP Message Format (XML)

<!-- Message Stanza -->
<message from="alice@example.com" 
         to="bob@example.com" 
         type="chat">
    <body>Hello Bob!</body>
</message>

<!-- Presence Stanza -->
<presence from="alice@example.com">
    <show>away</show>
    <status>In a meeting</status>
</presence>

<!-- IQ (Info/Query) Stanza -->
<iq type="get" id="roster1">
    <query xmlns="jabber:iq:roster"/>
</iq>

XMPP Stanza Types:
• message - Chat messages
• presence - Online status
• iq - Request/response queries
# XMPP client example (slixmpp library)

def demonstrate_xmpp():
    """Show XMPP concepts"""
    
    print("XMPP Example (slixmpp)")
    print("=" * 50)
    
    print("""
    import slixmpp
    
    class ChatBot(slixmpp.ClientXMPP):
        def __init__(self, jid, password):
            super().__init__(jid, password)
            
            # Event handlers
            self.add_event_handler('session_start', self.start)
            self.add_event_handler('message', self.message)
        
        async def start(self, event):
            # Send presence
            self.send_presence()
            # Get contact list
            await self.get_roster()
        
        def message(self, msg):
            if msg['type'] == 'chat':
                print(f"From {msg['from']}: {msg['body']}")
                # Reply
                msg.reply(f"You said: {msg['body']}").send()
    
    # Connect
    bot = ChatBot('bot@example.com', 'password')
    bot.connect()
    bot.process(forever=True)
    """)
    
    print("\nXMPP Features:")
    print("• Federated (anyone can run a server)")
    print("• Multi-user chat (MUC) rooms")
    print("• End-to-end encryption (OMEMO)")
    print("• Extensible via XEPs")

demonstrate_xmpp()

Protocol Comparison

Decision Guide

Choosing the Right Protocol

Use CaseBest ChoiceReason
Chat applicationWebSocket / Socket.ioBidirectional, low latency
Live notificationsSSESimpler, HTTP/2 friendly
Video conferencingWebRTCP2P, low latency
VoIP / PBXSIP + RTPStandard, interoperable
IM with presenceXMPPFederated, feature-rich
Gaming (browser)WebSocket + WebRTCLow latency, P2P data
Stock tickerSSEOne-way, auto-reconnect
# Protocol selection helper

def recommend_protocol(use_case):
    """Recommend real-time protocol based on use case"""
    
    recommendations = {
        "bidirectional_chat": {
            "protocol": "WebSocket",
            "library": "Socket.io / ws",
            "reason": "Full-duplex, reliable delivery"
        },
        "server_notifications": {
            "protocol": "Server-Sent Events",
            "library": "Native EventSource",
            "reason": "Simple, auto-reconnect, HTTP/2"
        },
        "video_call": {
            "protocol": "WebRTC",
            "library": "Simple-peer / Mediasoup",
            "reason": "P2P, low latency, native browser"
        },
        "voip_pbx": {
            "protocol": "SIP + RTP",
            "library": "Opal / FreeSWITCH",
            "reason": "Telephony standard, PSTN interop"
        },
        "file_transfer_p2p": {
            "protocol": "WebRTC Data Channel",
            "library": "Simple-peer",
            "reason": "Direct P2P, no server storage"
        },
        "live_collaboration": {
            "protocol": "WebSocket + OT/CRDT",
            "library": "Y.js / ShareDB",
            "reason": "Real-time sync, conflict resolution"
        }
    }
    
    if use_case in recommendations:
        rec = recommendations[use_case]
        return f"{rec['protocol']} ({rec['library']}) - {rec['reason']}"
    return "WebSocket (general purpose)"

print("Protocol Recommendations")
print("=" * 50)
for case in ["bidirectional_chat", "server_notifications", "video_call", "voip_pbx"]:
    print(f"\n{case}:")
    print(f"  → {recommend_protocol(case)}")

Summary & Next Steps

Key Takeaways:
  • WebSocket: Bidirectional over TCP, foundation for real-time web
  • SSE: Server push only, simpler than WebSocket
  • WebRTC: P2P audio/video/data, requires signaling
  • SIP/RTP: VoIP standard, telephony interoperability
  • XMPP: Open IM standard, federated architecture
Quiz

Test Your Knowledge

  1. WebSocket vs HTTP difference? (Persistent, bidirectional)
  2. When to use SSE over WebSocket? (Server-push only, simpler)
  3. Why does WebRTC need signaling? (Exchange SDP, ICE candidates)
  4. STUN vs TURN? (Discovery vs relay)
  5. What protocol carries VoIP audio? (RTP)