WebSockets vs MQTT: Web vs IoT Communication Protocols
Quick Summary
MQTT and WebSockets serve different ecosystems: MQTT excels in IoT environments with its lightweight pub/sub model and Quality of Service guarantees, while WebSockets dominate web-based real-time communication. The two often work together, with MQTT handling device communication and WebSockets bridging to web applications.
At a Glance Comparison
Feature | MQTT | WebSockets |
---|---|---|
Protocol Model | Pub/Sub messaging | Point-to-point connection |
Target Environment | IoT devices | Web browsers & servers |
Message Routing | Topic-based | Direct connection |
QoS Levels | 0, 1, 2 (delivery guarantees) | Application-defined |
Browser Support | Via WebSocket bridge | Native (99%+) |
Message Retention | ✅ Built-in | Application-defined |
Will Messages | ✅ Last Will and Testament | ❌ Manual implementation |
Overhead | Minimal (2-byte header) | Low (2-14 byte frames) |
Binary Efficiency | ✅ Optimized | ✅ Supported |
Connection State | Session persistence | Stateful during connection |
Typical Use | Sensors, IoT devices | Web apps, real-time UI |
How MQTT Works
MQTT (Message Queuing Telemetry Transport) is a lightweight pub/sub protocol designed for constrained devices:
Core MQTT Concepts
- Broker-Centric: All communication goes through a central broker
- Topics: Hierarchical message routing (e.g.,
home/livingroom/temperature
) - QoS Levels: Delivery guarantees from fire-and-forget to exactly-once
- Retained Messages: Last known good value for new subscribers
- Will Messages: Notification if client disconnects unexpectedly
MQTT Client Example
// MQTT in browser via WebSocket transportconst mqtt = require('mqtt');
// Note: MQTT over WebSocket for browser compatibilityconst client = mqtt.connect('wss://broker.example.com:8083/mqtt', { clientId: 'web-client-' + Math.random().toString(16).substr(2, 8), clean: true, reconnectPeriod: 1000, // Last Will and Testament will: { topic: 'clients/web-client/status', payload: 'offline', qos: 1, retain: true }});
client.on('connect', () => { console.log('Connected to MQTT broker');
// Publish online status client.publish('clients/web-client/status', 'online', { qos: 1, retain: true });
// Subscribe to topics client.subscribe('sensors/+/temperature', { qos: 1 }); client.subscribe('commands/web-client/#', { qos: 2 });});
client.on('message', (topic, message) => { console.log(`Topic: ${topic}, Message: ${message.toString()}`);
// Route based on topic if (topic.startsWith('sensors/')) { updateSensorDisplay(topic, message); } else if (topic.startsWith('commands/')) { handleCommand(topic, message); }});
// Publish with QoSclient.publish('control/lights/living-room', JSON.stringify({ state: 'on', brightness: 80}), { qos: 1 });
import paho.mqtt.client as mqttimport json
def on_connect(client, userdata, flags, rc): print(f"Connected with result code {rc}")
# Subscribe to topics on connect client.subscribe("sensors/+/temperature", qos=1) client.subscribe("commands/device/#", qos=2)
# Publish online status client.publish("devices/device-1/status", "online", qos=1, retain=True)
def on_message(client, userdata, msg): print(f"Topic: {msg.topic}, Message: {msg.payload.decode()}")
# Process based on topic if msg.topic.startswith("sensors/"): process_sensor_data(msg.topic, msg.payload) elif msg.topic.startswith("commands/"): execute_command(msg.topic, msg.payload)
# Create client with Last Willclient = mqtt.Client("python-device")client.will_set("devices/device-1/status", "offline", qos=1, retain=True)
client.on_connect = on_connectclient.on_message = on_message
# Connect to brokerclient.connect("broker.example.com", 1883, 60)
# Start loopclient.loop_forever()
How WebSockets Work
WebSockets provide direct, bidirectional communication between client and server:
// QoS 0: At most once (fire and forget)client.publish('sensors/data', payload, { qos: 0 });// Fastest, but message may be lost
// QoS 1: At least once (acknowledged delivery)client.publish('alerts/warning', payload, { qos: 1 });// Guaranteed delivery, but may duplicate
// QoS 2: Exactly once (assured delivery)client.publish('commands/critical', payload, { qos: 2 });// Slowest, but exactly once delivery guaranteed// WebSocket - Direct connectionconst ws = new WebSocket('wss://api.example.com/socket');
ws.onopen = () => { console.log('WebSocket connected');
// Send identification ws.send(JSON.stringify({ type: 'identify', clientId: 'web-app-user-123' }));};
ws.onmessage = (event) => { const data = JSON.parse(event.data);
// Handle different message types switch(data.type) { case 'sensor-update': updateSensorDisplay(data); break; case 'command': handleCommand(data); break;
// Direct message sendingws.send(JSON.stringify({ type: 'control', textdevice: 'lightsliving-room', state: 'on', brightness: 80}));
Key Differences
Architecture Model
MQTT: Broker-centric pub/sub
- Decoupled publishers and subscribers
- Central message routing
- Topic-based filtering
- Many-to-many communication
WebSockets: Point-to-point Structureconnections
- Direct client-server communication
- No built-in routing
- Application-level message handling
- One-to-one or server-mediated broadcast
MQTT Message Structure
MQTT Fixed Header (2-5 bytes):┌─────────────────┬─────────────────┐│ Message Type │ Remaining Length││ + Flags (1B) │ (1-4 bytes) │└─────────────────┴─────────────────┘
Variable Header (optional):┌─────────────────────────────────────┐│ Protocol-specific fields (varies) │└─────────────────────────────────────┘
Payload:┌─────────────────────────────────────┐│ Application Data │└─────────────────────────────────────┘
Quality of Service
WebSocket Frame Structure
WebSocket Frame (2-14 bytes header): 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| | (if 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 ... |+---------------------------------------------------------------+**MQTT**: Built-in QoS levels- QoS 0: At most once (fire and forget)- QoS 1: At least once (acknowledged)- QoS 2: Exactly once (4-way handshake)
**WebSockets**: OverheadTCPreliabilityonly- Messages delivered in order- No built-in acknowledgment- Application must implement QoS
### Connection Handling**MQTT**: Session awareness- Clean/persistent sessions- Automatic resubscription- Queued messages for offline clients- Last Will and Testament**WebSockets**: Simple connection model- No built-in session persistence- Manual reconnection handling- No offline message queuing- Application-level presence
## Use Case Analysis
### When MQTT is the Clear Winner
✅ **IoT and embedded devices**:- Sensor networks- Smart home devices- Industrial IoT- Agricultural monitoring- Vehicle telemetry
// MQTT excels here due to:// - Pub/sub reduces connections from 200 to 1 (broker)// - Built-in QoS handles network interruptions// - Topic hierarchy organizes data logically// - Retained messages provide device status✅ **Constrained environments**:- Low bandwidth networks- Battery-powered devices- Unreliable connections- High-latency links
✅ **Message patterns**:- One-to-many broadcasting- Topic-based routing- Offline message delivery- Retained state values
### When WebSockets are Superior
✅ **Web applications**:- Browser-based real-time apps- Chat and messaging- Live dashboards- Collaborative tools- Gaming
✅ **Interactive features**:- Bidirectional communication- Low-latency requirements- Request-response patterns- Direct server push✅ **Existing web infrastructure**:- HTTP/HTTPS environments- Web server integration- CDN compatibility- Standard web security
## Bridging MQTT and WebSockets
The two protocols often work together in IoT platforms:
### Common Architecture Pattern
// MQTT for device communication// WebSockets for web interface
IoTPlatformmqttBrokerMQTTBrokerwsServerWebSocketServer();
// Bridge MQTT to WebSocket this.bridgeMessages bridgeMessages() { // Subscribe to all MQTT topics this.mqttBroker.subscribe('#', (topic, message) => { // Forward to WebSocket clients interested in this topic this.wsServer.broadcast({ type: 'mqtt-message', topic: topic, payload: message // Handle WebSocket commands this.wsServer.on('message', (client, data) => { if (data.type === 'publish') { // Publish to MQTT from WebSocket this.mqttBroker.publish(data.topic, data.payload); } else if (data.type === 'subscribe') { // Track WebSocket subscriptions client.subscriptions.add(data.topic);}
MQTT over WebSockets
Many MQTT brokers support WebSocket transport, enabling browser access:
// MQTT directly in browser via WebSocket transportconst client = mqtt.connect('wss://broker.example.com:8083/mqtt');
// This is still MQTT protocol, just transported over WebSocketclient.subscribe('sensors/+/data');client.publish('commands/device', 'restart');
Implementation Examples
Hybrid IoT DeviceDashboard
# IoT device using native MQTTimport paho.mqtt.client as mqttimport timeimport randomconst { randomBytes } = require('crypto');
class TemperatureSensor { constructor(config) { this.deviceId = config.deviceId || `sensor-${randomBytes(4).toString('hex')}`; this.location = config.location; this.reportingInterval = config.reportingInterval || 30000; // 30 seconds
// MQTT configuration this.client = mqtt.connect(config.brokerUrl, { clientId: this.deviceId,client = mqtt.Client("temp-sensor-001")
def publish_sensor_data(): while True: temperature = 20 + random.uniform(-5, 5) humidity = 60 + random.uniform(-10, 10)
# Publish sensor data via MQTT client.publish("sensors/temp-sensor-001/temperature", f"{temperature:.2f}", qos=1, retain=True) client.publish("sensors/temp-sensor-001/humidity", f"{humidity:.2f}", qos=1, retain=True)
time.sleep(10) # Update every 10 seconds
client.connect("mqtt.broker.com", 1883)client.loop_start()publish_sensor_data()
// mqttWeb dashboard using WebSocketclass SmartThermostat { constructor(config) { this.deviceId = config.deviceId; this.location = config.location; this.currentTemp = 20; // Start at 20°C this.targetTemp = 22; this.mode = 'auto'; // auto, heat, cool, off this.isHeating = false; this.isCooling = false;IoTDashboardwsWebSocket'wss://api.example.com/dashboard'setupWebSocket setupWebSocket() { this.ws.onopen = () => { // Subscribe to sensor updates this.ws.send(JSON.stringify({ action: 'subscribe', topics: ['sensors/+/temperature', 'sensors/+/humidity'] })); };
this.client = mqtt.connect(config.brokerUrl, { clientId: this.deviceId, clean: false, will: { topic: `devices/${this.deviceId}/status`, payload: JSON.stringify({ online: false, lastSeen: Date.now() }), qos: 1, retain: true this.ws.onmessage = (event) => { const data = JSON.parse(event.data);
// Update UI with sensor data if (data.topic && data.topic.includes('temperature')) { this.updateTemperature(data.device, data.value); } else if (data.topic && data.topic.includes('humidity')) { this.updateHumidity(data.device, data.value); } };
// Publish to HVAC system with QoS 2 for critical commands this.client.publish(`hvac/${this.location}/${system}`, JSON.stringify(command), { qos: 2 } // Exactly once delivery for HVAC commands );
console.log(`HVAC Command: ${system} ${enabled ? 'ON' : 'OFF'}`); }
publishStatus() { const status = { deviceId: this.deviceId, location: this.location, online: true, currentTemperature: this.currentTemp, targetTemperature: this.targetTemp, mode: this.mode, heating: this.isHeating, cooling: this.isCooling, timestamp: Date.now() };
// Retain status for new subscribers this.client.publish(`devices/${this.deviceId}/status`, JSON.stringify(status), { qos: 1, retain: true } ); }
startThermostatLogic() { // Update HVAC state every 30 seconds setInterval(() => { this.updateHVACState(); this.publishStatus(); }, 30000); }
emergencyStop() { console.log('EMERGENCY STOP - Turning off all HVAC systems');
this.isHeating = false; this.isCooling = false;
this.publishHVACCommand('heating', false); this.publishHVACCommand('cooling', false);
// Publish emergency status this.client.publish(`alerts/emergency`, JSON.stringify({ deviceId: this.deviceId, location: this.location, type: 'hvac_emergency_stop', timestamp: Date.now() }), { qos: 2 } ); }
sendCommand(device, command) { // Send control command via WebSocket this.ws.send(JSON.stringify({ action: 'command', device: device, command: command })); }
broadcastToWebClients(message) { const messageStr = JSON.stringify(message); }
updateTemperature(device, value) { document.getElementById(`${device}-temp`).textContent = `${value}°C`; }
updateHumidity(device, value) { document.getElementById(`${device}-humidity`).textContent = `${value}%`; }}
const dashboard = new IoTDashboard();
// Export for external health checksmodule.exports = dashboard;
Choosing the Right Protocol
Decision Factors
Choose MQTT when:
- Building IoT device networks
- Need pub/sub messaging patterns
- Require QoS guarantees
- Working with constrained devices
- Need offline message delivery
Choose WebSockets when:
- Building web applications
- Need browser compatibility
- Require bidirectional communication
- Working with existing web infrastructure
- Need simple point-to-point connections
Use both when:
- Building complete IoT platforms
- Need device-to-web communication
- Require different protocols for different parts
- Want to leverage strengths of each
Working with Protocol Providers
Rather than implementing raw protocols, consider established solutions: MQTT Platforms provide:
- Managed brokers
- Device management
- Security and authentication
- Scaling and redundancy
WebSocket Services offer:
- Global infrastructure
- Automatic scaling
- Protocol abstraction
- Multiple transport support
Unified Platforms like Ably provide:
- Both MQTT and WebSocket support
- Protocol interoperability
- Message bridging
- Unified API across protocols These solutions handle the complexity of protocol management while providing reliable, scalable infrastructure for real-time communication.
Conclusion
The choice between MQTT and WebSockets depends on your application’s domain. These aren’t fundamentally competing protocols but complementary technologies serving different needs. MQTT excels in IoT environments with its pub/sub model and QoS guarantees, while WebSockets provide the ideal solution for web-based real-time communication.
Key Takeaways:
- MQTT is designed for IoT, WebSockets for web applications
- They often work together in complete solutions
- MQTT over WebSockets enables browser access to MQTT
- Choose based on your environment and requirements
- Consider unified platforms that support both protocols
For most web developers, WebSockets remain the primary choice, with MQTT relevant when extending into IoT device communication.
Further Reading
- MQTT Protocol Specification
- WebSocket Protocol RFC 6455
- Building a WebSocket Application
- WebSocket Security Guide
While raw WebSocket or MQTT implementation requires significant effort, production applications often benefit from using established libraries or services that handle protocol complexities, bridging, and scaling challenges.
Written by Matthew O’Riordan, Co-founder & CEO of Ably, with experience building real-time systems reaching 2 billion+ devices monthly.