Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Artfiles.de
Bietet Serviceorientierte...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains...
https://www.Hosterplus.de
 
 
 

PHP stream_socket_client(): TCP- und TLS-Verbindungen

Sie befinden sich: Home > Php Tutorial > PHP stream_socket_client():...

PHP stream_socket_client(): TCP- und TLS-Verbindungen
Eintrag am:
10.08.2026
Hits / Besucher:
6
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Ein Port soll geprüft werden, ein hauseigener Dienst spricht sein eigenes zeilenbasiertes Protokoll, oder ein Partnerserver verlangt eine eigene Zertifikatsdatei. Dann hilft kein fertiger HTTP-Client weiter, sondern PHP stream_socket_client. Die Funktion baut eine Leitung zu einer Adresse auf und liefert dafür etwas Vertrautes: einen ganz gewöhnlichen Stream.

Illustration zum Tutorial: PHP stream_socket_client(): TCP- und TLS-Verbindungen

Der Weg dorthin führt über vier Stationen: die Adresse, die Fehlerprüfung, die Verschlüsselung und die Zeitlimits.

Was PHP stream_socket_client macht

Die Funktion nimmt eine Zieladresse entgegen, baut die Verbindung auf und gibt bei Erfolg eine Stream-Ressource heraus. Scheitert der Aufbau, kommt false an. Mehr passiert nicht: es entsteht eine offene Leitung, und was darauf gesprochen wird, bestimmt dein Code.

Ein Stream ist dieselbe Abstraktion, die auch hinter einer geöffneten Datei steckt. Auf dem Rückgabewert gelten deshalb die vertrauten Funktionen: fwrite() schreibt, fgets() und fread() lesen, feof() meldet das Ende, fclose() schließt die Leitung wieder. Eine Socket-Bibliothek braucht es nicht, auch ext/sockets bleibt außen vor: PHP stream_socket_client steckt im Kern.

Dazu gleich die ehrliche Einordnung. Wer eine Webseite oder eine JSON-Schnittstelle abrufen will, ist hier falsch. Dafür gibt es PHP file_get_contents() und cURL, die sich um Weiterleitungen, Statuscodes und Header kümmern. PHP stream_socket_client lohnt sich bei eigenen Protokollen, bei selbst bestimmter Zertifikatsprüfung und dann, wenn die Verbindung dem eigenen Ablauf folgt.

Die Adresse: Transport, Host und Port in einem String

Der auffälligste Unterschied zu älteren Wegen steckt im ersten Parameter. PHP stream_socket_client nimmt Host und Port nicht getrennt entgegen, sondern zusammen mit dem Transport in einem String in URL-Schreibweise. Welche Transporte deine Installation mitbringt, verrät stream_get_transports().

Transport Wofür Beispieladresse
tcp:// Klartext, der Normalfall bei eigenen Diensten tcp://example.com:80
tls:// Verschlüsselt, mit Zertifikatsprüfung tls://example.com:443
udp:// Einzelne Pakete, etwa an einen Syslog-Empfänger udp://10.0.0.5:514
unix:// Lokaler Dienst, ganz ohne Netzwerkstack unix:///var/run/dienst.sock

Bei unix:// fällt der Port ersatzlos weg, denn die Adresse ist ein Dateipfad. Die drei Schrägstriche sind kein Tippfehler: zwei gehören zum Schema, der dritte zum absoluten Pfad. Und ssl:// ist nicht dasselbe wie tls://: hinter ssl:// steckt die veraltete Aushandlung.

Der erste Verbindungsaufbau und die Fehlerprüfung

Der einfachste Fall ist die Frage, ob ein Dienst antwortet. PHP stream_socket_client beantwortet sie in wenigen Zeilen. Der zweite und dritte Parameter werden per Referenz gefüllt und sind im Fehlerfall die einzige Auskunftsstelle.

<?php

$verbindung = stream_socket_client(
'tcp://www.php-space.info:80',
$errno,
$errstr,
5
);

