Newsgroup: it.comp.lang.perl
Titolo: webserver in perl?

Ragazzi mi sono spaventato!!!
Vi ricordate il codice "Server" che ho postato prima? (ve lo riposto)
[code]
#!/perl
#
# Server
use strict;
use warnings;
use IO::Socket::INET;
my $answer;
my $server = IO::Socket::INET->new(LocalPort => '81',
                                   Type      => SOCK_STREAM,
                                   Reuse     => 1,
                                   Listen    => 1 ) || die "$!\n";
while ( my $client = $server->accept() ) {
   print $client "The time is now: " . scalar(localtime(time())) . "\n";
   close $client;
 
}
close($server);                            
[/code]
ebbene l'ho eseguito,poi sono andato nel browser ed ho digitato: 
http://127.0.0.1:81
Il browser mi ha restituito la data corrente!
Vuol dire che in Perl potrei essere in grado di programmare un piccolo 
web server?
E a questo web server potrei fargli eseguire gli scripts .cgi?
ad esempio quando digito 
http://127.0.0.1:81/pages/page.html
 lo script 
mi restituisce: GET /pages/page.html HTTP1.1
Credo che intanto mi studiero un pò il protocollo HTTP1.1
Ciao!

 E a questo web server potrei fargli eseguire gli scripts .cgi?
Oddio, questo magari richiede un po' di codice in più ma si può fare.

 [/code]
perchè se invece scrivo:
[code]
#!/perl
#
# Server
use strict;
use warnings;
use IO::Socket::INET;
my @answer;
my $server = IO::Socket::INET->new(LocalPort => '81',
                                   Type      => SOCK_STREAM,
                                   Reuse     => 1,
                                   Listen    => 1 ) || die "$!\n";
while ( my $client = $server->accept() ) {
   @answer = <$client>;
   print @answer;
   print $client "The time is now: " . scalar(localtime(time())) . "\n";
   close $client;
 
}
close($server);                            
[/code]
mi da un caricamento infinito???
qual'è il miglior modo per leggere la richiesta da parte del browser?

 web server?
Studiati qualcuno di questi moduli. 
 Net::HTTPServer 
 HTTP::Server::Singlethreaded
 POE::Component::Server::HTTPServer 
 HTTP::Daemon
 
Max

 [/code]
OK. Mi arrendo! Non volero ripostare per la seconda volta questo codice
ma non saprei come spiegarmi altrimenti:
Esempio di echoserver che riceve connessioni sulla porta che hai usato
tu, la 6362 e risponde con uppercase di quanto riceve.
Al posto di uppercase metti quello che vuoi. per collaudarlo usa telnet.
------ 
echoserver.pl
 -------------
use Socket;
use strict; # e' un pragma per la sicurezza. vedi perldoc -U -m strict
            # mi costringe a dichiarare le variabili com my.
my $proto = getprotobyname('tcp');
my $port = 6362;
socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "err in syscall
socket()\n";
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or
  die "err in syscall setsockopt()\n";
bind(SOCK, sockaddr_in($port, INADDR_ANY)) or die "err in syscall
bind()\n";
listen(SOCK, SOMAXCONN) or die "err in syscall listen()\n";
$SIG{CHLD} = 'IGNORE'; # Riceve SIGCHLD ed evita popolamento di zombies
# ATTENZIONE! la seguente funzione non e' portabile! pathname Linux.
# copiata da perldoc -U perlipc
use POSIX 'setsid';
sub daemonize
{
  chdir '/'                    or die "err chdir()\n";
  open STDIN, '</dev/null'     or die "err open()\n";
  open STDOUT, '>/dev/null'    or die "err open()\n";
  defined(my $pid = fork())    or die "err fork()\n";
  if ($pid)  { exit(); }
  no strict;
  setsid()                     or die "err setsid()\n";
  use strict;
  open STDERR, '>&STDOUT'      or die "err open()\n";
}
print "Echo Server in esecuzione. Porta $port\n";
daemonize();
while (my $newsocket = accept(REMOTE,SOCK))
{
  if (fork()) { close REMOTE; } # se sono processo padre chiudo
                      # nuovo socket e mi rimetto in accept()
  else
    { # se sono processo figlio chiudo sochet in LISTEN e comunico
      # con client tramite nuovo socket.
      close SOCK;
      $SIG{PIPE} = 'IGNORE';
      my ($remoteport, $remoreaddr) = sockaddr_in($newsocket);
      while (my $str = <REMOTE>)
        { syswrite REMOTE, uc($str); }
      exit 0;
    }
}
--------- fine 
echoserver.pl
 ----------------
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

