Start · Sprachen · PHP · Referenz · socket_select

socket_select

Funktion

Überwacht mehrere Sockets gleichzeitig auf Lese-, Schreib- und Fehlerbereitschaft mittels des <code>select()</code>-Systemaufrufs und optional einem Zeitlimit.

seit PHP 4.1.0 Kategorie: http

Signatur

socket_select(array &$read, array &$write, array &$except, ?int $seconds, int $microseconds = 0): int|false

Beschreibung

socket_select() ermöglicht es, mehrere Socket-Ressourcen gleichzeitig auf ihre Bereitschaft zu überwachen, ohne jeden Socket einzeln blockierend abfragen zu müssen. Die Funktion wartet, bis mindestens einer der übergebenen Sockets bereit zum Lesen, Schreiben oder ein Ausnahmezustand eingetreten ist – oder bis das angegebene Zeitlimit abgelaufen ist.

Die drei Arrays $read, $write und $except werden by-reference übergeben und von der Funktion modifiziert: Nach dem Aufruf enthalten sie nur noch die Sockets, die tatsächlich bereit sind. Sockets in $read haben eingehende Daten oder eine neue Verbindungsanfrage, Sockets in $write können Daten senden, ohne zu blockieren, und Sockets in $except haben Out-of-Band-Daten oder Fehler.

Durch das Setzen von $seconds auf 0 (und $microseconds auf 0) verhält sich die Funktion als nicht-blockierender Poll, während null als $seconds eine unbegrenzte Wartezeit bedeutet. Diese Funktion ist ideal für die Implementierung von nicht-blockierenden, ereignisgesteuerten Server-Anwendungen, die viele parallele Verbindungen verwalten müssen.

Ein typischer Einsatz ist ein einfacher TCP-Server, der ohne Threads oder Prozesse mehrere Clients gleichzeitig bedienen kann, indem er in einer Schleife wiederholt socket_select() aufruft und die bereiten Sockets verarbeitet.

Parameter

Name Typ Default Beschreibung
$read Pflicht array Array von Socket-Ressourcen, die auf eingehende Daten oder neue Verbindungen überwacht werden sollen. Nach dem Aufruf enthält es nur die lesbereiten Sockets. Soll nicht überwacht werden, kann [] (leeres Array) übergeben werden.
$write Pflicht array Array von Socket-Ressourcen, die auf Schreibbereitschaft überwacht werden sollen. Nach dem Aufruf enthält es nur die schreibbereiten Sockets. Soll nicht überwacht werden, kann [] übergeben werden.
$except Pflicht array Array von Socket-Ressourcen, die auf Ausnahmezustände (z. B. Out-of-Band-Daten) überwacht werden sollen. Nach dem Aufruf enthält es nur die betroffenen Sockets. Soll nicht überwacht werden, kann [] übergeben werden.
$seconds Pflicht ?int Sekundenanteil des Zeitlimits. Bei 0 kehrt die Funktion sofort zurück (nicht-blockierender Poll). Bei null wartet die Funktion unbegrenzt, bis ein Socket bereit ist.
$microseconds int 0 Mikrosekundenanteil des Zeitlimits, der zum Sekundenanteil addiert wird. Ermöglicht eine feinere Zeitsteuerung des Timeouts.

Rückgabewert

Typ
int|false
Beschreibung
Gibt die Anzahl der bereiten Socket-Ressourcen über alle drei Arrays zurück. Ist das Zeitlimit abgelaufen ohne dass ein Socket bereit wurde, wird 0 zurückgegeben. Bei einem Fehler wird false zurückgegeben; der Fehlercode ist dann mit socket_last_error() abrufbar.

Beispiele

Einfacher nicht-blockierender Socket-Server mit socket_select

<?php
// Erstelle einen TCP-Server-Socket
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, '0.0.0.0', 8080);
socket_listen($server, 5);
echo "Server lauscht auf Port 8080...\n";

$clients = [];

while (true) {
    // Alle zu überwachenden Sockets zusammenstellen
    $read = array_merge([$server], $clients);
    $write  = [];
    $except = [];

    // Bis zu 5 Sekunden auf Aktivität warten
    $ready = socket_select($read, $write, $except, 5);

    if ($ready === false) {
        echo "socket_select Fehler: " . socket_strerror(socket_last_error()) . "\n";
        break;
    }

    if ($ready === 0) {
        echo "Timeout – keine Aktivität.\n";
        continue;
    }

    // Neue Verbindung am Server-Socket?
    if (in_array($server, $read)) {
        $client = socket_accept($server);
        $clients[] = $client;
        socket_write($client, "Willkommen!\n");
        echo "Neuer Client verbunden.\n";
    }

    // Daten von vorhandenen Clients lesen
    foreach ($clients as $key => $client) {
        if (in_array($client, $read)) {
            $data = socket_read($client, 1024, PHP_NORMAL_READ);
            if ($data === false || trim($data) === '') {
                // Client hat Verbindung getrennt
                socket_close($client);
                unset($clients[$key]);
                echo "Client getrennt.\n";
            } else {
                echo "Empfangen: " . trim($data) . "\n";
                // Echo zurücksenden
                socket_write($client, "Echo: " . $data);
            }
        }
    }
}

socket_close($server);
Server lauscht auf Port 8080... Neuer Client verbunden. Empfangen: Hallo Server Timeout – keine Aktivität.

Nicht-blockierender Poll mit Timeout 0 (sofortige Rückkehr)

<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'example.com', 80);
socket_set_nonblock($socket);

$request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
socket_write($socket, $request);

$response = '';
$maxWait = 50; // maximale Iterationen
$i = 0;

while ($i++ < $maxWait) {
    $read   = [$socket];
    $write  = [];
    $except = [];

    // Sofort zurückkehren (Poll), kein Blockieren
    $ready = socket_select($read, $write, $except, 0, 100000); // 100 ms

    if ($ready === false) {
        echo "Fehler: " . socket_strerror(socket_last_error($socket)) . "\n";
        break;
    }

    if ($ready > 0 && in_array($socket, $read)) {
        $chunk = socket_read($socket, 4096);
        if ($chunk === '' || $chunk === false) {
            break; // Verbindung geschlossen
        }
        $response .= $chunk;
    }
}

socket_close($socket);
echo substr($response, 0, 200);
HTTP/1.0 200 OK Content-Type: text/html; ...

// Wichtig · Fallstricke

Wichtig bei der Verwendung von null vs. 0: Um unbegrenzt zu warten, muss $seconds = null übergeben werden. Der Wert 0 bedeutet dagegen sofortige Rückkehr ohne Warten.

Arrays werden modifiziert: Da alle drei Arrays by-reference übergeben werden, müssen die Original-Socket-Listen vor dem Aufruf in temporäre Variablen kopiert werden, wenn sie nach dem Aufruf noch vollständig benötigt werden.

Maximale Anzahl Sockets: Auf manchen Systemen (insbesondere Windows) ist die Anzahl der gleichzeitig überwachbaren Sockets durch FD_SETSIZE (typischerweise 64 oder 1024) begrenzt. Für sehr viele gleichzeitige Verbindungen sollten Alternativen wie libevent oder ReactPHP in Betracht gezogen werden.

Signal-Interrupts: Auf Unix-Systemen kann ein Signal (z. B. SIGCHLD) die Funktion vorzeitig beenden und false mit dem Fehlercode EINTR zurückgeben. Dies sollte in der Fehlerbehandlung berücksichtigt werden.