opi: Socket auslesen und Datenverarbeitung

Hallo zusammen,

mittels Perl und IO::Socket::INET stelle ich eine Client-Server
Verbindung her. Nun werden vom Server allerhand Informationen zum
Client gesendet, die ich in einer while-Schleife verarbeite.

Nun weiß ich nicht so recht, an welcher Stelle ich die Daten in
meinem Skript verarbeiten soll... prüfen (bei bestimmten Daten
wird eine eMail generiert), filtern, die Daten aufteilen und
tabellenartig wegschreiben.

Ich möchte die Verarbeitung gerne schon in der Schleife

while ($socket) {
      # Verarbeitung
      # eMail falls notwendig
      # Wegschreiben
   }

durchführen. Könnte was dagegen sprechen?

Ich habe mal irgendwo gelesen, dass man die Daten zunächst aufnehmen
sollte und später verarbeiten, damit die Client-Server Verbindung
um so schneller geschlossen werden kann. Warum jedoch kann ich mir
nicht so recht vorstellen.

Habt Ihr ein paar Tipps für mich?

Greez,
opi

--
Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
  1. Tag opi.

    mittels Perl und IO::Socket::INET stelle ich eine Client-Server
    Verbindung her. Nun werden vom Server allerhand Informationen zum
    Client gesendet, die ich in einer while-Schleife verarbeite.

    Jepp, das ist das normale Vorgehen.

    Ich habe mal irgendwo gelesen, dass man die Daten zunächst aufnehmen
    sollte und später verarbeiten, damit die Client-Server Verbindung
    um so schneller geschlossen werden kann. Warum jedoch kann ich mir
    nicht so recht vorstellen.

    Ich vermute mal, dass du auf Folgendes hinauswillst:

    Wenn du mehrere eingehende Verbindungen nacheinander entgegennehmen willst, geschieht das ja für gewöhnlich so:

    use strict;  
    use IO::Sockets;  
      
    my $server = IO::Socket::INET->new("www.example.org:80")  
       or die "Error connecting to Server: $@\n";  
    my $client;  
    my $answer;  
    while($client = $server->accept()) {  
      $answer = <$client>;  
      # tu was mit $answer  
    }  
    close($server);
    

    Der Aufruf von accept() bewirkt, dass - falls sich keine Verbindung in der Warteschlange befindet - das Script solange stehen bleibt, bis eine Verbindung hereinkommt. Abhilfe schafft hier, non-blocking Sockets zu verwenden, Hilfe hierbei liefert das Modul Fcntl.

    Falls deine Serverantwort nicht einzeilig sein sollte, müsstest du natürlich entsprechend anders vorgehen:

    while($client = $server->accept()) {  
      while(<$client>) {  
        # tu was mit der aktuellen Zeile  
      }  
    }
    

    Normalerweise funktioniert der Zugriff über eine while-Schleife ein bisschen anders, denn Perl ist ja nicht blöd :-) Statt Zeile für Zeile in Echtzeit abzurufen, puffert Perl in weiser Voraussicht einen wesentlich größeren Inhalt der Antwort, um teure Systemaufrufe und damit Zeit zu sparen. Das kann man umgehen, indem man den Handle durch setzen von $| auf einen wahren Wert "hot" macht, und genau dies geschieht bei IO::Socket automatisch. Dann wird aber das Puffern unterbunden, somit sind mehr Systemaufrufe nötig und das Script wird langsamer ("Suffering from buffering"). Bei ein paar Zeilen ist das m.E. unproblematisch, bei großen Datenmengen kann es aber deshalb von Vorteil sein, den gesamten Inhalt in ein Array zu schreiben, um schnelleren Zugriff auf die Daten zu bekommen:

    while($client = $server->accept()) {  
      @lines = <$client>;  
      foreach(@lines) {  
        # tu was mit der aktuellen Zeile  
      }  
    }
    

    Sehr ausführlich beschrieben ist es in http://www.foo.be/docs/tpj/issues/vol3_3/tpj0303-0002.html.

    Siech*in der Hoffnung, nichts übersehen/vergessen zu haben*fred

    1. Hallo Siechfred,

      Ich vermute mal, dass du auf Folgendes hinauswillst:

      Wenn du mehrere eingehende Verbindungen nacheinander entgegennehmen willst, geschieht das ja für gewöhnlich so:

      nicht so ganz.

      Ich habe einen Agenten auf jedem Server laufen.

      while (my $client = $server->accept()) {
         my $q = <$client>;
         # Anfrage Verarbeiten
         print $client "meine Antwort";
         # ca. 600kb werden zurück gesendet
      }

      Und hier schicke ich meine Anforderung an den Agenten:

      my $q = "anfrage an der Agenten";

      my $socket = IO::Socket::INET->new(
                      PeerAddr => $ip,
                      PeerPort => $service,
                      Proto    => "tcp",
                      Type     => SOCK_STREAM
      )

      print $socket "$q\n";

      while (<$socket>) {
         # [1] Antwort bearbeiten
      }

      close $socket;

      [1] genau diese Stelle meinte ich. Die Daten müssen verarbeitet und
      in mehrere Dateien weggeschrieben werden.

      Wäre es also ratsamer, die Daten zunächst in ein Array oder einen
      Hash zu packen oder kann ich sie auch sofort verarbeiten und
      wegschreiben?

      while (<$socket>) {
         last if /^EOF/;
         # push @$arr, $_;
         # oder
         # Verarbeitung und Wegschreiben
      }

      Greez,
      opi

      --
      Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
      1. Tag opi.

        while (<$socket>) {
           # [1] Antwort bearbeiten
        }
        Wäre es also ratsamer, die Daten zunächst in ein Array oder einen
        Hash zu packen oder kann ich sie auch sofort verarbeiten und
        wegschreiben?

        Das ist m.E. eine Frage der Geschwindigkeit und des verfügbaren Speichers. Natürlich könntest du dir eine Datenstruktur anlegen, die zunächst die erhaltenen Daten speichert, die Verbindung wieder beendet und dann die Verbindung zum nächsten Server aufbaut usw., nach Abrufen aller Verbindungen würdest du dann diese Datenstruktur im nächsten Schritt auswerten. Und diese Struktur kann halt einen ziemlich großen Umfang annehmen, was möglicherweise zu Lasten des Speichers geht. Du kannst natürlich auch gleich die Daten in einer while-Schleife verarbeiten. Letztendlich ist es eine Frage der Performance des Scripts, das müsstest du halt testen. Die Verbindungsdauer jedenfalls dürfte nur geringfügig variieren, egal, ob du die Daten zwischenspeicherst (was m.E. die schnellere Variante wäre) oder "on the fly" auswertest. Eventuell wäre ein Mittelweg in der Form gangbar, dass du in der while-Schleife die Daten auswertest und das *Ergebnis* in einer Datenstruktur ablegst, die dann später ausgewertet wird (E-Mails versenden u.ä.).

        Siechfred

        1. Hallo,

          Das ist m.E. eine Frage der Geschwindigkeit und des verfügbaren Speichers. Natürlich könntest du dir eine Datenstruktur anlegen, die zunächst die erhaltenen Daten speichert, die Verbindung wieder beendet und dann die Verbindung zum nächsten Server aufbaut usw.,

          Es ist nur so, dass die Datenstruktur ziemlich groß ist. Ich würde
          die Daten in einen Hash packen, der dann allerdings sehr viele
          Schlüssel haben wird. Da wäre dann auch schon gleich meine nächste
          Frage...

          Ist es üblich, dass man die Schlüsselnamen für alles mögliche
          missbraucht? Beispielsweise würde ich, um den Hash nach der
          Verarbeitung besser auslesen zu können, den ersten Key als Datei-
          namen nutzen und zu guter Letzt die Daten Zeilenweise als Array
          dort ablegen. Beispiel:

          Beispiel-Hash:

          use Fcntl qw(:DEFAULT :flock);  
            
          my $hash = {};  
          $hash->{Datei_1}->[0] = "1 2 3 4 5";  
          $hash->{Datei_1}->[1] = "1 2 3 4 5";  
          $hash->{Datei_2}->[0] = "1 2 3 4 5";  
          $hash->{Datei_2}->[1] = "1 2 3 4 5";  
          $hash->{Datei_3}->[0] = "1 2 3 4 5";  
          $hash->{Datei_3}->[1] = "1 2 3 4 5";  
          $hash->{Datei_4}->[0] = "1 2 3 4 5";  
            
          # Daten wegschreiben  
          foreach my $file (keys %{$hash}) {  
             die "datei $file kann nicht geöffnet werden\n"  
                unless sysopen FILE,"./$file",O_WRONLY | O_APPEND | O_CREAT;  
             flock(FILE,LOCK_EX);  
             foreach my $line (@{$hash->{$file}}) {  
                print FILE "$_\n";  
             }  
             close FILE;  
          }
          

          Greez,
          opi

          --
          Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
      2. Hallo,

        dein skript stellt ja den client da. Du riskierst in dem Skript mit der direkten Verarbeitung incl wegschreiben und etc, dass, wenn das Wegschreiben nicht sofort klappt (gelockte Datei etc), einfach die Verbindung so lange offen bleibt, bis die Daten verarbeitet sind. Nun ist halt die Frage, ob es dir wichtiger ist, dass das Skript weniger Speicher verbraucht, oder die Verbindung schneller beendet wird.

        gruss

        --
        no strict;
        no warnings;
        79.78 cups of Coffee (Brewed) + Me = Death
        Reklame ist die Kunst, auf den Kopf zu zielen und die Brieftasche zu treffen.
        1. Hallo Eternius,

          Hallo,

          dein skript stellt ja den client da. Du riskierst in dem Skript mit der direkten Verarbeitung incl wegschreiben und etc, dass, wenn das Wegschreiben nicht sofort klappt (gelockte Datei etc), einfach die Verbindung so lange offen bleibt, bis die Daten verarbeitet sind.

          daran hatte ich noch garnicht gedacht! Allerdings habe ich den
          Vorteil, dass der Server und der Client einzig und allein über diese
          Verbindung unterhalten.

          Nun ist halt die Frage, ob es dir wichtiger ist, dass das Skript weniger Speicher verbraucht, oder die Verbindung schneller beendet wird.

          Ich werde mal Testen, auf wieviel Speicher der Prozess anwächst und
          dann meine Schlüsse ziehen!

          Bis hierhin danke schonmal!

          Greez,
          opi

          --
          Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|