Zum Inhalt springen
Deutsch

TCP

TcpSocketActor umschließt eine clientseitige TCP-Verbindung. Zum Lauschen auf einem Port (eingehende TCP-Verbindungen annehmen) lautet das Framework-Muster: “spawne pro eingehender Verbindung von einem Listener-Actor genau einen Client-Actor”.

import { ActorSystem, Props, TcpSocketActor } from 'actor-ts';
const tcp = system.spawn(
Props.create(() => new TcpSocketActor({
host: 'metrics-collector.example.com',
port: 8125,
})),
'tcp-client',
);
// Rohe Bytes senden:
tcp.tell({ kind: 'send', payload: new Uint8Array([0x01, 0x02, 0x03]) });
// Oder Text:
tcp.tell({ kind: 'send-text', payload: 'PING\n' });
// Eingehende Bytes subscribieren:
tcp.tell({ kind: 'subscribe', subscriber: protocolHandler });
interface TcpSocketActorSettings extends BrokerCommonSettings {
host: string;
port: number;
keepAlive?: boolean;
keepAliveDelay?: number;
noDelay?: boolean; // Nagles Algorithmus deaktivieren
framer?: TcpFramer; // optionale Frame-Parsing-Schicht
}
import { match } from 'ts-pattern';
class ProtocolHandler extends Actor<TcpInbound> {
override onReceive(msg: TcpInbound): void {
match(msg)
.with({ kind: 'data' }, (m) => this.handleBytes(m.payload)) // Uint8Array
.with({ kind: 'connected' }, () => { this.log.info('TCP verbunden'); })
.with({ kind: 'disconnected' }, () => { this.log.info('TCP getrennt'); })
.exhaustive();
}
}

Jede data-Nachricht trägt einen Chunk Bytes — KEINE logische Nachricht. TCP ist ein Byte-Stream; Framing ist dein Job.

import { LengthPrefixedFramer } from 'actor-ts';
new TcpSocketActor({
host, port,
framer: new LengthPrefixedFramer({ prefixSize: 4 }),
});

Optionaler Framer, der den Byte-Stream in diskrete Nachrichten parst. Das Framework liefert:

  • LengthPrefixedFramer — 4-Byte- (konfigurierbares) Längen- Prefix.
  • DelimitedFramer — zeilen-delimited oder durch eine bestimmte Byte-Folge abgeschlossen (Newline, NUL usw.).
  • Custom — implementiere TcpFramer für Protokolle wie HTTP/1.1, redis-protocol usw.

Mit einem Framer enthält die data-Nachricht von TcpInbound einen vollständigen Frame, nicht beliebige Chunks.

Das Framework liefert keinen “TCP-Listener-Actor” — schreib einen, der pro angenommener Verbindung TcpSocketActor-ähnliche Handler spawnt:

// In einem Init-Actor:
import net from 'node:net';
const server = net.createServer((socket) => {
const handler = system.spawn(
Props.create(() => new IncomingConnectionActor(socket)),
);
});
server.listen(port, host);

Für die meisten Anwendungsfälle gilt: bau dir keinen eigenen TCP-Server — nimm stattdessen HTTP, WebSocket oder ein höherrangiges Protokoll.

Drei legitime Einsatzfälle:

  1. Mit Legacy-Protokollen sprechen — proprietäre Protokolle, für die es keine höherrangigen Wrapper gibt.
  2. Eigene Binärprotokolle — Game-Server, Metric-Collector mit eigenem Wire-Format.
  3. Brücken zu Nicht-HTTP-Services — Message-Queues mit proprietärem Wire (z. B. einige Finanzprotokolle).

Für neue Anwendungsprotokolle gilt: greife nicht zuerst zu raw TCP. HTTP, WebSocket oder gRPC sind für fast alles die besseren Startpunkte.

  • I/O-Übersicht — das große Bild.
  • BrokerActor-Basis — der gemeinsame Lifecycle.
  • UDP — die verbindungslose Alternative.
  • gRPC — typisierter RPC über HTTP/2 — meist die bessere Wahl als raw TCP.