Zum Hauptinhalt springen

🔌 Was ist WebSocket?

📖 Definition

WebSocket ist ein Protokoll, das bidirektionale Echtzeit-Kommunikation zwischen Client und Server ermöglicht. Im Gegensatz zu HTTP wird die Verbindung nach dem Aufbau dauerhaft aufrechterhalten, und der Server kann Daten zuerst an den Client senden (Push). Es wird in Chat, Echtzeit-Benachrichtigungen, Spielen, Kollaborationstools usw. verwendet.

🎯 Durch Analogie verstehen

Telefon vs Post

HTTP (Post)
├─ Brief senden (Anfrage)
├─ Antwort erhalten (Antwort)
├─ Neuen Brief schreiben (Anfrage)
└─ Jedes Mal neuer Brief nötig (Overhead)

WebSocket (Telefon)
├─ Einmal Telefonverbindung herstellen
├─ Kontinuierliches Gespräch möglich
├─ Beide können zuerst sprechen
└─ Verbindung bis zum Auflegen aufrechterhalten

Lieferung vs Direktleitung

HTTP
┌─────────┐ Anfrage ┌─────────┐
│ Client │ ──────────→ │ Server │
└─────────┘ └─────────┘
┌─────────┐ Antwort ┌─────────┐
│ Client │ ←────────── │ Server │
└─────────┘ └─────────┘
Jedes Mal neue Verbindung!

WebSocket
┌─────────┐ ┌─────────┐
│ Client │ ←─────────→ │ Server │
└─────────┘ Persistente └─────────┘
Verbindung
↕ Bidirektional

⚙️ Funktionsprinzip

1. WebSocket-Verbindungsprozess

1. HTTP-Handshake-Anfrage
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade

2. Server-Genehmigung
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

3. WebSocket-Verbindung hergestellt
↕ Beginn der bidirektionalen Kommunikation

4. Datenübertragung
Nachricht ←→ Nachricht ←→ Nachricht

5. Verbindung schließen
close() aufrufen

2. HTTP vs WebSocket

HTTP (Unidirektional, Anfrage-Antwort)
Client → Server: Anfrage
Client ← Server: Antwort
[Verbindung geschlossen]

WebSocket (Bidirektional, Persistent)
Client ↔ Server
- Server kann zuerst Nachrichten senden
- Verbindung aufrechterhalten
- Echtzeit-Kommunikation

💡 Praktische Beispiele

Basis WebSocket (Client)

// Im Webbrowser
const ws = new WebSocket('ws://localhost:8080');

// Verbindung erfolgreich
ws.onopen = () => {
console.log('Verbindung hergestellt!');
ws.send('Hallo!'); // Nachricht senden
};

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

// Fehlerbehandlung
ws.onerror = (error) => {
console.error('Fehler:', error);
};

// Verbindung schließen
ws.onclose = () => {
console.log('Verbindung geschlossen');
};

// Nachricht senden
ws.send('Hello Server!');
ws.send(JSON.stringify({ type: 'chat', message: 'Hi' }));

// Verbindung schließen
ws.close();

Basis WebSocket Server (Node.js)

const WebSocket = require('ws');

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

console.log('WebSocket-Server läuft: ws://localhost:8080');

// Bei Client-Verbindung
wss.on('connection', (ws) => {
console.log('Client verbunden!');

// Willkommensnachricht
ws.send('Willkommen!');

// Nachricht empfangen
ws.on('message', (message) => {
console.log('Nachricht empfangen:', message.toString());

// An alle Clients übertragen
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message.toString());
}
});
});

// Verbindung schließen
ws.on('close', () => {
console.log('Client-Verbindung geschlossen');
});

// Fehlerbehandlung
ws.on('error', (error) => {
console.error('Fehler:', error);
});
});

Socket.io (Praktische Chat-Anwendung)

// ============ Server (server.js) ============
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

app.use(express.static('public'));

const users = new Map(); // Verbundene Benutzer

