Página 1 de 1

Dos sockets con IO::Select

NotaPublicado: 2009-05-28 12:23 @557
por perleando_apenas
Hola a todos nuevamente.

Les cuento que quiero hacer una pequeña prueba para un módulo de mayor tamaño. Mi objetivo es hacer un único programita en Perl que abra dos [iyz]sockets[/iyz] distintos en un mismo [iyz]host[/iyz] y quede escuchando en ambos... y bueno, poder discriminar por cual de los dos entró un mensaje.

El código que estoy probando lo tomé prestado de otra respuesta en este mismo foro y es:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -w

use IO::Socket;
use IO::Select;
use strict;

my $srv_sock = IO::Socket::INET->new (
                                 Hostname => "192.168.1.2",
                                 LocalPort => "9999",
                                 Proto => 'udp'
                                 ) or die "socket: $@";

my $rqt_sock = IO::Socket::INET->new (
                                 Hostname => "192.168.1.2",
                                 LocalPort => "9998",
                                 Proto => 'udp'
                                 ) or die "socket: $@";

my $selector = new IO::Select ( $srv_sock, $rqt_sock );

while (my @ready = $selector->can_read ) {
  foreach ( @ready ) {
    ( $_ == $srv_sock ) ? print "server\n" : print "request\n";
  }
}
Coloreado en 0.006 segundos, usando GeSHi 1.0.8.4


Cuando ejecuto este código se queda esperando. Luego, desde otra ventana mando mensaje al puerto 9999 o 9998, con un código en Perl como este:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$sock = IO::Socket::INET->new(Proto     => 'udp',
                              PeerPort  => $portno,
                              PeerAddr  => "192.168.1.2")
    or die "Creating socket: $!\n";

$sock->send($msg) or die "send: $!";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


El problema es que si mando al 9999 el [iyz]server[/iyz] se va en un [iyz]loop[/iyz] imprimiendo repetidamente:

Código: Seleccionar todo
server
server
server
...


Y por supuesto... si mando al 9998, el [iyz]loop[/iyz] se va infinitamente con:

Código: Seleccionar todo
request
request
request
...


¿Por qué pasa esto?

La verdad es que estos son mis primeros intentos con IO::Select... así que agradeceré mucho la ayuda que me puedan dar.

Salu2!

NotaPublicado: 2009-05-28 18:59 @832
por explorer
Entra en un bucle sin fin porque te está indicando que hay algo por leer, pero que no estás leyendo.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

use IO::Socket;
use IO::Select;

my $srv_sock = IO::Socket::INET->new (
     Hostname  => 'localhost',
     LocalPort => '9999',
     Proto     => 'udp',
 ) or die "socket: $@";

my $rqt_sock = IO::Socket::INET->new (
     Hostname  => 'localhost',
     LocalPort => '9998',
     Proto     => 'udp',
 ) or die "socket: $@";

my $selector = new IO::Select ( $srv_sock, $rqt_sock );

$|=1; # no caché

