Newsgroup: it.comp.lang.perl
Titolo: Multiplexing con i socket...

Salve, sto cercando di scrivere un programma in Perl che usi i Socket e che
accetti in qualsiasi momento sia input da STDIN che dal socket connesso, non
cioč dovendo aspettare che la controparte risponda per scrivere di nuovo..
Ora, forse la guida da cui ho preso spunto fa cilecca, perchč questo č piů o
meno (a parte il nome di qualche variabile) ciň che suggerisce e non
funziona, o almeno non fa quello che io vorrei: bisogna sempre aspettare,
dopo aver inviato un messaggio, che il client risponda, e il programma si
blocca fino a quel momento.
Questo č il codice:
#!/usr/bin/perl
use Socket;
$portname = "test";
$proto = getprotobyname('tcp') or die "getprotobyname error: $! \n";
$port = getservbyname($portname,'tcp') or die "getservbyport error: $! \n";
socket(ASCOLTO, PF_INET, SOCK_STREAM, $proto);
bind(ASCOLTO, sockaddr_in($port,INADDR_ANY)) or die "bind: $! \n";
listen(ASCOLTO,SOMAXCONN) or die "listen: $! \n";
# $messaggio = "Hello World!!! \n";
while(1) {
 $paddr = accept(NUOVO,ASCOLTO);
 ($port, $iaddr) = sockaddr_in($paddr);
 print "Connection from: ", inet_ntoa($iaddr), " on port ", $port, "\n";
 $rin = "";
 vec($rin, fileno(STDIN), 1) = 1;
 vec($rin, fileno(NUOVO), 1) = 1;
 while(1) {
  select $pronto = $rin, undef, undef, undef;
  if (vec($pronto, fileno(STDIN), 1) == 1) {
   sysread(STDIN,$mesg,256);
   syswrite(NUOVO,$mesg,length($mesg));
  }
  if (vec($pronto, fileno(NUOVO), 1) == 1) {
   $controllo = sysread NUOVO, $netmesg, 256;
   if ($controllo == 0) { goto  EXIT }
   print "$netmesg\n";
   $netmesg = "";
 }
}
 EXIT: close NUOVO;
 print "Client closed connection \n";
}
close ASCOLTO
#######
Non escludo di aver sbagliato qualcosa con vec() o select perchč č la prima
volta che le uso. Comunque visto che questo codice non funzionava bene, l'ho
riadattato in questo modo:
#!/usr/bin/perl
use Socket;
$portname = "test";
$proto = getprotobyname('tcp') or die "getprotobyname error: $! \n";
$port = getservbyname($portname,'tcp') or die "getservbyport error: $! \n";
socket(ASCOLTO, PF_INET, SOCK_STREAM, $proto);
bind(ASCOLTO, sockaddr_in($port,INADDR_ANY)) or die "bind: $! \n";
listen(ASCOLTO,SOMAXCONN) or die "listen: $! \n";
# $messaggio = "Hello World!!! \n";
while(1) {
 $paddr = accept(NUOVO,ASCOLTO);
 ($port, $iaddr) = sockaddr_in($paddr);
 print "Connection from: ", inet_ntoa($iaddr), " on port ", $port, "\n";
 $rin = "";
 vec($rin, fileno(STDIN), 1) = 1;
 vec($rin, fileno(NUOVO), 1) = 1;
 while(1) {
  select $pronto = $rin, undef, undef, undef;
  while (vec($pronto, fileno(NUOVO), 1) == 1) {
   $controllo = sysread NUOVO, $netmesg, 256;
   if ($controllo == 0) { goto  EXIT }
   print "$netmesg\n";
   $netmesg = "";
   select $pronto = $rin, undef, undef, undef;
  while (vec($pronto, fileno(STDIN), 1) == 1) {
   sysread(STDIN,$mesg,256);
   syswrite(NUOVO,$mesg,length($mesg));
   $mesg = "";
   select $pronto = $rin, undef, undef, undef;
  }
 }
}
 EXIT: close NUOVO;
 print "Client closed connection \n";
}
close ASCOLTO
#######
In questo caso il programma funziona diversamente, ma ancora non come
desidero: A seconda che io metta prima il ciclo while del socket o
dell'input da tastiera, il programma riceve sempre e solo dal socket o dalla
tastiera.
Sono davvero a corto di idee, se qualcuno mi potesse dire cosa sbaglio mi
farebbe un grande favore.
Grazie.
--
bye
Erix    UIN: 72432400
http://erix.cjb.net

 accetti in qualsiasi momento sia input da STDIN che dal socket connesso,