io.on('connection', (socket) => {
console.log('Benutzer verbunden:', socket.id);

// Benutzer beitritt
socket.on('join', (username) => {
users.set(socket.id, username);

// Beitrittsbenachrichtigung (an alle)
io.emit('user-joined', {
username,
userCount: users.size
});

console.log(`${username} beigetreten (insgesamt ${users.size} Personen)`);
});

// Chat-Nachricht
socket.on('chat-message', (message) => {
const username = users.get(socket.id);

// An alle übertragen
io.emit('chat-message', {
username,
message,
timestamp: new Date()
});
});

// Tippt gerade
socket.on('typing', () => {
const username = users.get(socket.id);
socket.broadcast.emit('user-typing', username);
});

// Verbindung schließen
socket.on('disconnect', () => {
const username = users.get(socket.id);
users.delete(socket.id);

// Austrittsbenachrichtigung
io.emit('user-left', {
username,
userCount: users.size
});

console.log(`${username} ausgetreten (verbleibend: ${users.size} Personen)`);
});
});

server.listen(3000, () => {
console.log('Server läuft: http://localhost:3000');
});
<!-- ============ Client (public/index.html) ============ -->
<!DOCTYPE html>
<html>
<head>
<title>Echtzeit-Chat</title>
<style>
#messages {
height: 300px;
overflow-y: auto;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
.message {
margin-bottom: 10px;
}
.system {
color: #999;
font-style: italic;
}
</style>
</head>
<body>
<h1>Echtzeit-Chat</h1>
<div id="messages"></div>
<div id="typing"></div>
<input id="message-input" type="text" placeholder="Nachricht eingeben...">
<button id="send-btn">Senden</button>

<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const messagesDiv = document.getElementById('messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const typingDiv = document.getElementById('typing');

// Benutzername eingeben
const username = prompt('Bitte Benutzername eingeben:') || 'Anonym';
socket.emit('join', username);

// Benutzer-Beitrittsbenachrichtigung
socket.on('user-joined', (data) => {
addMessage(`${data.username} ist beigetreten. (${data.userCount} verbunden)`, 'system');
});

// Benutzer-Austrittsbenachrichtigung
socket.on('user-left', (data) => {
addMessage(`${data.username} hat verlassen. (${data.userCount} verbunden)`, 'system');
});

// Chat-Nachricht empfangen
socket.on('chat-message', (data) => {
const time = new Date(data.timestamp).toLocaleTimeString();
addMessage(`[${time}] ${data.username}: ${data.message}`);
});

// Tippen anzeigen
socket.on('user-typing', (username) => {
typingDiv.textContent = `${username} tippt gerade...`;
setTimeout(() => {
typingDiv.textContent = '';
}, 1000);
});

// Nachricht senden
function sendMessage() {
const message = messageInput.value.trim();
if (message) {
socket.emit('chat-message', message);
messageInput.value = '';
}
}

sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});

// Tipp-Ereignis
let typingTimeout;
messageInput.addEventListener('input', () => {
clearTimeout(typingTimeout);
socket.emit('typing');
typingTimeout = setTimeout(() => {
// Tippen beenden
}, 500);
});