if ($verbindung === false) {
/* $errno traegt den Systemfehlercode, $errstr den Klartext dazu */
echo 'Nicht erreichbar: ' . $errstr . ' (Code ' . $errno . ')';
exit;
}

echo 'Verbindung steht';
fclose($verbindung);

Wer PHP stream_socket_client ohne diese Prüfung aufruft, steht im Störungsfall ohne jede Auskunft da. Zwei Punkte machen deutlich, warum daran nichts zu sparen ist.

Ohne die beiden Fehlerparameter bleibt jede Störung ein Rätsel

Eine abgelehnte Verbindung, ein nicht auflösbarer Name und ein abgelaufenes Zeitlimit sehen im Code gleich aus, denn alle drei liefern false. Erst Fehlercode und Meldung trennen die Fälle und sagen dir, ob Firewall, DNS oder der Dienst das Problem ist.

Ein vorangestelltes @ löst gar nichts

Der Klammeraffe vor dem Aufruf unterdrückt die Warnung, nicht den Fehler. Die Verbindung steht danach genauso wenig, nur ohne Spur im Protokoll.

Verschlüsselt verbinden über tls://

Der Wechsel auf eine verschlüsselte Verbindung besteht aus einem Wort in der Adresse. Alles Weitere bleibt gleich, der Rückgabewert ist wieder ein Stream. Das folgende Beispiel baut eine HTTP-Anfrage von Hand zusammen.

<?php

$host = 'www.php-space.info';

$verbindung = stream_socket_client("tls://$host:443", $errno, $errstr, 10);

if ($verbindung === false) {
exit("Kein Zugriff: $errstr ($errno)");
}

/* Eine HTTP-Anfrage ist reiner Text, jede Zeile endet mit CR und LF */
$anfrage = "GET / HTTP/1.1\r\n"
. "Host: $host\r\n"
. "Connection: close\r\n"
. "\r\n";

fwrite($verbindung, $anfrage);

while (!feof($verbindung)) {
echo fgets($verbindung, 1024);
}

fclose($verbindung);

Zwei Dinge sind daran wichtig. HTTP-Zeilen enden mit Wagenrücklauf und Zeilenvorschub, nicht mit einem einfachen Umbruch; manche Server antworten auf die falsche Variante gar nicht. Und in der Ausgabe landet der komplette Kopf samt Statuszeile. Weiterleitungen, gestückelte Übertragung und Komprimierung kämen obendrauf. Im Alltag übernehmen das PHP curl_init() und PHP curl_exec(). Das Beispiel steht hier, weil es den Stream begreifbar macht, nicht als Empfehlung.

Kontext-Optionen und die Zertifikatsprüfung

Bei der Verschlüsselung wird PHP stream_socket_client erst durch den sechsten Parameter interessant. Über stream_context_create() entsteht ein Kontext mit den TLS-Einstellungen, dazu bei Bedarf die Quelladresse per bindto. Aus einer nur verschlüsselten Verbindung wird damit eine mit einer Prüfung, die du selbst festlegst.

<?php

$kontext = stream_context_create([
'ssl' => [
/* Prueft, ob das Zertifikat auf eine bekannte CA zurueckgeht */
'verify_peer' => true,
/* Prueft zusaetzlich, ob der Name im Zertifikat zum Host passt */
'verify_peer_name' => true,
/*
Datei mit den vertrauenswuerdigen Wurzelzertifikaten.
Fuer einen internen Partnerserver steht hier dessen
eigene CA, nicht das System-Bundle.
*/
'cafile' => '/etc/ssl/certs/partner-ca.pem',
'allow_self_signed' => false,
],
]);

$verbindung = stream_socket_client(
'tls://partner.example.com:8443',
$errno,
$errstr,
10,
STREAM_CLIENT_CONNECT,
$kontext
);

if ($verbindung === false) {
exit("TLS-Verbindung fehlgeschlagen: $errstr ($errno)");
}

