# 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