Carlo: file_get_contents vs. fsockopen

Hallo,

aus einem Script heraus sollen einige Daten an einen zentralen Host
gesendet werden. Also ohne das Script zu unterbrechen und auch ohne
eine Antwort abzuwarten. IMHO geht das mit file_get_contents und
auch mit fsockopen.

Gibt es noch andere Methoden?
Und welche ist davon am effektivsten und warum?

Bin gespannt auf eure Vorschläge und Ideen.

mit bestem Gruß Carlo

  1. Hallo,

    Gibt es noch andere Methoden?

    file, fopen, include, require... ;)

    Und welche ist davon am effektivsten und warum?

    Man sollte unbedingt fsockopen nutzen, da file_get_contents o.ä. zweckenfremdet werden (und auf keinen Fall include o.ä. nutzen).

    Mit fsockopen hast du die beste Kontrolle darüber was du machst, kannst steuern, was passiert, wenn der Host mal nicht erreichbar ist, kannst den HTTP Header senden und entsprechende Daten mitsenden usw.

    Außerdem hast du keine Probleme, wenn hoffentlich, allow_url_fopen deaktiviert  wurde.

    1. Hallo Klaus,

      Und welche ist davon am effektivsten und warum?
      Man sollte unbedingt fsockopen nutzen,

      okay

      da file_get_contents o.ä. zweckenfremdet werden

      wieso ist es eine Zweckentfremdung damit einen URL aufzurufen?
      Ich hatte vermutet, dass es extra dazu gemacht wurde ...

      Ansonsten habe ich eine kleine Zeitmessung gebastelt.
      Hier liegt fsockopen klar VOR file_get_contents, also eine Scriptlösung
      ist deutlich schneller als eine C-Implementation, das begreife wer will.
      Die Daten:
      222 x file_get_contents("http://$host/brw/receiver.php?I=1&D=1001");
      haben eine Laufzeit von:   18.961,306 ms
      222 x fsockopen() nur:      4.878,922 ms

      Da fällt eine Entscheidung nicht schwer - nur verstehen tue ich es nicht!

      Mit bestem Gruß Carlo

      1. Hallo,

        da file_get_contents o.ä. zweckenfremdet werden
        wieso ist es eine Zweckentfremdung damit einen URL aufzurufen?

        weil file_get_contents *eigentlich* dafür gedacht ist, eine *Datei* zu öffnen und ihren Inhalt auszulesen. Dass man -je nach PHP-Konfiguration- alternativ die Bequemlichkeit bietet, damit auch auf HTTP-Ressourcen zuzugreifen, ist eigentlich schon die Zweckentfremdung.

        Ansonsten habe ich eine kleine Zeitmessung gebastelt.
        Hier liegt fsockopen klar VOR file_get_contents, also eine Scriptlösung ist deutlich schneller als eine C-Implementation, das begreife wer will.

        Du vergleichst hier Bohrmaschinen mit Bohrern.

        222 x file_get_contents("http://$host/brw/receiver.php?I=1&D=1001");
        haben eine Laufzeit von:   18.961,306 ms

        Das bedeutet 222mal ...
         TCP/IP-Verbindung zu $host:80 aufbauen
         GET-Request auf /brw/receiver.php?I=1&D=1001 absetzen
         Serverantwort abwarten
         Header und Nutzinhalt der angeforderten Ressource empfangen
         TCP/IP-Verbindung wieder schließen
         Daten in einen PHP-String kopieren

        222 x fsockopen() nur:      4.878,922 ms

        Im Gegensatz zu 222mal ...
         TCP/IP-Verbindung zu $host:80 aufbauen
         TCP/IP-Verbindung wieder schließen

        Wenn du außer fsockopen() nichts weiter betrachtest, ist das also kein Wunder. Um einen realistischen Vergleich zu haben, müsstest du den Austausch der Request- und Response-Daten z.B. mit fwrite() und fread() auch in deine Rechnung einbeziehen. Und dann hat die C-Implementierung wahrscheinlich auch wieder die Nase vorn - wenn auch nur minimal, wie ich vermute. Denn hier überwiegen eindeutig die Wartezeiten bei der Kommunikation gegenüber der eigentlichen Script-Rechenzeit.

        Da fällt eine Entscheidung nicht schwer - nur verstehen tue ich es nicht!

        Den Eindruck habe ich auch.
        Trotzdem solltest du dich mit der fsockopen()-Lösung anfreunden, denn manche Hoster erlauben mit den file-Funktionen eben keinen URL-Zugriff (url_fopen_wrapper=off).

        Schönen Sonntag noch,
         Martin

        --
        Wenn man sieht, was der liebe Gott auf der Erde so alles zulässt, hat man das Gefühl, er experimentiert immer noch.
          (Sir Peter Ustinov, Charakterdarsteller, 2004 verstorben)
        1. Guten Tag,

          Trotzdem solltest du dich mit der fsockopen()-Lösung anfreunden, denn
          manche Hoster erlauben mit den file-Funktionen eben keinen URL-Zugriff
          (url_fopen_wrapper=off).

          Die Konfigurationsoption heißt allerdings allow_url_fopen. --enable-url-fopen-wrapper gibt es seit 4.3.0 nicht mehr.

          Ansonsten hast du Recht. Ich möchte noch auf pfsockopen() hinweisen.

          Gruß
          Christoph Jeschke

          --
          Zend Certified Engineer
        2. Hallo Martin,

          222 x fsockopen() nur:      4.878,922 ms
          Im Gegensatz zu 222 mal ...
          TCP/IP-Verbindung zu $host:80 aufbauen
          TCP/IP-Verbindung wieder schließen

          na-ja,
          wenigstens fwrite() muss verwendet werden, weil sonst der Zweck des Ganzen,
          die Übertragung von "I=1&D=1001", nicht funktioniert.
          Das normalerweise übliche fread(); kann jedoch entfallen!

          Bei dem Zeitunterschied hatte ich auch zuerst an die Responsedaten gedacht.
          Aber im Serverlog kann man an den Einträgen nicht unterscheiden, mit welcher
          Methode angefragt wird. D.h. die Daten landen in jedem Fall im TCP-Stack des
          anfragenden Hosts, sie werden halt nur nicht ausgelesen.

          Man findet unzählige Anleitungen, wie Sockets angewendet werden, aber niemand
          weiss, wie sie intern wirklich funktionieren.
          Wird das nicht mehr gelehrt? Das könnte irgendwann peinlich werden ...

          ... - nur verstehen tue ich es nicht!
          Den Eindruck habe ich auch.

          genau,
          sonst würde ich hier nicht posten, sondern Vorlesungen halten ... ;-)

          Schönen Sonntag noch,

          Danke gleichfalls
          Carlo

          1. Hi Carlo,

            222 x fsockopen() nur:      4.878,922 ms
            Im Gegensatz zu 222 mal ...
            TCP/IP-Verbindung zu $host:80 aufbauen
            TCP/IP-Verbindung wieder schließen
            na-ja,
            wenigstens fwrite() muss verwendet werden, weil sonst der Zweck des Ganzen, die Übertragung von "I=1&D=1001", nicht funktioniert. Das normalerweise übliche fread(); kann jedoch entfallen!

            je nachdem, was du wirklich vorhast, magst du Recht haben.

            Bei dem Zeitunterschied hatte ich auch zuerst an die Responsedaten gedacht. Aber im Serverlog kann man an den Einträgen nicht unterscheiden, mit welcher Methode angefragt wird.

            Stimmt - im Server-Log werden nur die Requests protokolliert. Man kann den Apachen aber so einstellen, dass auch die übertragene Datenmenge (Request und Response) mitprotokolliert wird. Dann würde der Unterschied auffallen. Das macht aber (vermutlich aus Performance-Gründen) kaum jemand.

            D.h. die Daten landen in jedem Fall im TCP-Stack des anfragenden Hosts, sie werden halt nur nicht ausgelesen.

            Aber nur der erste Block. Da der vom Client auf IP-Ebene nicht bestätigt wird, bricht der Server die Verbindung dann von sich aus ab, ohne den Rest noch hinterherzuschmeißen. Wenn der Client mit fclose() die Verbindung trennt, geht's sogar noch schneller; dann braucht der Server nicht einmal ein Timeout abzuwarten.

            Man findet unzählige Anleitungen, wie Sockets angewendet werden, aber niemand weiss, wie sie intern wirklich funktionieren.

            Meinst du das ernst?
            Mal ehrlich: Wie sie intern funktionieren, ist sicher auch interessant, aber für die Praxis meistens nicht relevant, zumal die Implementierung immer betriebssystemabhängig ist. Für die meisten Programmierer genügt es zu wissen, dass Sockets eine direkte Kontaktaufnahme auf IP-Ebene erlauben (wahlweise TCP oder UDP).

            Wird das nicht mehr gelehrt?

            Wurde das je gelehrt? Nach meiner Erfahrung gehört das zum Programmierer-Wissen, das man sich im Lauf der Zeit selbst aneignen muss.

            So long,
             Martin

            --
            F: Was ist eigentlich aus deinem schlimmen Durchfall geworden?
            A: Mein Arzt hat mir Valium verschrieben.
            F: Und das hilft?
            A: Naja, ich mach mir immer noch in die Hose. Aber inzwischen ist es mir egal.
            1. Hallo Martin,

              D.h. die Daten landen in jedem Fall im TCP-Stack des anfragenden Hosts, sie werden halt nur nicht ausgelesen.
              Aber nur der erste Block.

              vergiss es!
              Wenn eine Datei angefordert wird, wird sie auch gesendet und zwar komplett!
              Habe es getestet, der Apache schreibt nur ins Logfile, was er gesendet hat.
              Und zwar immer danach:
              192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.0" 200 389
              192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.0" 200 389
              192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.1" 200 389
              192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.1" 200 389
              Das HTTP/1.0 ist ein Abruf mit file_get_contents und HTTP/1.1 mit fsockopen.
              Okay, das sind nur 389 Byte, wieviele hättest Du denn gerne, um den Abruch zu prüfen?

              Meinst du das ernst?
              im Prinzip ja,
              habe Ende letztes Jahrtausend versucht eine deutsche Quelle zu finden,
              aber auch nur Deine Links der Anwendungs-Programmierung gefunden.
              Die beschreiben nicht wie sie funktionieren, nur wie man sie benutzt.

              Wird das nicht mehr gelehrt?
              Wurde das je gelehrt?

              oops

              Nach meiner Erfahrung gehört das zum Programmierer-Wissen, das man sich im Lauf der Zeit selbst aneignen muss.

              aha,
              Und Du bist sicher, dass sich die Jungs das aus dem Quellcode selbst rauspuzzeln?
              Wahrscheinlich während sie DSDS gucken ... ROFL ... ;-)

              Gruss Carlo

              1. Hallo,

                Wenn eine Datei angefordert wird, wird sie auch gesendet und zwar komplett!

                woran siehst du das? Doch nicht an deinem Beispiel mit einer sehr, sehr kleinen Ressource?

                Und zwar immer danach:
                192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.0" 200 389
                192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.0" 200 389
                192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.1" 200 389
                192.168.2.20 - [28/Sep/2008:12:47:27 +0200] "GET /receiver.php?I=1&D=1001 HTTP/1.1" 200 389
                Okay, das sind nur 389 Byte, wieviele hättest Du denn gerne, um den Abruch zu prüfen?

                Auf jeden Fall mehr. Ich bin mir jetzt nicht sicherm, wie groß die Blöcke üblicherweise sind - ich meine aber, deutlich über 1kB. Damit sind 389B auf jeden Fall schon mit einem einzigen Block übertragen, zumal das IP-Protokoll erlaubt, mehrere Datenblöcke im Voraus zu senden, ohne jeweils für jeden Block einzeln auf die Quittierung zu warten.

                Meinst du das ernst?
                im Prinzip ja, habe Ende letztes Jahrtausend versucht eine deutsche Quelle zu finden, aber auch nur Deine Links der Anwendungs-Programmierung gefunden.

                Die deutschen kannste meistens vergessen. Erstens sind sie seltener, zweitens oft gekürzt oder falsch/ungünstig übersetzt. Wann immer möglich, halte ich mich an die englische Dokumentation.

                Die beschreiben nicht wie sie funktionieren, nur wie man sie benutzt.

                Ich sagte ja: Für die meisten Programmierer ist das völlig ausreichend.
                Für die Systemprogrammierer, die so ein System erstmal implementieren müssen, sieht das sicher anders aus. Aber: Das Socket-Konzept ist nur eine "typische" Schnittstelle zwischen dem IP-Protokoll und der Anwendung aus der Unix-Welt. Jeder Betriebssystementwickler hat die Freiheit, diese Schnittstelle nach eigenem Ermessen zu implementieren. Natürlich versuchen alle nach Möglichkeit, sich an dem bewährten Unix-Konzept zu orientieren - wer wollte denn das Rad immer wieder und wieder neu erfinden.

                Wird das nicht mehr gelehrt?
                Wurde das je gelehrt?
                oops

                Genau. Als ich Informatik studiert habe, hat man uns zwar das Socket-Konzept aus Unix auch erläutert - aber eben genau so, wie du es jetzt auch siehst: Als grundsätzliches Konzept mit ein paar Anwendungsbeispielen. Aber sicher nicht, wie es intern realisiert ist.

                Nach meiner Erfahrung gehört das zum Programmierer-Wissen, das man sich im Lauf der Zeit selbst aneignen muss.
                aha, Und Du bist sicher, dass sich die Jungs das aus dem Quellcode selbst rauspuzzeln?

                Nö. Sie sehen sich die API an und ziehen daraus ihre Schlüsse, bzw. sie versuchen, das Prinzip zu verstehen und so zu realisieren, dass es zum API passt.

                Ciao,
                 Martin

                --
                Finanztipp:
                Leihen Sie sich Geld von einem Pessimisten.
                Er rechnet sowieso nicht damit, dass er es zurückbekommt.
                1. Hallo Martin,

                  Auf jeden Fall mehr.

                  okay:
                  192.168.2.20 - [28/Sep/2008:15:18:53 +0200] "GET /brw/receiver.php?I=1&D=1000 HTTP/1.0" 200 196076
                  192.168.2.20 - [28/Sep/2008:15:18:53 +0200] "GET /brw/receiver.php?I=1&D=1001 HTTP/1.0" 200 196093
                  192.168.2.20 - [28/Sep/2008:15:18:53 +0200] "GET /brw/receiver.php?I=1&D=1001 HTTP/1.1" 200 74117
                  192.168.2.20 - [28/Sep/2008:15:18:53 +0200] "GET /brw/receiver.php?I=1&D=1001 HTTP/1.1" 200 74117

                  Hier sieht man nun einen wesentlichen Unterschied von gesendeten Bytes.
                  Damit wird der Zeitvorsprung von fsockopen vs. file_get_contents deutlich mysteriöser.
                  An der zu empfangenden Datenmenge liegt es offensichtlich nicht, denn die sind viel zu klein,
                  um einen Unterschied zu bewirken.

                  Gruss Carlo

          2. Hallo,

            na-ja,
            wenigstens fwrite() muss verwendet werden, weil sonst der Zweck des Ganzen,
            die Übertragung von "I=1&D=1001", nicht funktioniert.
            Das normalerweise übliche fread(); kann jedoch entfallen!

            Nein! Wenn Du nicht komplett auf die Serverantwort wartest, dann wird das Script, das Du remote aufrufst, abgebrochen - und zwar u.U. mitten in der Verarbeitung. Das führt dann dazu, dass die Aktionen, die Du ausführen willst, evtl. nicht vollständig durchgeführt werden. Daher auch die Differenzen in der Menge der übertragenen Antwortdaten, wie Du sie weiter unten im Thread feststellst.

            Ferner: Rein konzeptionell ist der Flaschenhals immer beim Remote-Script und *nie* bei fsockopen() vs. file_get_contents(). Wenn Du fsockopen() nämlich *korrekt* anwedest, dann braucht das mit Sicherheit etwa genau so lange wie file_get_contents() - hat aber den großen Nachteil, dass Du HTTP selbst implementieren musst - bei file_get_contents() wird Dir diese Aufgabe abgenommen.

            Viele Grüße,
            Christian