// Nachricht hinzufügen
function addMessage(text, className = '') {
const messageEl = document.createElement('div');
messageEl.className = `message ${className}`;
messageEl.textContent = text;
messagesDiv.appendChild(messageEl);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
</script>
</body>
</html>

Room-Funktion (Gruppen-Chat)

// Server
io.on('connection', (socket) => {
// Raum beitreten
socket.on('join-room', (roomId) => {
socket.join(roomId);
console.log(`${socket.id} ist Raum ${roomId} beigetreten`);

// Nur an Personen im selben Raum senden
socket.to(roomId).emit('user-joined-room', {
userId: socket.id,
roomId
});
});

// Nachricht an bestimmten Raum senden
socket.on('room-message', ({ roomId, message }) => {
// Nur an diesen Raum senden
io.to(roomId).emit('room-message', {
userId: socket.id,
message
});
});

// Raum verlassen
socket.on('leave-room', (roomId) => {
socket.leave(roomId);
socket.to(roomId).emit('user-left-room', socket.id);
});
});

Echtzeit-Benachrichtigungssystem

// Server
const notifications = io.of('/notifications');

notifications.on('connection', (socket) => {
console.log('Benachrichtigungsverbindung:', socket.id);

// Benutzer-Authentifizierung
const userId = socket.handshake.query.userId;

// Persönlichen Raum beitreten
socket.join(`user-${userId}`);

// Benachrichtigung an bestimmten Benutzer senden
socket.on('send-notification', ({ targetUserId, notification }) => {
notifications.to(`user-${targetUserId}`).emit('notification', notification);
});
});

// Benachrichtigung von überall senden
function sendNotification(userId, notification) {
notifications.to(`user-${userId}`).emit('notification', {
type: notification.type,
message: notification.message,
timestamp: new Date()
});
}

// Beispiel: Neue Bestellbenachrichtigung
app.post('/api/orders', async (req, res) => {
const order = await createOrder(req.body);

// Echtzeit-Benachrichtigung an Verkäufer
sendNotification(order.sellerId, {
type: 'new-order',
message: `Neue Bestellung eingegangen!`,
orderId: order.id
});

res.json(order);
});

Echtzeit-Kollaboration (Gemeinsame Bearbeitung)

// Server
io.on('connection', (socket) => {
socket.on('join-document', (docId) => {
socket.join(docId);

// Aktuellen Dokumentinhalt senden
const document = getDocument(docId);
socket.emit('document-loaded', document);
});

// Dokumentänderung
socket.on('document-change', ({ docId, changes }) => {
// Dokument aktualisieren
updateDocument(docId, changes);

// Änderungen an andere Benutzer senden
socket.to(docId).emit('document-updated', {
userId: socket.id,
changes
});
});

// Cursor-Position teilen
socket.on('cursor-move', ({ docId, position }) => {
socket.to(docId).emit('cursor-update', {
userId: socket.id,
position
});
});
});

🤔 Häufig gestellte Fragen

F1. WebSocket vs HTTP Polling?

A:

// HTTP Polling (Ineffizient)
// Client sendet regelmäßig Anfragen an Server

setInterval(() => {
fetch('/api/messages')
.then(res => res.json())
.then(messages => {
// Neue Nachrichten prüfen
});
}, 1000); // Anfrage jede Sekunde

// Probleme:
// - Unnötige Anfragen (Anfrage auch ohne neue Nachricht)
// - Verzögerungszeit (bis zu 1 Sekunde)
// - Server-Last

// WebSocket (Effizient)
// Bidirektionale Echtzeit-Kommunikation

const socket = io();

socket.on('new-message', (message) => {
// Neue Nachrichten sofort empfangen!
});

// Vorteile:
// - Sofortiger Empfang (keine Verzögerung)
// - Server-Push
// - Effizient

F2. Ist WebSocket immer besser?

A:

// ✅ WebSocket ist gut in diesen Fällen
1. Echtzeit-Chat
2. Echtzeit-Benachrichtigungen
3. Kollaborationstools (gemeinsame Bearbeitung)
4. Echtzeit-Spiele
5. Aktieninformationen (Echtzeit-Daten)
6. IoT-Gerätesteuerung

// ❌ WebSocket ist nicht erforderlich in diesen Fällen
1. Allgemeine Websites (Blog, Nachrichten)
2. REST API (CRUD)
3. Statischer Inhalt
4. Keine Echtzeit-Updates erforderlich

// HTTP ist besser in diesen Fällen:
// - Einfache Anfrage-Antwort
// - Caching erforderlich
// - RESTful-Design

F3. Socket.io vs natives WebSocket?

A:

// Natives WebSocket
const ws = new WebSocket('ws://localhost:8080');
ws.send('Hello');

// Vorteile: Leichtgewichtig und schnell
// Nachteile: Keine zusätzlichen Funktionen, kein Fallback

// Socket.io
const socket = io();
socket.emit('message', 'Hello');

// Vorteile:
// 1. Automatische Wiederverbindung
// 2. Room/Namespace-Unterstützung
// 3. Fallback (Polling wenn WebSocket nicht verfügbar)
// 4. Ereignisbasiert
// 5. Binär-Unterstützung

// Nachteile:
// 1. Groß (Bibliotheksgröße)
// 2. Nicht kompatibel mit nativem WebSocket

// Auswahlkriterien:
// Einfaches Projekt → Natives WebSocket
// Komplexes Projekt → Socket.io

F4. WebSocket-Sicherheit?

A:

// 1. wss:// verwenden (WebSocket Secure)
// Wie HTTP → HTTPS
// ws:// → wss://

const socket = new WebSocket('wss://example.com'); // ✅ Verschlüsselt
const socket = new WebSocket('ws://example.com'); // ❌ Klartext

// 2. Authentifizierung
// Socket.io
io.use((socket, next) => {
const token = socket.handshake.auth.token;

if (isValidToken(token)) {
next();
} else {
next(new Error('Authentifizierung fehlgeschlagen'));
}
});

// Client
const socket = io({
auth: {
token: 'user-token'
}
});

// 3. Berechtigungsprüfung
socket.on('delete-message', (messageId) => {
const userId = socket.userId;

if (canDeleteMessage(userId, messageId)) {
deleteMessage(messageId);
} else {
socket.emit('error', 'Keine Berechtigung');
}
});

// 4. Rate Limiting
const rateLimit = require('socket.io-rate-limit');

io.use(rateLimit({
max: 10, // Maximal 10
interval: 1000 // Pro Sekunde
}));

// 5. Eingabevalidierung
socket.on('message', (message) => {
// XSS-Prävention
const sanitized = sanitizeHtml(message);

if (sanitized.length > 1000) {
return socket.emit('error', 'Nachricht zu lang');
}

broadcast(sanitized);
});

F5. WebSocket-Verbindungsverwaltung?

A:

// 1. Automatische Wiederverbindung (Socket.io)
const socket = io({
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000
});

socket.on('disconnect', () => {
console.log('Verbindung unterbrochen');
});

socket.on('reconnect', () => {
console.log('Wiederverbindung erfolgreich');
});

// 2. Heartbeat (Verbindungsprüfung)
// Server
setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate(); // Bei fehlender Antwort schließen
}