non
[...]
Nessuno sa aiutarmi? :-(
Un newsgroup più specifico, magari in italiano, su cui possa chiedere c'è?


Un newsgroup più specifico, magari in italiano, su cui possa chiedere c'è?
Il gruppo non e' sbagliato, anche se magari potresti provare su qualche
gruppo dedicato a Linux o al C. Evidentemente, pero', non c'e' nessuno che
conosca l'argomento.
Io mi sono dato un'occhiata alla documentazione di select, e mi pare che
potrebbe anche funzionare; una cosa cosi', pero', non l'ho mai provata.
Un'altra strada che mi viene in mente e' ricorrere ad un fork, generando
piu' processi che ascoltano sui diversi input, e comunicano con un server.
Piu' complicato, ma, almeno per me, meno misterioso di select.
Tra l'altro, mi pare che un fork da qualche parte ci starebbe bene anche
col tuo metodo: se arriva qualcosa da una parte mentre stai servendo
l'altra che succede?
Ciao.

 l'altra che succede?
Ok, vada per la documentazione di fork :-)
Grazie


 l'altra che succede?
Mi sono letto la documentazione di fork (man fork, man perlfunc): ho capito
cosa fa ma non ho capito molto bene come si usa. Qualcuno mi fa un esempio?
Anche il più semplice possibile, giusto per capire come strutturare la
chiamata.
Grazie


 client risponda, e il programma si blocca fino a quel momento.
Mi pare, invece, che con la prima versione tu possa continuare a scrivere
su STDIN e tutto verrà copiato sul socket senza che il client debba
rispondere alcunchè. Perciò non capisco il tuo commento. Probabilmente mi
sfugge qualcosa.
La tua variante mi sembra che complichi le cose con due cicli while senza
aggiungere nulla di significativo. L'esecuzione, per altro, non entra nel
primo dei due while aggiuntivi finchè l'input non comincia ad arrivare
anche dal socket. Una volta entrata lì non ne esce più se non in caso di
chiusura di NUOVO; quindi il loop esterno non serve più a nulla.
Max

 sfugge qualcosa.
hai provato ad eseguirlo?


 
Certo. Una volta che una connessione è stata aperta, se scrivo qualcosa su
STDIN questo viene replicato sulla connessione; se scrivo qualcosa sulla
connessione questo viene replicato su STDOUT. Ora, se mi descrivi
chiaramente cosa accade sulla tua piattaforma, possiamo cercare di capire
quale potrebbe essere la ragione del diverso comportamento.
Max

 quale potrebbe essere la ragione del diverso comportamento.
e fin qui va bene, ma se dopo aver scritto su STDIN, scrivi di nuovo su
STDIN, funziona? se scrivo e ricevo alternativamente funziona, ma se voglio
scrivere due volte di seguito su STDIN non riesco, perchè dopo aver premuto
enter il programma si blocca finchè non riceve qualcosa dalla connessione.
sono stato chiaro?


 connessione. sono stato chiaro?
Chiarissimo. 
Dunque, ora ho provato lo script sotto Windows (prima l'avevo provato
sotto Linux), ed in effetti il comportamento è piuttosto anomalo.
Apparentemente, anche se scriviamo soltanto su STDIN l'escuzione entra
subito nel secondo blocco if, anche se in realtà non c'è nulla in attesa
sul socket. Quindi la successiva sysread blocca finchè non vien scritto
qualcosa sul socket. Al contrario, se scrivo sul socket la select non esce
proprio.
Per complicare ulteriormente la faccenda, la documentazione dice che sotto
Win32 select() funziona solo con i socket (v. perlport).
Ho riscritto tutto lo script usando IO::Socket::INET e IO::Select (che
sono in generale preferibili). Ebbene, con IO::Select sembra che non si
riesca a fare il polling su STDIN, mentre con il socket funziona.
Max

 riesca a fare il polling su STDIN, mentre con il socket funziona.
quindi x STDIN non c'č soluzione?
potresti postare la tua versione del prog, con IO::Socket e IO::Select ?
grazie dell'aiuto.


 ?
Eccola. 
 use IO::Select;
 use IO::Socket::INET;
 use strict;
 my $port = 1030;
 my $mesg;
 my $netmesg;
 my $rc;
 my $inet = IO::Socket::INET->new(
          Listen  => 1, LocalPort => $port,
          Reuse   => 1, Proto     => 'tcp' );
 while(1) 
 {
  my $sock = $inet->accept();
  warn "Connection from ", $sock->peerhost, "\n";
  my $selector = IO::Select->new();
  $selector->add($sock);
  $selector->add(\*STDIN);
  READ: 
  while(1) 
  {
   my @handles = $selector->can_read     ;
   last if( @handles == 0 );
   for my $s( @handles )
   {
           if( $s == \*STDIN )
           {
              sysread(STDIN, $mesg, 256 );
	      $sock->syswrite($mesg);
           }
	   elsif( $s == $sock ) 
	   {
                $rc = $sock->sysread( $netmesg, 256 );
                last READ if( $rc == 0 ); 
                print $netmesg;
                $netmesg = "";
         }
   }
  }
  print "client closed connection \n";
 }
Max