Die drei entscheidenden Optionen prüfen jeweils etwas anderes.

verify_peer

Fragt, ob sich das Serverzertifikat auf eine Zertifizierungsstelle zurückführen lässt, der dein System vertraut. Ohne diese Prüfung ist die Leitung zwar verschlüsselt, aber niemand weiß mehr, wer am anderen Ende sitzt.

verify_peer_name

Fragt zusätzlich, ob der Name im Zertifikat zum angesprochenen Host passt. Ein gültiges Zertifikat für einen ganz anderen Server fällt erst hier auf.

cafile

Benennt die Datei mit den Wurzelzertifikaten, gegen die geprüft wird. Bei einem internen Partnerserver steht dort dessen eigene Zertifizierungsstelle statt des System-Bundles. Das ist die richtige Antwort auf einen Zertifikatsfehler.

Seit PHP 5.6 stehen die ersten beiden Optionen bei PHP stream_socket_client von sich aus auf true. Der Rat, sie bei einem Fehler auf false zu setzen, ist kein Fix, sondern die Abschaltung genau der Prüfung, um die es geht. Die Meldung verschwindet, das Risiko bleibt.

Die zwei Timeouts, die ständig verwechselt werden

Hier scheitert die Praxis am häufigsten. Der vierte Parameter von PHP stream_socket_client begrenzt ausschließlich den Verbindungsaufbau; steht die Leitung, ist er erledigt. Nimmt der Server sie an und schweigt danach, wartet das Skript weiter, bis das Zeitlimit für Lesen und Schreiben greift. Genau das setzt stream_set_timeout().

<?php

/* Zeitlimit 1: gilt ausschliesslich fuer den Verbindungsaufbau */
$strom = stream_socket_client('tcp://10.0.0.7:9000', $errno, $errstr, 3);

if ($strom === false) {
exit("Aufbau fehlgeschlagen: $errstr ($errno)");
}

/* Zeitlimit 2: gilt fuer jedes Lesen und Schreiben danach */
stream_set_timeout($strom, 5);

fwrite($strom, "STATUS\r\n");
$antwort = fgets($strom);

$info = stream_get_meta_data($strom);

if ($info['timed_out']) {
echo 'Der Dienst hat nicht rechtzeitig geantwortet';
} else {
echo trim((string) $antwort);
}

fclose($strom);

Der dritte Baustein ist die Auswertung: stream_get_meta_data() meldet unter timed_out, ob ein Zeitlimit zugeschlagen hat. Ohne diese Abfrage sieht ein Zeitablauf aus wie eine leere Antwort.

Bleibt der vierte Parameter weg, greift default_socket_timeout aus der php.ini mit 60 Sekunden. Für ein Wartungsskript mag das passen, für eine Webseite ist eine Minute Wartezeit auf einen Dienst nicht zumutbar.

Der folgende Ablauf fasst die Entscheidungen zusammen, die vor jedem Verbindungsaufbau anstehen.

