2010-03-01 8 views
1

J'ai rencontré un problème étrange. J'ai écrit un petit démon en Perl qui lie un port sur un serveur. Sur le même serveur il y a une LAMP en cours d'exécution et le client pour mon démon Perl est un fichier php qui ouvre une socket avec le démon, pousse quelques informations et ferme ensuite la connexion. Dans le démon Perl, je consigner chaque connexion dans un fichier journal pour une utilisation ultérieure.Perl IO :: problème de synchronisation de socket

Mon plus gros problème est le suivant: entre le moment où le script php termine son exécution, il y a 15-20secondes jusqu'à ce que le démon enregistre la connexion.

PHP Client:

$sh = fsockopen("127.0.0.1", 7890, $errno, $errstr, 30); 
if (!$sh) 
{ 
echo "$errstr ($errno)<br />\n"; 
} 
else 
{ 

    $out = base64_encode('contents'); 
    fwrite($sh, $out); 
    fclose($sh); 
} 

Perl démon (seulement la partie femelle)

#!/usr/bin/perl 

use strict; 
use warnings; 
use Proc::Daemon; 
use Proc::PID::File; 
use IO::Socket; 
use MIME::Base64; 
use Net::Address::IP::Local; 

MAIN: 
{ 
#setup some vars to be used down... 
if (Proc::PID::File->running()) 
     { 
       exit(0); 
     } 


     my $sock = new IO::Socket::INET(
         LocalHost => $ip, 
         LocalPort => $port, 
         Proto => 'tcp', 
         Listen => SOMAXCONN, 
         Reuse => 1); 

     $sock or die "no socket :$!"; 
     my($new_sock, $c_addr, $buf); 

for (;;) 
     { 

       # setup log file 
       open(LH, ">>".$logs); 

       print "SERVER started on $ip:$port \n"; 
       print LH "SERVER started on $ip:$port \n"; 

       while (($new_sock, $c_addr) = $sock->accept()) 
       { 
         my ($client_port, $c_ip) =sockaddr_in($c_addr); 
         my $client_ipnum = inet_ntoa($c_ip); 
         my $client_host =gethostbyaddr($c_ip, AF_INET); 

         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time; 
         $year += 1900; 
         $mon += 1; 
         print "$year-$mon-$mday $hour:$min:$sec [".time()."] - got a connection from: [$client_ipnum]"; 

         open(AL, ">>".$accessLog); 
         print AL "$year-$mon-$mday $hour:$min:$sec [".time()."] - got a connection from: [$client_ipnum]\n"; 
         close AL; 

         while (defined ($buf = <$new_sock>)) 
         { 
           print "contents:", decode_base64($buf), " \n"; 
           open(FH, ">".$basepath."file_" . time() .".txt") or warn "Can't open ".$basepath."file_".time().".txt for writing: $!"; 
           print FH decode_base64($buf); 
           close FH; 
         } 
       } 
       close LH; 

     } 

} 

Quelle est la chose que je fais si mal et conduit alors à 20 secondes écart entre php fermer la prise après l'écriture et le script Perl enregistrant la connexion. Une idée? Soyez gentil, je suis nouveau à Perl :)

+0

Si j'étais vous, j'ajouterais plus d'impressions de débogage() dans les boucles. Juste en regardant à partir du code, il pourrait imprimer FH est mise en mémoire tampon, ou peut-être <$new_sock> ne ferme pas rapidement et il est coincé dans la boucle. Ou peut-être que tout fonctionne mais la journalisation est retardée? –

Répondre

2

$ new_sock n'est pas fermé explicitement et n'est donc pas fermé avant l'acceptation de l'appel. Cela peut entraîner le blocage de certaines choses jusqu'à ce que les délais d'expiration soient atteints. (Je ne sais pas si la fermeture se produira à l'entrée pour accepter ou quitter.)

En outre, vous utilisez l'opérateur « <> » pour lire les données à partir d'une prise. Que se passe-t-il s'il n'y a pas de nouvelle ligne dans l'entrée?

La meilleure façon de voir ce qui se passe réellement est d'exécuter le processus sous "strace -e trace = network" et d'essayer de faire correspondre l'appel système réseau avec les instructions perl et php.

+0

C'est vrai :) J'ai fermé le $ new_socket avant que le gros ne se termine et tout a bien fonctionné. Pouvez-vous me dire une meilleure méthode de lecture du client si j'envoie en "première ligne" la longueur du contenu qui va être envoyé? Merci! –

+0

Je pense que vous avez des problèmes fondamentaux avec votre serveur: par exemple, je ne vois pas comment il peut gérer plus d'une connexion à la fois. L'approche la plus simple est de fork() après l'acceptation, et de gérer tous les IO dans l'enfant et de quitter à la fin: google pour "perl cookbook" et les serveurs forking. Pour lire des données, j'aurais tendance à utiliser read() ou sysread() selon que j'utilise la mise en mémoire tampon. – Martin

+0

Je vois. Merci pour l'info :) Je vais aborder le problème de la fonte à côté. Au moment où je me suis installé pour $ new_sock-> recv(); pour lire les données du client. Merci pour le soutien! –

0

Je ne vois aucun appel pour vider le tampon, pourriez-vous vérifier si le retard disparaît lors du rinçage après la journalisation?

+0

J'ai essayé de tirer la chasse après l'enregistrement et je n'ai obtenu aucune amélioration. Peut-être que le code Perl que j'ai écrit est mauvais? –

+0

Flush ne fait pas de mal. Quand la socket est-elle vraiment hors de portée du côté PHP? – weismat

+0

Du côté PHP, je viens d'écrire des données sur le socket et de le fermer. La chose est très bien, j'ai vérifié avec tcmpdump et tout est ok, juste le démon perl traîne sur l'écriture dans la console (en cours d'exécution pour le développement avec démoniser off) ou le fichier journal. –