Start · Sprachen · PHP · Referenz · socket_accept

socket_accept

Funktion

Akzeptiert eine eingehende Verbindung auf einem lauschenden <code>Socket</code> und gibt einen neuen Socket für die Kommunikation zurück.

seit PHP 4.1.0 Kategorie: http

Signatur

socket_accept(Socket $socket): Socket|false

Beschreibung

socket_accept() wartet auf eine eingehende Verbindung an einem zuvor mit socket_bind() und socket_listen() vorbereiteten Socket. Sobald ein Client eine Verbindung aufbaut, gibt die Funktion einen neuen Socket-Ressource zurück, der ausschließlich für die Kommunikation mit diesem Client verwendet wird. Der ursprüngliche Server-Socket bleibt unverändert und kann weiterhin auf weitere Verbindungen warten.

Die Funktion arbeitet standardmäßig blockierend: Sie hält die Ausführung des Skripts an, bis eine Verbindung eingeht. Mit socket_set_nonblock() oder socket_set_block() lässt sich das Verhalten steuern. Im nicht-blockierenden Modus gibt die Funktion sofort false zurück, wenn keine Verbindung ansteht.

Das typische Anwendungsmuster für einen TCP-Server lautet: Socket erstellen → binden → lauschen → in einer Schleife socket_accept() aufrufen → mit dem zurückgegebenen Client-Socket Daten lesen/schreiben → Client-Socket schließen. Der zurückgegebene Socket muss nach Gebrauch explizit mit socket_close() geschlossen werden, um Ressourcen freizugeben.

Für produktive Server-Anwendungen mit vielen gleichzeitigen Verbindungen empfiehlt sich der Einsatz von socket_select(), um mehrere Sockets gleichzeitig zu überwachen, oder der Wechsel zu asynchronen Frameworks wie ReactPHP oder Swoole.

Parameter

Name Typ Default Beschreibung
$socket Pflicht Socket Ein gültiger, lauschender Socket-Ressource, der zuvor mit socket_create(), socket_bind() und socket_listen() vorbereitet wurde.

Rückgabewert

Typ
Socket|false
Beschreibung
Bei Erfolg wird ein neues Socket-Objekt zurückgegeben, das die Verbindung zum verbundenen Client repräsentiert. Im Fehlerfall (z. B. ungültiger Socket oder nicht-blockierender Modus ohne ausstehende Verbindung) wird false zurückgegeben. Fehlercodes können mit socket_last_error() abgerufen werden.

Beispiele

Einfacher TCP-Echo-Server

<?php
// Socket erstellen (IPv4, TCP)
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($server === false) {
    die('socket_create() fehlgeschlagen: ' . socket_strerror(socket_last_error()));
}

// Socket an Adresse und Port binden
socket_bind($server, '127.0.0.1', 8080);

// Auf eingehende Verbindungen lauschen (max. 5 in der Warteschlange)
socket_listen($server, 5);

echo "Server lauscht auf 127.0.0.1:8080 ...\n";

while (true) {
    // Blockierend warten, bis ein Client verbindet
    $client = socket_accept($server);
    if ($client === false) {
        echo 'socket_accept() fehlgeschlagen: ' . socket_strerror(socket_last_error($server)) . "\n";
        continue;
    }

    // Begrüßungsnachricht senden
    $message = "Willkommen beim Echo-Server!\n";
    socket_write($client, $message, strlen($message));

    // Daten vom Client lesen
    $input = socket_read($client, 1024, PHP_NORMAL_READ);
    $input = trim($input);
    echo "Empfangen: $input\n";

    // Eingabe zurückspiegeln (Echo)
    $response = "Echo: $input\n";
    socket_write($client, $response, strlen($response));

    // Client-Socket schließen
    socket_close($client);

    // Nach einer Anfrage beenden (Demo-Zweck)
    break;
}

socket_close($server);
echo "Server beendet.\n";
Server lauscht auf 127.0.0.1:8080 ... Empfangen: Hallo Welt Server beendet.

Nicht-blockierender Accept mit socket_select()

<?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, '127.0.0.1', 9090);
socket_listen($server, 10);

// Nicht-blockierenden Modus aktivieren
socket_set_nonblock($server);

$clients = [];
$running = true;

echo "Nicht-blockierender Server auf Port 9090 gestartet.\n";

// Simulierte Schleife (in der Praxis z. B. zeitlich begrenzt)
$iterations = 0;
while ($running && $iterations < 100) {
    $iterations++;

    // socket_select() prüft, ob Aktivität auf dem Server-Socket vorliegt
    $read = array_merge([$server], $clients);
    $write = $except = [];
    $changed = socket_select($read, $write, $except, 0, 200000); // 200 ms Timeout

    if ($changed === false) {
        break;
    }

    // Neue Verbindung prüfen
    if (in_array($server, $read)) {
        $client = socket_accept($server);
        if ($client !== false) {
            $clients[] = $client;
            socket_write($client, "Verbunden!\n");
            echo "Neuer Client verbunden.\n";
        }
    }

    // Daten von bestehenden Clients lesen
    foreach ($clients as $key => $sock) {
        if (in_array($sock, $read)) {
            $data = @socket_read($sock, 1024, PHP_NORMAL_READ);
            if ($data === false || $data === '') {
                socket_close($sock);
                unset($clients[$key]);
                echo "Client getrennt.\n";
            } else {
                echo "Daten: " . trim($data) . "\n";
            }
        }
    }
}

foreach ($clients as $sock) {
    socket_close($sock);
}
socket_close($server);
echo "Server gestoppt.\n";
Nicht-blockierender Server auf Port 9090 gestartet. Server gestoppt.

// Wichtig · Fallstricke

Sicherheitshinweis: Validiere stets die Herkunft eingehender Verbindungen mit socket_getpeername(), wenn nur bestimmte IP-Adressen zugelassen werden sollen. Ohne Filterung kann jeder Client eine Verbindung aufbauen.

Ressourcen-Verwaltung: Jeder von socket_accept() zurückgegebene Client-Socket muss nach Gebrauch mit socket_close() geschlossen werden, sonst entstehen Ressourcen-Lecks (Dateideskriptoren).

Hinweis zu PHP-Versionen: Seit PHP 8.0 wird statt einer Ressource ein Socket-Objekt zurückgegeben. Code, der is_resource() zur Prüfung verwendet, muss für PHP 8+ angepasst werden.

Blockierendes Verhalten: Im blockierenden Modus kann das Skript unendlich lange anhalten, wenn keine Verbindung eingeht. Für robuste Server sollte socket_select() mit Timeout verwendet werden.