Habitualmente un programa utiliza la entrada-salida estandar para comunicarse con el usuario. Por ejemplo, el módulo Chatbot::Eliza establece un diálogo con el usuario imitando a un psiquiatra:
nereida:~/LGRID_Machine/lib/GRID$ perl -wde 0 main::(-e:1): 0 DB<1> use Chatbot::Eliza DB<2> $bot = Chatbot::Eliza->new DB<3> $bot->command_interface() Eliza: Is something troubling you? you: The future Eliza: I'm not sure I understand you fully. you: Nothing stands, everything changes Eliza: I'm not sure I understand you fully. you: Is very honest from you to say that Eliza: Oh, I to say that? you: bye! Eliza: Goodbye. It was nice talking to you.
La solución está en redirigir la entrada/salida del programa hacia el socket tal y como hacíamos en la sección 1.5.
milogin@beowulf:~/src/perl/NETWORKING$ cat -n eliza_server.pl 1 #!/usr/bin/perl 2 use strict; 3 use Chatbot::Eliza; 4 use IO::Socket; 5 use POSIX 'WNOHANG'; 6 7 my $PORT = shift || 1026; 8 9 my $quit = 0; 10 11 # signal handler for child die events 12 $SIG{CHLD} = sub { while ( waitpid(-1,WNOHANG)>0 ) { } }; 13 14 # signal handler for interrupt key and TERM signal (15) 15 $SIG{INT} = sub { $quit++ }; 16 17 my $listen_socket = IO::Socket::INET->new(LocalPort => $PORT, 18 Listen => 20, 19 Proto => 'tcp', 20 Reuse => 1, 21 Timeout => 60*60, 22 ); 23 die "Can't create a listening socket: $@" unless $listen_socket; 24 warn "Server ready. Waiting for connections...\n"; 25 26 while (!$quit) { 27 28 next unless my $connection = $listen_socket->accept; 29 30 defined (my $child = fork()) or die "Can't fork: $!"; 31 if ($child == 0) { 32 $listen_socket->close; 33 interact($connection); 34 exit 0; 35 } 36 37 $connection->close; 38 } 39 40 sub interact { 41 my $sock = shift; 42 STDIN->fdopen($sock,"<") or die "Can't reopen STDIN: $!"; 43 STDOUT->fdopen($sock,">") or die "Can't reopen STDOUT: $!"; 44 STDERR->fdopen($sock,">") or die "Can't reopen STDERR: $!"; 45 $|=1; 46 my $bot = Chatbot::Eliza->new; 47 $bot->command_interface(); 48 }El método
command_interface
del objeto $bot
(línea 47) utiliza
los ficheros STDIN
y STDOUT
. Pero ahora, al reconvertir el
método a un servicio queremos que use el socket $sock
.
El método fdopen
en IO::Handle funciona de manera parecida a open
pero su primer parámetro es un descriptor de fichero. El método cierra el
fichero y lo reabre utilizando el manejador $sock
proveído como
argumento. Después de las tres llamadas en las líneas 42-44 escribir en
STDOUT
y STDERR
es enviar los datos vía el socket.
Leer desde STDIN
es leer desde el socket.
Funciona de manera parecida a
las aperturas con los descriptores ">&"
(escritura) y "<&"
(lectura).
casiano@beowulf:~/src/perl/NETWORKING$ ./eliza_server.pl Server ready. Waiting for connections... .............................. milogin@beowulf:~/src/perl/netserver$ pkill eliza_server |
milogin@micasa:~/LPOP$ telnet beowulf 1026 Trying 123.123.123.123... Connected to beowulf. Escape character is '^]'. Eliza: Please tell me what's been bothering you. you: Hey! it works! Eliza: Tell me more about that. you: You are server. You work Eliza: Do you sometimes wish you were server? you: No Eliza: Are you saying no just to be negative? you: quit Eliza: Life is tough. Hang in there! Connection closed by foreign host. |
Hay otro problema con la conversión de Eliza en un servicio.
Eliza esta pensado para trabajar con un sólo usuario y por ello
sólo termina cuando la entrada contiene palabras como quit
o bye
. En otro caso se queda en un bucle a la espera
por mas entrada del usuario. Incluso si se genera un final de
fichero pulsando CTRL-D
no termina.
Para lograr que el programa termine cuando se alcanza el final de fichero
Lincoln Stein propone sustituir el método privado _test_quit
del objeto por este:
sub Chatbot::Eliza::_testquit { my ($self,$string) = @_; return 1 unless defined $string; # test for EOF foreach (@{$self->{quit}}) { return 1 if $string =~ /\b$_\b/i }; }
Casiano Rodríguez León