WebSockets
Ein Kommunikationsprotokoll für bidirektionale Echtzeit-Verbindungen zwischen Client und Server – im Gegensatz zu HTTP bleibt die Verbindung dauerhaft offen.
Ein Web-Standard, der es einem Server ermöglicht, kontinuierlich Daten an einen Browser zu senden – über eine einzige HTTP-Verbindung, ohne dass der Client erneut anfragen muss.
Klassische HTTP-Anfragen sind Request-Response: Der Client fragt, der Server antwortet, die Verbindung ist geschlossen. Für Echtzeit-Updates müsste der Client ständig neu anfragen (Polling) – ineffizient.
SSE hält die Verbindung offen: Der Server kann jederzeit neue Daten senden, ohne dass der Client erneut fragt. Das ist der Grund, warum ChatGPT Tokens einzeln erscheinen statt alles auf einmal: Das Modell streamt über SSE, Token für Token.
SSE vs. WebSockets vs. Polling:
| Aspekt | Polling | SSE | WebSockets |
|---|---|---|---|
| Richtung | Client → Server | Server → Client | Bidirektional |
| Protokoll | HTTP | HTTP | WS |
| Reconnect | Manuell | Automatisch | Manuell |
| Komplexität | Niedrig | Niedrig | Mittel |
| Use Case | Einfache Updates | Streaming, Feeds | Chat, Games |
app.get('/api/generate', async (req, res) => {
const { prompt } = req.query;
// SSE-Headers setzen
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// LLM-Stream
const stream = await openai.chat.completions.create({
model: 'gpt-5',
messages: [{ role: 'user', content: prompt }],
stream: true,
});
for await (const chunk of stream) {
const token = chunk.choices[0]?.delta?.content || '';
if (token) {
res.write(`data: ${JSON.stringify({ token })}\n\n`);
}
}
res.write('data: [DONE]\n\n');
res.end();
});
const source = new EventSource(`/api/generate?prompt=${encodeURIComponent(prompt)}`);
let output = '';
source.onmessage = (event) => {
if (event.data === '[DONE]') {
source.close();
return;
}
const { token } = JSON.parse(event.data);
output += token;
document.getElementById('output').textContent = output;
};
source.onerror = () => source.close();
id: 1
event: token
data: {"token": "Hallo"}
id: 2
event: token
data: {"token": " Welt"}
data: [DONE] SSE ist wie ein Radio: Du schaltest es ein (öffnest die Verbindung) und der Sender (Server) spielt kontinuierlich Musik (Daten), ohne dass du jedes Mal neu anfragen musst. WebSockets wären ein Telefongespräch – beide können jederzeit sprechen.
Einweg-Kommunikation: Server → Client (kein Client → Server über dieselbe Verbindung)
Basiert auf normalem HTTP – einfacher als WebSockets, automatisches Reconnect
Ideal für KI-Streaming: Token für Token in Echtzeit an den Browser senden
LLM-Token-Streaming
ChatGPT-ähnliches Streaming: Tokens werden sofort angezeigt, sobald das Modell sie generiert
Live-Dashboards
Echtzeit-Metriken, Logs oder Status-Updates ohne Polling
Benachrichtigungen
Server-seitige Events (neue Nachricht, Job abgeschlossen) sofort an den Browser senden
SSE: Einweg (Server → Client), HTTP-basiert, automatisches Reconnect, einfacher zu implementieren. WebSockets: Bidirektional (beide Seiten können senden), eigenes Protokoll, mehr Overhead. Für KI-Streaming ist SSE die bessere Wahl – einfacher, ausreichend und HTTP/2-kompatibel.
Ja, und das ist ein großer Vorteil gegenüber WebSockets. HTTP/2 multiplext mehrere SSE-Verbindungen über eine einzige TCP-Verbindung. Bei HTTP/1.1 gibt es ein Limit von 6 gleichzeitigen Verbindungen pro Domain – mit HTTP/2 kein Problem.
Der Browser reconnectet automatisch, wenn die SSE-Verbindung abbricht. Der Server kann eine Last-Event-ID mitsenden, damit der Client beim Reconnect weiß, ab welchem Event er weitermachen soll – kein Event geht verloren.