while (my @ready = $selector->can_read ) {
    foreach my $fh ( @ready ) {

        print "Abriendo...\n";
        my $destino = ( $fh == $srv_sock ) ? 'Server' : 'Request';
        if (defined (my $buf = <$fh>)) {
            print "$destino: $buf";
        }
        print "Cerrando...\n";

    }
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Algo no anda bien ...

NotaPublicado: 2009-05-29 09:10 @423
por perleando_apenas
Gracias Explorer por tu respuesta.

Probé el código que enviaste. Lo ejecuto y se queda esperando. Cuando envío algo por el puerto 9999 o 9998 imprime "Abriendo..." y se queda pegado ... no hace nada más. ¿No debería imprimir Server o Request según el puerto por donde entre un mensaje?

Por otro lado, me gustaría entender bien qué significa esta asignación:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$buf = <$fh>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Muchas gracias.

NotaPublicado: 2009-05-29 13:40 @611
por explorer
El ejemplo que te he puesto es muy simple... se queda esperando a que haya algo qué leer y, a continuación, lee una línea (una cadena de bytes terminadas en un retorno de carro), que es justo la línea de código que muestras.

Si se queda esperando es porque no le estás enviando ese retorno de carro.

Si estás intercambiando datos binarios de un formato conocido, pero que no son líneas de texto, entonces deberás usar las funciones de lectura como read() para indicar cuánto debe de leer.

¡Ahora sí que sí!

NotaPublicado: 2009-05-29 16:28 @728
por perleando_apenas
Efectivamente, explorer, tienes razón.
Agregué un "\n" al "mensaje" que envío al socket y lo imprime correctamente. También se da cuenta sin problemas desde qué puerto está recibiendo la entrada.
¡Te agradezco mucho tu ayuda!

Funciones de lectura?

NotaPublicado: 2009-06-01 18:20 @805
por perleando_apenas
Hola nuevamente.

explorer escribiste:....
Si estás intercambiando datos binarios de un formato conocido, pero que no son líneas de texto, entonces deberás usar las funciones de lectura como read() para indicar cuánto debe de leer.


Respecto de los datos que recibirá el puerto, se trata de paquetes snmp del tipo "GET" que deberán ser parseados y respondidos por mi programita. Estoy usando un cliente para generar gets desde otro equipo hasta un puerto X en el host donde estoy desarrollando y por supuesto ya no puedo usar aquello de agregar un "\n" para que el paquete sea correctamente detectado.

Así que tengo que ver el tema de las funciones de lectura que mencionaste. El problema es que no sé a cuáles estás haciendo referencia. Imagino que te refieres a algo como lo que aparece aquí http://www.perlfect.com/articles/perlfile.shtml, pero no sé cómo incluirlo en el ciclo while.

Gracias por tu ayuda.

Muchas gracias.

Ya más en limpio ...

NotaPublicado: 2009-06-02 12:53 @578
por perleando_apenas
Hola.
Creo haberme aclarado un poco más respecto al tema de la lectura de los sockets definidos.

Mi código va así más o menos:


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my $alarm_sock = IO::Socket::INET->new (
                                 Hostname => $SRV_HOST,
                                 LocalPort => $SRV_PORT,
                                 Proto => 'udp'
                                 ) or die "socket: $@";
print "Awaiting UDP messages (alarms) on port $SRV_PORT\n";

my $get_sock = IO::Socket::INET->new (
                                 Hostname => $SRV_HOST,
                                 LocalPort => $GET_PORT,
                                 Proto => 'udp'
                                 ) or die "socket: $@";
print "Awaiting UDP messages (gets) on port $GET_PORT\n";

my $selector = new IO::Select ( $alarm_sock, $get_sock );
$|=1; #no flush

while (my @ready = $selector->can_read ) {
  foreach my $fh ( @ready ) {
    $fh->recv(my $buf, $MAXLEN);

    if ( $buf eq "stop\n" ) {
      print "Ending UDP server ...\n";
      close($alarm_sock);
      close($get_sock);
      exit 0;
    }

    if ( $fh == $alarm_sock ) {
      alarm_received ( $buf );
    }
    else {
      get_received ( $buf );
    }
  }
}
close($alarm_sock);
close($get_sock);
 
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Como expliqué antes, mi idea es tener un objeto "IO::Select" donde hay definidos 2 sockets. Por un socket entrarán alarmas y por otro entrarán solicitudes de estado SNMP del tipo "get" que deberé parsear (esa es una etapa en la que aún no entro).

Según esta descripción me sería de mucha ayuda que pudieran hacer observaciones de mi código... que está recibiendo correctamente... pero tal vez ustedes tienen ideas para hacerlo más eficiente (las funciones que estoy llamando no están aún completamente definidas... pero por ahora no es importante).

Saludos y gracias.

NotaPublicado: 2009-06-07 12:17 @554
por explorer
Aquí lo bonito sería usar POE...

...

NotaPublicado: 2009-06-09 14:14 @635
por perleando_apenas
AUCH!
Sería ideal probar algo nuevo, lamentablemente el tiempo no lo permite. Lo tendré en cuenta cuando retome una nueva versión del agente.
En todo caso, ¿por qué consideras recomendable usar POE?

NotaPublicado: 2009-06-09 14:27 @644
por explorer
Multitarea.