flowchart TD
    A[Verbindung noetig] --> B{Nur HTTP holen?}
    B -->|Ja| C[curl oder file_get_contents]
    B -->|Nein| D{Welcher Transport?}
    D -->|Klartext| E[tcp://host:port]
    D -->|Verschluesselt| F[tls://host:port]
    D -->|Lokaler Dienst| G[unix:///pfad.sock]
    F --> H[Kontext mit verify_peer]
    E --> I{Timeout wofuer?}
    H --> I
    G --> I
    I -->|Aufbau| J[4. Parameter timeout]
    I -->|Lesen| K[stream_set_timeout]
    J --> L[errno und errstr pruefen]
    K --> L

Ein eigenes Protokoll sprechen

Viele hauseigene Dienste sprechen zeilenbasiert: ein Befehl geht raus, eine Statuszeile kommt herein. Kein HTTP-Werkzeug hilft dabei, und genau dafür ist PHP stream_socket_client gemacht. Das Muster hat drei Teile und passt auf fast jedes solche Protokoll.

<?php

function befehlSenden($strom, string $befehl): string
{
fwrite($strom, $befehl . "\r\n");

$antwort = fgets($strom);

if ($antwort === false) {
throw new RuntimeException('Keine Antwort auf ' . $befehl);
}

return trim($antwort);
}

$strom = stream_socket_client('tcp://lager.intern:7500', $errno, $errstr, 5);

if ($strom === false) {
exit("Lager nicht erreichbar: $errstr ($errno)");
}

stream_set_timeout($strom, 5);

echo befehlSenden($strom, 'LOGIN kasse01'), "\n";
echo befehlSenden($strom, 'BESTAND 4711'), "\n";
echo befehlSenden($strom, 'QUIT'), "\n";

fclose($strom);

Das Zeilenende ist auch hier die kritische Stelle. Erwartet die Gegenseite Wagenrücklauf und Zeilenvorschub, genügt ein einfacher Umbruch nicht, und der Dienst schweigt. Umgekehrt liest fgets() ohne Längenangabe bis zum Zeilenende. Kommt keines, blockiert der Aufruf, bis das Zeitlimit greift. Deshalb steht es im Beispiel mit dabei.

Viele Ziele gleichzeitig prüfen

Wer vierzig Dienste im Minutentakt abfragt, darf nicht nacheinander auf jeden Aufbau warten. Für diesen Fall kennt PHP stream_socket_client im fünften Parameter das Flag STREAM_CLIENT_ASYNC_CONNECT. Der Aufruf kehrt sofort zurück, und alle Ziele laufen nebeneinander an. Der Normalwert dieses Parameters ist übrigens STREAM_CLIENT_CONNECT, den du bisher nicht angeben musstest.

<?php

$ziele = [
'tcp://10.0.0.7:80',
'tcp://10.0.0.8:80',
'tcp://10.0.0.9:80',
];

$offen = [];
$flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;

foreach ($ziele as $ziel) {
$strom = stream_socket_client($ziel, $errno, $errstr, 5, $flags);

/* Hier ist noch nichts verbunden, der Aufbau laeuft erst an */
if ($strom === false) {
echo "$ziel: $errstr ($errno)\n";
continue;
}

$offen[] = $strom;
}

$lesen = null;
$fehler = null;
$schreiben = $offen;

/* Beschreibbar heisst hier: der Aufbau ist abgeschlossen */
if ($schreiben !== [] && stream_select($lesen, $schreiben, $fehler, 5) > 0) {
foreach ($schreiben as $strom) {
echo 'Erreichbar: ', stream_socket_get_name($strom, true), "\n";
}
}

foreach ($offen as $strom) {
fclose($strom);
}

Ein Detail entscheidet hier über richtig und falsch: ein Rückgabewert ungleich false bedeutet bei PHP stream_socket_client im asynchronen Betrieb noch keinen Erfolg, sondern nur, dass der Versuch angelaufen ist. Ob die Verbindung steht, zeigt erst stream_select(), indem der Stream in der Schreibliste auftaucht. Wer das weglässt, meldet unerreichbare Server als erreichbar. Geht es nur um den Statuscode einer URL, ist PHP get_headers() der kürzere Weg.

Unix-Domain-Sockets und persistente Verbindungen

Auf derselben Maschine braucht es keinen Netzwerkstack. Lokale Dienste bieten ihre Schnittstelle deshalb oft als Socket-Datei an, und PHP stream_socket_client erreicht sie über einen Pfad ohne Port. Ein trotzdem angehängter Port führt zu einem Aufbau, der nie gelingt.

<?php

/* Kein Port: die Adresse ist der Pfad zur Socket-Datei */
$strom = stream_socket_client(
'unix:///var/run/dienst.sock',
$errno,
$errstr,
5,
STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT
);

if ($strom === false) {
exit("Lokaler Dienst nicht erreichbar: $errstr ($errno)");
}

fwrite($strom, "PING\r\n");
echo fgets($strom);

/*
Bei einer persistenten Verbindung beendet fclose() die Leitung
nicht wirklich. Sie bleibt im Prozess erhalten, und der naechste
Aufruf uebernimmt sie samt allem, was vorher darauf passiert ist.
*/
fclose($strom);

Das zweite Flag spart bei jedem Folgeaufruf den Aufbau, weil die Leitung im Prozess bestehen bleibt. Der Preis wird gern übersehen: mitübernommen wird auch der Zustand. Steckt das Gegenüber noch in einer angemeldeten Sitzung, erbt der nächste Aufruf das mit. Bei einem zustandslosen Dienst ist das ein Gewinn, bei einem zustandsbehafteten eine Fehlerquelle.

Das Gegenstück auf der Serverseite heißt stream_socket_server() und arbeitet nach demselben Adressschema. Was daraus entstehen kann, zeigt das Tutorial zu WebSocket mit PHP.

Wann PHP stream_socket_client und wann cURL?

Diese Frage gehört vor die erste Zeile Code. Vier Wege stehen zur Auswahl, und sie unterscheiden sich weniger im Tempo als in der Arbeit, die sie dir abnehmen.

Weg Wofür geeignet TLS-Einstellungen Aufwand
file_get_contents() Eine URL abrufen, sonst nichts Über denselben Kontext wie hier Eine Zeile
cURL HTTP in jeder Form, auch POST und Upload Eigene Optionen, System-Bundle als Vorgabe Gering
fsockopen() Einfache Verbindungen in gewachsenem Code Kein eigener Kontext Mittel
stream_socket_client() Eigene Protokolle, Unix-Sockets, asynchroner Aufbau Voll steuerbar über den Kontext Höher

Wer mit fsockopen() bereits arbeitet, findet den Einstieg in Daten von einer externen Webseite auslesen. PHP stream_socket_client setzt dort an, wo jene Funktion nicht mehr weiterhilft: bei Verschlüsselung mit eigener Zertifikatsprüfung, bei Unix-Domain-Sockets und bei Verbindungen, die nicht auf den Aufbau warten sollen. Drei Unterschiede sind es im Kern: die einheitliche Adresse mit Transport, der Kontext für TLS und Socket-Optionen, und die beiden Flags. Wer keinen davon braucht, darf bei der alten Funktion bleiben.

Die Gegenrichtung gehört genauso deutlich gesagt: eine handgeschriebene HTTP-Anfrage über einen Socket ist fast immer die schlechtere Lösung, weil Statuscodes, Weiterleitungen und Komprimierung dann alle in deinem Code landen. Der Vorteil von PHP stream_socket_client liegt in der Kontrolle, nicht in der Geschwindigkeit.

Fazit zu PHP stream_socket_client

Vier Dinge tragen den größten Teil der Praxis. Die Adresse enthält Transport, Host und Port in einem String, bei einem lokalen Dienst einen Dateipfad ohne Port. Der Fehlerfall wird über die beiden Referenzparameter ausgewertet, nie stillgestellt. Zeitlimits gibt es zwei, eines für den Aufbau und eines fürs Lesen. Ein Zertifikatsfehler wird über eine passende Zertifikatsdatei gelöst, nicht durch Abschalten der Prüfung.

Bleibt die Frage nach dem Aufwand. Bei eigenen Protokollen, bei selbst bestimmter Prüfung, bei Unix-Domain-Sockets und beim gleichzeitigen Anlaufen vieler Ziele gibt es keine bequemere Alternative. Für alles, was am Ende doch nur eine HTTP-Anfrage ist, greifst du besser zu cURL. Diese Unterscheidung macht PHP stream_socket_client zu einem Werkzeug, das man selten braucht und dann durch nichts ersetzen kann.

 

Tags:

 

 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.