ws.isAlive = false;
ws.ping(); // Ping senden
});
}, 30000);

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

// 3. Speicherverwaltung
const MAX_CONNECTIONS = 1000;

wss.on('connection', (ws) => {
if (wss.clients.size > MAX_CONNECTIONS) {
ws.close(1008, 'Server ist voll');
return;
}

// Verbindung verarbeiten...
});

// 4. Graceful Shutdown
process.on('SIGTERM', () => {
console.log('Server wird heruntergefahren...');

// Shutdown-Benachrichtigung an alle Verbindungen
wss.clients.forEach((ws) => {
ws.send(JSON.stringify({ type: 'server-shutdown' }));
ws.close();
});

wss.close(() => {
console.log('WebSocket-Server heruntergefahren');
process.exit(0);
});
});

🎓 Nächste Schritte

Wenn Sie WebSocket verstanden haben, lernen Sie als Nächstes:

  1. Was ist Node.js? - WebSocket-Server aufbauen
  2. Was ist React? - Echtzeit-UI implementieren
  3. Was ist Docker? (Dokumentation in Erstellung) - WebSocket-Server bereitstellen

Zum Üben

# Socket.io Chat-App erstellen

# 1. Projekt initialisieren
mkdir chat-app
cd chat-app
npm init -y

# 2. Pakete installieren
npm install express socket.io

# 3. Server erstellen (siehe Beispiel oben)
# server.js

# 4. Ausführen
node server.js

# 5. Zugriff auf http://localhost:3000

🎬 Zusammenfassung

WebSocket ist die Kerntechnologie des Echtzeit-Web:

  • Bidirektionale Kommunikation: Server ↔ Client
  • Persistente Verbindung: Einmal verbunden, kontinuierliche Nutzung
  • Echtzeit: Sofortige Datenübertragung
  • Effizient: Viel effizienter als Polling

Wenn Sie Echtzeit-Funktionen benötigen, verwenden Sie WebSocket! 🔌✨