[Perl] lecture/écriture simultanées sur une socket

lecture/écriture simultanées sur une socket [Perl] - Perl - Programmation

Marsh Posté le 11-08-2007 à 12:51:16    

Bonjour,
 
J'aimerais faire du client-serveur intéractif en Perl.
 
J'ai un serveur.pl qui fonctionne correctement, j'ai repris ce script : http://perl.enstimac.fr/DocFr/perl [...] io::socket
 
Et j'ai un client.pl dont voici le code :
 

Code :
  1. use IO::Socket;
  2. use warnings;
  3. use strict;
  4. my ($sock, $kidpid);
  5. $sock = IO::Socket::INET->new(Proto     => "tcp",
  6.                               PeerAddr  => "192.168.0.11",
  7.                               PeerPort  => 9000)
  8.                         || die "Failed : $!";
  9. $sock->autoflush(1);
  10. $kidpid = fork();
  11. die "can't fork: $!" if ! defined($kidpid);
  12. if ($kidpid) {
  13.     # PERE : Ecoute des entrées clavier et envoi à la socket
  14.     while (<STDIN> ) { print $sock $_; }
  15. } else {
  16.     # FILS : Ecoute de la socket et affichage à l'écran
  17.     while (<$sock> ) { print STDOUT $_; }
  18. }
  19. close $sock;


Mon problème :
La connexion réussie, et le client reçoit et affiche les messages d'accueil du serveur.
Mais après la première ligne rentrée au clavier, çà se "bloque". Et le serveur ne recoit même pas cette ligne.
 
Après un déboggage "approfondi", j'en ai conclu que le processus père n'arrive pas à écrire sur la socket tant que le fils boucle sur cette même socket pour écouter.
En effet, si je ne fais que écrire ou que écouter, cà fonctionne très bien.
 
J'ai essayer plusieurs scripts client trouvés sur le net et j'en arrive toujours au même problème.
Même celui proposé ici réagit pareil : http://perl.enstimac.fr/DocFr/perl [...] io::socket
 
Je suis sous Windows 2000 et j'utilise Perl 5.8.8
Est-ce à cause de Windows qui gère mal le fork() ou les sockets ?
 
Merci d'avance pour votre aide !!

Reply

Marsh Posté le 11-08-2007 à 12:51:16   

Reply

Marsh Posté le 12-08-2007 à 09:19:33    

Bonjour,  
Je ne suis pas sûr que l'on puisse avoir le même programme pour envoyer/recevoir, et donc utiliser un fork.
 
Pour ma part j'utilise deux programmes séparés :  
Pour recevoir :  
#!/usr/bin/perl  
use strict;
use warnings;
$|=1 ;
 
use IO::Socket;
use warnings;
use strict;
my $sock = IO::Socket::INET->new(Proto     => "tcp",
                             PeerAddr  => "127.0.0.1",
                             LocalPort  => 9000,
                             Reuse=>1,  
                             Listen=>1)
                       || die "Failed : $!";
my $sock_l = $sock->accept() ;
while (1) { my $lig = <$sock_l> ;  
            last if ( $lig eq "q\n" ) ;
            print $lig if ( $lig ) ;
            select(undef, undef, undef, 0.25) ;
          }
close $sock;
_________________________________________________________________________________
Et pour envoyer :
 #!/usr/bin/perl  
use IO::Socket;
use warnings;
use strict;
 
my $sock = IO::Socket::INET->new(Proto     => "tcp",
                             PeerAddr  => "127.0.0.1",
                             PeerPort  => 9000 )  
                       || die "Failed : $!";
 
$sock->autoflush(1);
while ( 1 ) {  
  my $lig = <STDIN> ;
  print $sock $lig ;
  last if ( $lig eq "/q\n" ) ;  
}
close $sock;
 
 
 
 

Reply

Marsh Posté le 12-08-2007 à 11:38:23    

pmarion a écrit :