che differenza c'è tra:
[code]
while ( my $client = $server->accept() ) {
   $answer = <$client>;
   print $answer;
   print $client "The time is now: " . scalar(localtime(time())) . "\n";
   close $client;
 
}
[/code]
e:
[code]
while ( $server->listen() ) {
   $client = $server->accept();
   $answer = <$client>;
   print $answer;
   print $client "The time is now: " . scalar(localtime(time())) . "\n";
   close $client;
 
}
[/code]

 [/code]
La prima versione e' un po' piu' ortodossa (ma solo un poco).
entrambe funzionano perche e' la accept ad essere bloccante comunque.
Comunque chiamare listen() dopo bind() potrebbe essere considerato
errore in alcune implementazioni di interfaccia socket con lo stack
di protocolli. Che io sappia listen si chiama una volta sola,
dopo socket() e prima di bind() 
Informati su come usare fork() per fare un semplice server concorrente.
O se preferisci, usa un thread per ogni connessione.
<Pubblicita'>
R.Stevens R.Stevens  R.Stevens  R.Stevens  R.Stevens 
</Pubblicita'>
Ciao.
Roberto


il tuo codice l'ho trasformato cosi':
[code]
#!/perl
use Socket;
use strict;
my $proto = getprotobyname('tcp');
my $port = 6362;
socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "err in syscall 
socket()\n";
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) || die "err in 
syscall setsockopt()\n";
bind(SOCK, sockaddr_in($port, INADDR_ANY)) || die "err in syscall 
bind()\n";
listen(SOCK, SOMAXCONN) || die "err in syscall listen()\n";
while (my $newsocket = accept(REMOTE,SOCK))
{
 
   #close SOCK;
 
   my ($remoteport, $remoreaddr) = sockaddr_in($newsocket);
 
   while (my $str = <REMOTE>) { syswrite REMOTE, uc($str); }
}
[/code]
 e devo dire che va benone...il mio server deve rispondere solo ad un 
client...niente concorrenza...
tu come lo riscriveresti usando IO::SOCKET::INET? 
perchè fai close SOCK dentro al ciclo while?
Grazie per il tempo...ciao!

Io l'ho riscritto cosi':
#!/perl
#
# Server...
use strict;
use warnings;
use IO::Socket::INET;
my ($socket,$client,$str);
$socket = IO::Socket::INET->new(LocalPort => '6362',
                                 Proto => 'tcp',
                                  Type      => SOCK_STREAM,
                                   Reuse     => 1,
                                    Listen    => 1) || die "$!\n";
$socket->listen();
while ( $client = $socket->accept() )
{
    while ( $str = <$client> ) { syswrite $client, uc($str) . "\r\n"; }
}
__END__;
Non so cosa sia, ma credi che dovrei aggiungere: $client->autoflush(1); 
??
Credi che poi avrei dovuto aggiungere: close $client; ??
Grazie ancora!

 Grazie per il tempo...ciao!
SIGH! Mi hai fatto fuori proprio la parte di codice che volevo enfatizzare.
Non saprei riscriverlo con IO::SOCKET::INET perche' ho sempre fatto altre
scelte stilistiche e non ho esercizio  in OOP, pero' chredo che come lo
hai fatto tu andrebbe bene. Comunque copiando gli esempi in
perldoc perlipc
si ottengono del server scolasticamente corretti.
Il tuo codice del primo post funziona, l' ho provato su due macchine in lan
e il client riceve la data/ora dal server. Tu hai ora detto che non importa
la limitazione ad un solo client per volta e questo rende il tuo codice
"perfetto" secondo i canoni progettuali. Io mi sono permesso di dare dei
consigli generali per lo sviluppo di semplici servers connection-oriented
che sono sempre utili in molte altre occasioni.
Avrai visto che c' era una call a fork(). questa funzione (syscall) e'
l' unico modo in UNIX LINUX e simili per generare un nuovo processo.
quindi fork() ritorna 2 volte: una nel processo padre e una nel figlio.
per distinguersi i due processi usano il valore di ritorno di fork()
che e' 0 per il figlio e pid nuovo processo per il padre. con 
l' if() il padre si trovera' ad eseguire il then {} e il figlio l' else {}
quindi il padre essendo solo interessato ad accetare connessioni e generare
processi che le gestiranno chiudera' la nuova connessione e si riportera'
in accept() (che e' bloccante fino alla prossima) mentre i figlio che non
e'
interessato a connessioni entranti, ma solo ad offrire il servizio tramite
la nuova connessione, chiudera' la sua copia di descrittore di sockek
originale. (scusa se sono cosi' prolisso)
Il codice cosi' ridotto e' della stessa efficacia del tuo codice originale.
Un solo processo che si deve liberare le mani della connessione portando
a termine il servizio offerto prima di accettarne un' altra. (e se il 
servizio deve produrre molto output o fare complesse elaborazioni?)
(pero' forse sto' dando per scontato che il perl che usi sia un Unix.
c' e' un wrapper per avere fork() sotto win??)
Ciao
Roberto
