Files
michaelschiemer/docs/claude/sockets-module.md
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
2025-11-09 14:46:15 +01:00

672 lines
18 KiB
Markdown

# Sockets Module Documentation
## Übersicht
Das Sockets-Modul (`src/Framework/Sockets/`) bietet eine vollständige, type-safe Abstraktion über PHP's Socket-Erweiterung. Es ermöglicht:
- **Type-safe Socket-Operationen**: Alle Socket-Operationen verwenden Value Objects statt primitiver Typen
- **TCP/UDP Server & Client**: Vollständige Server- und Client-Implementierung
- **Unix Domain Sockets**: Unterstützung für Unix Domain Sockets
- **PCNTL Integration**: Socket-Server in geforkten Worker-Prozessen
- **Async Integration**: Non-blocking Socket I/O mit Fibers
- **Connection Pooling**: Automatisches Management von Socket-Verbindungen
## Architektur
### Modulstruktur
```
src/Framework/Sockets/
├── SocketService.php # Haupt-Service (Facade)
├── SocketFactory.php # Socket-Erstellung
├── SocketServer.php # Server-Operationen
├── SocketClient.php # Client-Operationen
├── SocketConnection.php # Connection Wrapper
├── SocketConnectionPool.php # Connection Pool Management
├── SocketsInitializer.php # DI-Initializer
├── Exceptions/ # Exception-Klassen
├── ValueObjects/ # Type-safe Value Objects
└── Integration/ # PCNTL & Async Integration
```
## Value Objects
### SocketAddress
Repräsentiert eine Socket-Adresse (IPv4, IPv6 oder Unix Domain Socket).
```php
use App\Framework\Sockets\ValueObjects\SocketAddress;
// IPv4 Adresse
$address = SocketAddress::ipv4('127.0.0.1', 8080);
// IPv6 Adresse
$address = SocketAddress::ipv6('::1', 8080);
// Unix Domain Socket
$address = SocketAddress::unix('/tmp/socket.sock');
// Von String parsen
$address = SocketAddress::fromString('127.0.0.1:8080');
$address = SocketAddress::fromString('/tmp/socket.sock');
// Eigenschaften
$host = $address->getHost(); // null für Unix Sockets
$port = $address->getPort(); // null für Unix Sockets
$path = $address->getUnixPath(); // null für Network Sockets
$protocol = $address->getProtocol(); // SocketProtocol Enum
$isUnix = $address->isUnixSocket();
$isNetwork = $address->isNetworkSocket();
$string = $address->toString(); // "127.0.0.1:8080" oder "/tmp/socket.sock"
```
### SocketType
Enum für Socket-Typen.
```php
use App\Framework\Sockets\ValueObjects\SocketType;
SocketType::TCP // SOCK_STREAM
SocketType::UDP // SOCK_DGRAM
SocketType::RAW // SOCK_RAW
SocketType::RDM // SOCK_RDM
SocketType::SEQPACKET // SOCK_SEQPACKET
// Methoden
$type->getName(); // "TCP", "UDP", etc.
$type->getValue(); // SOCK_STREAM, SOCK_DGRAM, etc.
$type->isReliable(); // true für TCP, SEQPACKET
$type->isConnectionless(); // true für UDP
```
### SocketProtocol
Enum für Protokoll-Familien.
```php
use App\Framework\Sockets\ValueObjects\SocketProtocol;
SocketProtocol::IPv4 // AF_INET
SocketProtocol::IPv6 // AF_INET6
SocketProtocol::UNIX // AF_UNIX
// Methoden
$protocol->getName(); // "IPv4", "IPv6", "Unix Domain"
$protocol->getValue(); // AF_INET, AF_INET6, AF_UNIX
$protocol->isNetwork(); // true für IPv4/IPv6
$protocol->isUnix(); // true für Unix Domain
```
### SocketOption
Enum für Socket-Optionen.
```php
use App\Framework\Sockets\ValueObjects\SocketOption;
SocketOption::SO_REUSEADDR
SocketOption::SO_REUSEPORT
SocketOption::SO_KEEPALIVE
SocketOption::SO_LINGER
SocketOption::SO_RCVBUF
SocketOption::SO_SNDBUF
SocketOption::SO_RCVTIMEO
SocketOption::SO_SNDTIMEO
SocketOption::TCP_NODELAY
SocketOption::TCP_KEEPIDLE
SocketOption::TCP_KEEPINTVL
SocketOption::TCP_KEEPCNT
// Methoden
$option->getName(); // "SO_REUSEADDR", etc.
$option->getLevel(); // SOL_SOCKET oder SOL_TCP
$option->getValue(); // SO_REUSEADDR, etc.
```
### SocketResource
Type-safe Wrapper für Socket-Ressourcen mit automatischem Cleanup.
```php
use App\Framework\Sockets\ValueObjects\SocketResource;
$resource = SocketResource::fromResource($socket, $type, $protocol);
// Eigenschaften
$socketResource = $resource->getResource(); // Native Socket-Resource
$type = $resource->getType(); // SocketType
$protocol = $resource->getProtocol(); // SocketProtocol
$isClosed = $resource->isClosed();
// Manuelles Schließen (automatisch im Destructor)
$resource->close();
```
## Core Services
### SocketFactory
Factory für Socket-Erstellung mit automatischem Option-Setup.
```php
use App\Framework\Sockets\SocketFactory;
use App\Framework\Sockets\ValueObjects\SocketAddress;
use App\Framework\Sockets\ValueObjects\SocketType;
use App\Framework\Sockets\ValueObjects\SocketProtocol;
$factory = new SocketFactory();
// TCP Socket erstellen
$address = SocketAddress::ipv4('127.0.0.1', 8080);
$socket = $factory->createTcp($address);
// UDP Socket erstellen
$socket = $factory->createUdp($address);
// Unix Domain Socket erstellen
$unixAddress = SocketAddress::unix('/tmp/socket.sock');
$socket = $factory->createUnix($unixAddress);
// Generische Erstellung
$socket = $factory->createServer(SocketType::TCP, SocketProtocol::IPv4);
$socket = $factory->createClient(SocketType::TCP, SocketProtocol::IPv4);
```
### SocketServer
Server-Operationen für Socket-Verwaltung.
```php
use App\Framework\Sockets\SocketServer;
use App\Framework\Sockets\ValueObjects\SocketAddress;
use App\Framework\Sockets\ValueObjects\SocketResource;
$server = new SocketServer();
$address = SocketAddress::ipv4('0.0.0.0', 8080);
// Socket binden
$server->bind($socket, $address);
// Auf Verbindungen lauschen
$server->listen($socket, 10); // backlog = 10
// Verbindung akzeptieren (non-blocking)
$connection = $server->accept($socket);
if ($connection !== null) {
// Verbindung verarbeiten
$clientSocket = $connection->getSocket();
$clientAddress = $connection->getAddress();
}
// Socket-Auswahl (select)
$read = [$socket];
$write = [];
$except = [];
$numReady = $server->select($read, $write, $except, 1); // 1 Sekunde Timeout
// Non-blocking/Blocking Mode
$server->setNonBlocking($socket);
$server->setBlocking($socket);
```
### SocketClient
Client-Operationen für Socket-Verbindungen.
```php
use App\Framework\Sockets\SocketClient;
use App\Framework\Sockets\ValueObjects\SocketAddress;
use App\Framework\Sockets\ValueObjects\SocketResource;
$client = new SocketClient();
$address = SocketAddress::ipv4('127.0.0.1', 8080);
// Verbindung herstellen
$client->connect($socket, $address);
// Daten lesen
$data = $client->read($socket, 1024); // null wenn keine Daten (non-blocking)
// Daten schreiben
$bytesWritten = $client->write($socket, "Hello, World!");
// Daten senden (mit Flags)
$bytesSent = $client->send($socket, $data, MSG_DONTWAIT);
// Daten empfangen
$data = $client->receive($socket, 1024, MSG_DONTWAIT);
// Verbindung schließen
$client->close($socket);
```
### SocketConnection
Wrapper für Socket-Verbindungen mit Metadaten.
```php
use App\Framework\Sockets\SocketConnection;
$connection = SocketConnection::create($socketResource, $address);
// Eigenschaften
$socket = $connection->getSocket();
$address = $connection->getAddress();
$connectionId = $connection->getConnectionId();
$isClosed = $connection->isClosed();
// Verbindung schließen
$connection->close();
```
### SocketConnectionPool
Verwaltung mehrerer Socket-Verbindungen.
```php
use App\Framework\Sockets\SocketConnectionPool;
$pool = new SocketConnectionPool(
maxConnectionsPerAddress: 5,
connectionTimeoutSeconds: 300
);
// Verbindung hinzufügen
$pool->add($connection);
// Verbindung entfernen
$pool->remove($connection);
// Verbindung abrufen
$connection = $pool->get($connectionId);
$connections = $pool->getConnectionsByAddress($address);
$connection = $pool->getConnectionForAddress($address);
// Dead Connections bereinigen
$removed = $pool->cleanupDeadConnections();
// Statistiken
$count = $pool->getConnectionCount();
$allConnections = $pool->getAllConnections();
```
### SocketService
Haupt-Facade für alle Socket-Operationen.
```php
use App\Framework\Sockets\SocketService;
$socketService = $container->get(SocketService::class);
// Socket-Erstellung
$socket = $socketService->createTcp($address);
$socket = $socketService->createUdp($address);
$socket = $socketService->createUnix($address);
// Server-Operationen
$socketService->bind($socket, $address);
$socketService->listen($socket, 10);
$connection = $socketService->accept($socket);
$socketService->setNonBlocking($socket);
// Client-Operationen
$socketService->connect($socket, $address);
$data = $socketService->read($socket, 1024);
$bytes = $socketService->write($socket, $data);
// Connection Pool
$socketService->addConnection($connection);
$socketService->removeConnection($connection);
$connection = $socketService->getConnection($connectionId);
$removed = $socketService->cleanupDeadConnections();
```
## Integration
### PCNTL Integration
Socket-Server in geforkten Worker-Prozessen für Load Balancing.
```php
use App\Framework\Sockets\Integration\PcntlSocketServer;
use App\Framework\Sockets\SocketService;
use App\Framework\Pcntl\PcntlService;
use App\Framework\Sockets\ValueObjects\SocketAddress;
use App\Framework\Sockets\ValueObjects\SocketResource;
$socketService = $container->get(SocketService::class);
$pcntlService = $container->get(PcntlService::class);
$pcntlServer = new PcntlSocketServer(
socketService: $socketService,
pcntlService: $pcntlService,
maxWorkers: 4
);
// Connection Handler setzen
$pcntlServer->setConnectionHandler(function (SocketConnection $connection) {
// Verbindung verarbeiten
$socket = $connection->getSocket();
$data = $socketService->read($socket->getResource(), 1024);
// ...
});
// Server starten
$address = SocketAddress::ipv4('0.0.0.0', 8080);
$socket = $socketService->createTcp($address);
$pcntlServer->start($socket, $address, workerCount: 4);
// Server stoppen (graceful shutdown)
$pcntlServer->stop();
```
### Async Integration
Non-blocking Socket-Server mit Fibers für parallele Connection-Verarbeitung.
```php
use App\Framework\Sockets\Integration\AsyncSocketServer;
use App\Framework\Sockets\SocketService;
use App\Framework\Async\FiberManager;
use App\Framework\Sockets\ValueObjects\SocketAddress;
$socketService = $container->get(SocketService::class);
$fiberManager = $container->get(FiberManager::class);
$asyncServer = new AsyncSocketServer(
socketService: $socketService,
fiberManager: $fiberManager
);
// Server starten (gibt Fiber zurück)
$address = SocketAddress::ipv4('0.0.0.0', 8080);
$socket = $socketService->createTcp($address);
$serverFiber = $asyncServer->startAsync(
socket: $socket,
address: $address,
connectionHandler: function (SocketConnection $connection) {
// Connection in separatem Fiber verarbeiten
$socket = $connection->getSocket();
// Non-blocking lesen
$readFiber = $asyncServer->readAsync($connection, 1024);
$data = $readFiber->start();
// Non-blocking schreiben
$writeFiber = $asyncServer->writeAsync($connection, "Response");
$bytes = $writeFiber->start();
}
);
// Server-Fiber starten
$serverFiber->start();
```
## Beispiele
### Einfacher TCP Server
```php
use App\Framework\Sockets\SocketService;
use App\Framework\Sockets\ValueObjects\SocketAddress;
$socketService = $container->get(SocketService::class);
$address = SocketAddress::ipv4('0.0.0.0', 8080);
// Socket erstellen
$socket = $socketService->createTcp($address);
// Binden und lauschen
$socketService->bind($socket, $address);
$socketService->listen($socket, 10);
$socketService->setNonBlocking($socket);
// Hauptschleife
while (true) {
// Verbindung akzeptieren
$connection = $socketService->accept($socket);
if ($connection !== null) {
$clientSocket = $connection->getSocket();
// Daten lesen
$data = $socketService->read($clientSocket, 1024);
if ($data !== null) {
// Antwort senden
$socketService->write($clientSocket, "Echo: " . $data);
}
// Verbindung schließen
$connection->close();
}
usleep(10000); // 10ms
}
```
### TCP Client
```php
use App\Framework\Sockets\SocketService;
use App\Framework\Sockets\ValueObjects\SocketAddress;
$socketService = $container->get(SocketService::class);
$address = SocketAddress::ipv4('127.0.0.1', 8080);
// Client-Socket erstellen
$socket = $socketService->createClient(
SocketType::TCP,
SocketProtocol::IPv4
);
// Verbinden
$socketService->connect($socket, $address);
// Daten senden
$socketService->write($socket, "Hello, Server!");
// Antwort lesen
$response = $socketService->read($socket, 1024);
// Verbindung schließen
$socketService->close($socket);
```
### Unix Domain Socket Server
```php
use App\Framework\Sockets\SocketService;
use App\Framework\Sockets\ValueObjects\SocketAddress;
$socketService = $container->get(SocketService::class);
$address = SocketAddress::unix('/tmp/mysocket.sock');
// Socket erstellen
$socket = $socketService->createUnix($address);
// Binden und lauschen
$socketService->bind($socket, $address);
$socketService->listen($socket, 10);
// Verbindungen akzeptieren
while (true) {
$connection = $socketService->accept($socket);
if ($connection !== null) {
// Verarbeitung
$socket = $connection->getSocket();
$data = $socketService->read($socket, 1024);
// ...
}
}
```
## Migration von bestehendem Code
### Von `socket_create()` zu `SocketFactory`
**Vorher:**
```php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
```
**Nachher:**
```php
$address = SocketAddress::ipv4('127.0.0.1', 8080);
$socket = $socketService->createTcp($address);
```
### Von `socket_bind()`/`socket_listen()` zu `SocketServer`
**Vorher:**
```php
socket_bind($socket, '127.0.0.1', 8080);
socket_listen($socket, 10);
```
**Nachher:**
```php
$address = SocketAddress::ipv4('127.0.0.1', 8080);
$socketService->bind($socket, $address);
$socketService->listen($socket, 10);
```
### Von `socket_accept()` zu `SocketServer::accept()`
**Vorher:**
```php
$clientSocket = socket_accept($socket);
```
**Nachher:**
```php
$connection = $socketService->accept($socket);
if ($connection !== null) {
$clientSocket = $connection->getSocket();
}
```
### Von `socket_select()` zu `SocketServer::select()`
**Vorher:**
```php
socket_select($read, $write, $except, 1);
```
**Nachher:**
```php
$numReady = $socketService->select($read, $write, $except, 1);
```
### Von `socket_read()`/`socket_write()` zu `SocketClient`
**Vorher:**
```php
$data = socket_read($socket, 1024);
socket_write($socket, $data, strlen($data));
```
**Nachher:**
```php
$data = $socketService->read($socket, 1024);
$bytes = $socketService->write($socket, $data);
```
### Von `socket_close()` zu `SocketResource` Destructor
**Vorher:**
```php
socket_close($socket);
```
**Nachher:**
```php
// Automatisch im Destructor, oder manuell:
$socket->close();
```
## Fehlerbehandlung
Das Sockets-Modul verwendet spezifische Exceptions für verschiedene Fehlertypen:
```php
use App\Framework\Sockets\Exceptions\SocketException;
use App\Framework\Sockets\Exceptions\SocketBindException;
use App\Framework\Sockets\Exceptions\SocketConnectException;
use App\Framework\Sockets\Exceptions\SocketReadException;
try {
$socketService->bind($socket, $address);
} catch (SocketBindException $e) {
// Bind-Fehler behandeln
error_log("Failed to bind: " . $e->getMessage());
}
try {
$socketService->connect($socket, $address);
} catch (SocketConnectException $e) {
// Connect-Fehler behandeln
error_log("Failed to connect: " . $e->getMessage());
}
try {
$data = $socketService->read($socket, 1024);
} catch (SocketReadException $e) {
// Read-Fehler behandeln
if ($e->getMessage() === 'Socket connection closed') {
// Verbindung geschlossen
}
}
```
## Best Practices
1. **SocketService verwenden**: Nutze immer `SocketService` als Haupt-API statt direkter Aufrufe der einzelnen Services
2. **Value Objects**: Verwende immer Value Objects (`SocketAddress`, `SocketType`, etc.) statt primitiver Typen
3. **Resource Management**: `SocketResource` kümmert sich automatisch um Cleanup, aber manuelles Schließen ist für explizite Kontrolle möglich
4. **Non-blocking I/O**: Verwende `setNonBlocking()` für Server-Sockets, um mehrere Verbindungen parallel zu handhaben
5. **Connection Pooling**: Nutze `SocketConnectionPool` für Client-Verbindungen, die wiederverwendet werden sollen
6. **Error Handling**: Fange spezifische Exceptions (`SocketBindException`, `SocketConnectException`, etc.) für präzise Fehlerbehandlung
7. **PCNTL für Load Balancing**: Verwende `PcntlSocketServer` für Socket-Server, die mehrere Worker-Prozesse benötigen
8. **Async für Parallelität**: Verwende `AsyncSocketServer` für non-blocking I/O mit Fibers
## Dependency Injection
Alle Services werden automatisch über `SocketsInitializer` im DI-Container registriert:
```php
use App\Framework\Sockets\SocketService;
use App\Framework\Sockets\SocketFactory;
use App\Framework\Sockets\SocketServer;
use App\Framework\Sockets\SocketClient;
// Services sind im Container verfügbar
$socketService = $container->get(SocketService::class);
$factory = $container->get(SocketFactory::class);
$server = $container->get(SocketServer::class);
$client = $container->get(SocketClient::class);
```
## Framework-Kompatibilität
Das Sockets-Modul folgt allen Framework-Prinzipien:
-**Final readonly classes** wo möglich
-**Value Objects** statt Primitiven
-**Dependency Injection** überall
-**Composition** statt Inheritance
-**Strict Types** (`declare(strict_types=1)`)
-**PSR-12** Code Style
## Siehe auch
- [PCNTL Module Documentation](./pcntl-module.md) - Für PCNTL-Integration
- [Async Module Documentation](./async-module.md) - Für Async-Integration
- [Framework Guidelines](./guidelines.md) - Allgemeine Framework-Prinzipien