Je ne suis pas sûr que l'on puisse avoir le même programme pour envoyer/recevoir, et donc utiliser un fork.


Pourtant, je tire cet exemple de la doc du CPAN (Lien)
 
Faire 2 programmes est une solution mais du coup, le serveur voit çà comme 2 clients distincts. çà complique tout :(
 
Selon le CPAN, c'est possible avec le même programme et un fork (donc 2 processus) :

Citation :

Une fois que vous avez obtenu la connexion au service avec lequel vous désirez discuter, appelez fork pour cloner votre processus. Chacun de ces deux processus identiques a un travail très simple à réaliser : le parent copie tout ce qui provient de la socket sur la sortie standard, pendant que le fils copie simultanément sur la socket tout ce qui vient de l'entrée standard.


Est-ce que quelqun pourrait essayer ces 2 scripts du CPAN et me dire si çà fonctionne ?
Serveur
Client

Reply

Marsh Posté le 12-08-2007 à 14:29:47    

Non, je ne crois pas que le serveur voie deux clients.
 
Il faut lancer d'abord le programme de réception qui sera donc le serveur et le programme d'envoi qui sera le client qui se connecte au serveur.

Reply

Marsh Posté le 12-08-2007 à 14:49:19    

En ce qui concerne les exemples de CPAN je les ai testés sous LINUX et ils fonctionnent.  
 
Pour Win32 il existe peut-être un problème de fork avec des vieilles versions de perl, quelle version de perl, as-tu ?

Reply

Marsh Posté le 12-08-2007 à 15:23:39    

Voici un exemple complet sans fork du côté client.
 
Peux-tu essayer ces deux programmes chez moi sous Win32 ou Linux ils fonctionnent  :  
 
Version serveur :
 
#!/usr/bin/perl  
use strict;
use warnings;
$|=1 ;
 
use IO::Socket;
use Net::hostent;        
 
use warnings;
use strict;
my $sock = IO::Socket::INET->new(Proto     => "tcp",
                             LocalAddr  => "127.0.0.1",
                             LocalPort  => 9000,
                             Reuse=>1,  
                             Listen=>5)
                       || die "Failed : $!";
my $client ;  
my $hostinfo ;  
while ( $client = $sock->accept() ) {
  $client->autoflush(1) ;
  $hostinfo = gethostbyaddr($client->peeraddr);
  print "Connection" . $hostinfo->name() . "\n" ;
  while ( <$client> ) {
    my $lig = $_ ;
    print $lig if ( $lig ) ;
    if ( $lig eq "who\n" ) {
      print $client "who who who who who who\n" ;
    }
    elsif ( $lig eq "motd\n" ) {
      print $client "motd motd motd\n" ;
    }
    elsif ( $lig eq "quit\n" ) {
      print "Déconnexion\n" ;
      last ;
    }
    elsif ( $lig eq "motd\n" ) {
      print $client "motd motd motd\n" ;
    }
    else {
      print $client "Commande : who motd quit\n" ;
    }
    select(undef, undef, undef, 0.25) ;
  }
}
close $sock;
 
--------------------------------------------------------------------------------------------------------------------
 
Version client
#!/usr/bin/perl  
use IO::Socket;
use warnings;
use strict;
 
my $sock = IO::Socket::INET->new(Proto     => "tcp",
                             PeerAddr  => "127.0.0.1",
                             PeerPort  => 9000 )  
#                            PeerPort  => 9000, Listen=>1, Reuse=>1  )  
                       || die "Failed : $!";
 
$sock->autoflush(1);
while ( 1 ) {  
  my $lig = <STDIN> ;
  print $sock $lig ;
  last if ( $lig eq "quit\n" ) ;  
  my $reponse = <$sock> ;
  print $reponse ;
}
close $sock;

Reply

Marsh Posté le 13-08-2007 à 18:18:21    

D'abord, merci pour ton aide :)
 

pmarion a écrit :

Pour Win32 il existe peut-être un problème de fork avec des vieilles versions de perl, quelle version de perl, as-tu ?

J'ai la dernière version en date : 5.8.8 Build 822
 
Tes 2 programmes fonctionnent bien chez moi sous Win32.
Wow! Je suis bluffé. Comment ce while(1) arrive à tourner ?? Que représente ce '1' ?
 

Reply

Marsh Posté le 14-08-2007 à 09:35:50    

Le while (1) est l'equivalent d'une boucle sans fin (while true en shell) dont on sort heureusement avec last .
Comme d'habitude en perl, il existe plusieurs façons de générer un bloc .
 
En fait l'utilisation de fork complique la programmation pour un client simple.
En effet si le client envoie une requête et attend une réponse, il n'y a pas de raison de se compliquer la vie.
 
Par contre si l'application est plus  complexe il es nécessaire de passer au fork puisque chaque processus aura son autonomie.  
 
D'après mes tests le fork fonctionne bien en 5.8.8 sous Win32 et pour savoir ou cela bloque, il faudrait ajouter des print  "Etape xxx" à chaque étape du serveur, client père et fils pour voir ce qui se passe.  
 
Je ne sais pas sit tu veux continuer avec fork, mais je suis à ta disposition pour t'aider
 

Reply

Marsh Posté le 14-08-2007 à 11:47:57    

Effectivement, un client simple (comme ton exemple), recevra une réponse seulement après avoir envoyer une requête. Cà, je l'ai compris seulement ce matin :p
 
En effet, j'ai besoin que les 2 processus (lecture et écriture) aient leur autonomie.
 
Voici mon client avec des print à toutes les étapes :
(Pour le serveur, j'ai repris ton code)
 

Code :
  1. use strict;
  2.     use warnings;
  3.     use IO::Socket;
  4.     my ($kidpid, $sock, $ligne);
  5.     $sock = IO::Socket::INET->new(Proto     => "tcp",
  6.                                   PeerAddr  => "localhost",
  7.                                   PeerPort  => 9000)
  8.                               || die "Failed : $!";
  9.     $sock->autoflush(1);
  10.     print "DEBUT\n";
  11.     die "Can't fork: $!\n" unless defined($kidpid = fork());
  12.     # Père : Lecture de la socket
  13.     if ($kidpid) {
  14.         print "1\n";
  15.         while ($ligne = <$sock> ) {
  16.             print "2\n";
  17.             print $ligne;
  18.             print "3\n";
  19.         }
  20.         print "4\n";
  21.         kill("TERM", $kidpid); # envoie SIGTERM au fils
  22.         print "5\n";
  23.     }
  24.     # Fils : Ecriture vers la socket
  25.     else {
  26.         print "A\n";
  27.         while ($ligne = <STDIN> ) {
  28.         print "B\n";
  29.         print $sock $ligne ;
  30.         print "C\n";
  31.         }
  32.     }
  33.     print "FIN\n";


Je lance serveur et client :
 
DEBUT
1
A
2
HELLO
3

 
Les étapes 1,2,3 montrent que le père a bouclé une fois car le serveur a envoyé "HELLO". Là, le père écoute toujours la socket (ligne 19).
L'étape A montre que le fils reste en écoute sur le <STDIN> (ligne 31).
 
Ensuite, je tape "who" :
 
who
B

 
Et là, on a juste l'étape B qui montre que le fils a capté le "who" mais il reste bloqué sur l'écriture de la socket (ligne 33).
Et je n'ai plus la main.
 
 
Si le fils reste bloqué sur l'écriture de la socket, c'est parce que le père est en train de l'écouter ?
 

Reply

Marsh Posté le 14-08-2007 à 11:52:56    

J'avais fait un truc un peu comme ca en C (jeu d'échec) et effectivement j'ai utilisé les forks

Reply

Marsh Posté le 14-08-2007 à 11:52:56   

Reply

Marsh Posté le 14-08-2007 à 12:06:43    

Normalement l'envoi est indépendant de la réception car les sockets utilisent dans tampons et donc
en envoi de données n'est pas perdu même si le serveur n'est pas en état de répondre.
 
Si l'in connecte deux clients séparés sur un serveur simple que ne gère qu'une connexion à la fois, le deuxième client semble bloqué mais dès que le premier se déconnecte le deuxième reçoit les données et les traite.
 
Mais je ne sais même pas si c'est ton cas .
Je ne pense donc pas  

Reply

Marsh Posté le 14-08-2007 à 13:52:48    

Je suis d'accord pour le serveur simple qui ne sait gérer qu'un client à la fois. D'ailleurs c'est ce que j'utilise dans le test ci-dessus.
Pour le moment, je fais une seule connexion à la fois (un seul client) donc çà ne pose pas de problème.
 
 

pmarion a écrit :

D'après mes tests le fork fonctionne bien en 5.8.8 sous Win32

Chez moi aussi, le fork a l'air de réussir.
J'obtiens bien 2 processus différent qui arrivent à bosser (les print fonctionnent par exemple).
 

pmarion a écrit :

En ce qui concerne les exemples de CPAN je les ai testés sous LINUX et ils fonctionnent.

Et si tu les teste sur Win32, n'es-tu pas non plus bloqué, côté client, après la première saisie au clavier ?
 
Peux-être faut-il paramétrer autrement la création de la socket sur Win32 (IO::Socket::INET->new) ?

Reply

Marsh Posté le 14-08-2007 à 15:28:30    

Quel serveur utilises-tu ?
 Est-ce lui qui gère le HELLO qui est sur ta trace ?  
Peux-tu envoyer les instructions de ton serveur ?
 

Reply

Marsh Posté le 14-08-2007 à 18:08:44    

C'est bien le serveur qui gère le HELLO affiché sur la trace.
Voici le code du serveur que j'ai utilisé pour le test :

Code :
  1. use strict;
  2. use warnings;
  3. $|=1 ;
  4. use IO::Socket;
  5. use Net::hostent;
  6. my $sock = IO::Socket::INET->new(Proto      => "tcp",
  7.                                  LocalAddr  => "127.0.0.1",
  8.                                  LocalPort  => 9000,
  9.                                  Reuse      => 1,
  10.                                  Listen     => 5)
  11.                        || die "Failed : $!";
  12. my $client ;
  13. my $hostinfo ; 
  14. while ( $client = $sock->accept() ) {
  15.   $client->autoflush(1) ;
  16.   $hostinfo = gethostbyaddr($client->peeraddr);
  17.   print "Connexion de " . $hostinfo->name() . "\n" ;
  18.  
  19.   print $client "HELLO\n";
  20.   while ( <$client> ) {
  21.     my $lig = $_ ;
  22.     print $lig if ( $lig ) ;
  23.     if ( $lig eq "who\n" ) {
  24.       print $client "who who who who who who\n" ;
  25.     }
  26.     elsif ( $lig eq "motd\n" ) {
  27.       print $client "motd motd motd\n" ;
  28.     }
  29.     elsif ( $lig eq "quit\n" ) {
  30.       print "Déconnexion\n" ;
  31.       last ;
  32.     }
  33.     elsif ( $lig eq "motd\n" ) {
  34.       print $client "motd motd motd\n" ;
  35.     }
  36.     else {
  37.       print $client "Commande : who motd quit\n" ;
  38.     }
  39.     select(undef, undef, undef, 0.25) ;
  40.   }
  41. }
  42. close $sock;


 
Le serveur peut répondre au client sans problème.  
Et le client peut afficher la réponse du serveur tant qu'il ne bloque pas après la première saisie au clavier.

Reply

Marsh Posté le 19-08-2007 à 11:41:45    

:cry:  
 
Je n'ai toujours pas trouvé de solution pour avoir un tel client.
 
Personne n'a déjà fais çà pour Windows ?
 
Anybody help ?

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed