isConnected) { return false; } try { $frame = $this->createFrame($message, 0x1); // Text frame return fwrite($this->socket, $frame) !== false; } catch (\Throwable $e) { $this->disconnect(); return false; } } /** * Sendet JSON-Daten */ public function sendJson(array $data): bool { return $this->send(json_encode($data)); } /** * Sendet Binärdaten */ public function sendBinary(string $data): bool { if (! $this->isConnected) { return false; } try { $frame = $this->createFrame($data, 0x2); // Binary frame return fwrite($this->socket, $frame) !== false; } catch (\Throwable $e) { $this->disconnect(); return false; } } /** * Sendet einen Ping */ public function ping(string $data = ''): bool { if (! $this->isConnected) { return false; } try { $frame = $this->createFrame($data, 0x9); // Ping frame return fwrite($this->socket, $frame) !== false; } catch (\Throwable $e) { $this->disconnect(); return false; } } /** * Schließt die Verbindung */ public function close(int $code = 1000, string $reason = ''): void { if (! $this->isConnected) { return; } try { $payload = pack('n', $code) . $reason; $frame = $this->createFrame($payload, 0x8); // Close frame fwrite($this->socket, $frame); } catch (\Throwable $e) { // Fehler beim Senden des Close-Frames ignorieren } $this->disconnect(); } /** * Trennt die Verbindung */ public function disconnect(): void { $this->isConnected = false; if (is_resource($this->socket)) { fclose($this->socket); } } /** * Erstellt einen WebSocket-Frame */ private function createFrame(string $payload, int $opcode): string { $payloadLength = strlen($payload); $frame = ''; // Erste Byte: FIN (1) + RSV (000) + Opcode (4 bits) $frame .= chr(0x80 | $opcode); // Payload-Länge if ($payloadLength < 126) { $frame .= chr($payloadLength); } elseif ($payloadLength < 65536) { $frame .= chr(126) . pack('n', $payloadLength); } else { $frame .= chr(127) . pack('J', $payloadLength); } return $frame . $payload; } /** * Setzt ein Attribut für diese Verbindung */ public function setAttribute(string $key, mixed $value): void { $this->attributes[$key] = $value; } /** * Gibt ein Attribut zurück */ public function getAttribute(string $key, mixed $default = null): mixed { return $this->attributes[$key] ?? $default; } /** * Prüft, ob die Verbindung aktiv ist */ public function isConnected(): bool { return $this->isConnected; } /** * Gibt die Verbindungs-ID zurück */ public function getId(): string { return $this->id; } }