• Publicidad

Por favor, una ayudita con IO::Socket

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Por favor, una ayudita con IO::Socket

Notapor jc.morataya » 2009-06-12 20:26 @893

Muy buenas a todos,

Tengo una consulta básica sobre IO::Socket, mandé por equivocación un mensaje hace poco sobre esta consulta por error al Foro Experto, por lo cual pido disculpas ya que tuve que haberlo publicado aquí en Básico...

He estado intentando un script que sea capaz de enviar mensajes a uno o varios clientes previamente conectados a un servidor; he tomado prestado de este mismo foro los códigos con los cuales estoy probando y van así:

Servidor:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use IO::Socket::INET;

my $socket = new IO::Socket::INET(Proto => "udp",
                               LocalAddr => "localhost",
                               LocalPort => 3000,
                               PeerAddr => "localhost",
                               PeerPort => 3001,
                               Timeout => 10)
        || die "failed";
       
while (1) {
    my $msg = <$socket>;
        while (<STDIN>) {
                my $serverchat = <STDIN>;
                chomp $msg;
                #print "Mensaje Recibido...\n";
                print $socket "$serverchat\n";
   }
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Cliente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use IO::Socket::INET;
my $socket = new IO::Socket::INET(Proto => "udp",
                               LocalAddr => "localhost",
                               LocalPort => 3001,
                               PeerAddr => "localhost",
                               PeerPort => 3000,
                               Timeout => 10)
        || die "failed";
       
print $socket "1\n";
while (1) {
    my $msg = <$socket>;
    chomp $msg;
    print "$msg\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4



Lo que está pasando es que el mensaje sí pasa al/a los clientes pero con un "ENTER en blanco", es decir, no pasan uno a uno según se vayan poniendo en <STDIN>... Agradeceré mucho si pueden darme una manita con esto y si en alguno de mis post anteriores incumplí con alguna regla de la netiqueta del foro, ¡pido mil disculpas!

Saludos a todos.
jc.morataya
Perlero nuevo
Perlero nuevo
 
Mensajes: 33
Registrado: 2008-02-28 09:30 @438

Publicidad

Notapor explorer » 2009-06-13 04:47 @241

Deberías activar el autoflush(), para que el envío de mensajes sea por cada línea, y no por bloques.

Ejemplo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor jc.morataya » 2009-06-13 11:50 @535

Hola Explorer,

Antes que nada quiero agradecer tu pronta respuesta. Fíjate que probé con el autoflush() pero no he conseguido lograr enviar línea a línea los mensajes. Con el autoflush() el código queda así:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
while (1) {
$socket->autoflush(1);
    my $msg = <$socket>;
        while (<STDIN>) {
                my $serverchat = <STDIN>;
                chomp $msg;
                #print "Mensaje Recibido...\n";
                print $socket "$serverchat\n";
   }
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Qué crees que pase? ¡Gracias de nuevo!
jc.morataya
Perlero nuevo
Perlero nuevo
 
Mensajes: 33
Registrado: 2008-02-28 09:30 @438

Notapor jc.morataya » 2009-06-13 17:35 @774

Hola nuevamente,

Muchas gracias por la valiosa ayuda,

He estado trabajando siempre en el tema de los sockets, ahora el servidor tiene una GUI desde la cual es posible enviar un mensaje que aparecerá como un cuadro de dialogo a todos los clientes conectados, dos cosas:

1) El servidor debe ser capaz de enviar mensajes a los clientes conectados en cualquier momento y

2) Debo proveerle al script del cliente una GUI similar a la del servidor,

La filosofía de todo se basa en que el cliente una vez conectado, tenga las herramientas necesarias en su GUI para hacer unas tareas determinadas, las cuales dependerán de los mensajes (cuadros de dialogo) que reciba desde el servidor en cualquier momento,

¿Cómo puedo hacer esto?

Acá está el código del servidor:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use IO::Socket::INET;
use Win32::GUI();

my $socket = new IO::Socket::INET(Proto => "udp",
                               LocalAddr => "localhost",
                               LocalPort => 3000,
                               PeerAddr => "localhost",
                               PeerPort => 3001,
                               Timeout => 10)
        || die "failed";
       

       
       
my $W1 = Win32::GUI::Window->new(
        -name  => "W1",
        -title => "Mensajes para los Clientes",
        -pos   => [ 150, 150 ],
        -size  => [ 300, 200 ],
);

$W1->AddButton(
        -name => "Button1",
        -text => "Cerrar",
        -pos  => [ 0, 0 ],
        -width    => 60,
    -height   => 30,
);

$W1->AddButton(
        -name => "Button2",
        -text => "Enviar",
        -pos  => [ 60, 0 ],
        -width    => 60,
    -height   => 30,
);

my $mensaje = $W1->AddTextfield(
                    -name => 'mensaje_enviar',
                    -pos  => [ 50, 115 ],
                    -width => 225,
                    -height => 20,
                    -prompt => 'Mensaje:',
                    -text => 'Mensaje a Enviar',
);

$W1->Show();
Win32::GUI::Dialog();


sub W1_Terminate {
        $W1->Hide();
        return -1;
}

sub Button1_Click {
        $W1->Hide();
        return -1;
}

sub Button2_Click {
    $textchat = $W1->mensaje_enviar->Text;
        $msg = <$socket>;
        print $socket "$textchat\n";
    $W1->mensaje_enviar->Change( -text => '');
    $W1->mensaje_enviar->Update();    
        return 0;
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4



Y acá esta el código del Cliente:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use IO::Socket::INET;
use Win32::GUI();
my $socket = new IO::Socket::INET(Proto => "udp",
                               LocalAddr => "localhost",
                               LocalPort => 3001,
                               PeerAddr => "localhost",
                               PeerPort => 3000,
                               Timeout => 10)
        || die "failed";

# aquí (o en otro lado) debería ir el código para la GUI
       
print $socket "1\n";
my $msg = <$socket>;
chomp $msg;
while($msg) {
        $message = Win32::MsgBox("$msg", 0 | MB_ICONEXCLAMATION, "Aviso");
        last;
}


close $socket;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4



Gracias nuevamente, saludos cordiales,
jc.morataya
Perlero nuevo
Perlero nuevo
 
Mensajes: 33
Registrado: 2008-02-28 09:30 @438

Notapor creating021 » 2009-06-13 18:38 @818

Por favor, pon tu código en los tags adecuados... de esta forma:
Código: Seleccionar todo
[syntax="perl"]Aquí pones el código[/syntax]


Trabajaré con el servido que pones en el primer post, para explicar.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use IO::Socket::INET;

my $socket = new IO::Socket::INET(
    Proto => "udp",
    LoacalPort => 3001,
    Timeout => 10,
    Listen => SOMAXCONN,
    Reuse => 1
) or die "failed: $!\n";
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Hasta aquí, hemos creado el objeto a IO::Socket::INET, con las siguientes indicaciones:
  • El puerto a escuchar es el 3001 (eso es LocalPort => 3001).
  • Tiene un máximo tiempo de espera de 10 segundos (Timeout => 10).
  • El número de usuarios que se pueden conectar al servidor está limitado por el sistema operativo (Listen => SOMAXCONN).
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
# Mientras que tengamos 1 cliente con el cual trabajar...
while ( my $cliente = $socket->accept() ) {
    # Cada vez que imprimimos, limpiamos el buffer.
    $socket->autoflush(1);
    if ( <$socket> ) { # ¡Tienes un mensaje!
        print "Mensaje de cliente: $_\n";
    }
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

El asunto es que tienes varios errores de diseño... al menos en mi opinión un servidor no debería ser un cliente.

Además, el método que usas no permite que los usuarios escriban simultáneamente.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor jc.morataya » 2009-06-14 11:55 @538

Hola Creating021,

Muchas gracias por tu respuesta, tu ejemplo me ha hecho ver mejor las cosas, he estado tratando infructuosamente establecer comunicación bidireccional entre un servidor y uno o varios clientes... sin éxito. He conseguido que los clientes envíen mensajes al servidor, que el servidor envié mensajes a los clientes pero no ambos a la vez...

¿Podrías extender tu ejemplo del post anterior para ejemplificar este tipo de comunicación bidireccional?

Gracias por adelantado,

Salud!
jc.morataya
Perlero nuevo
Perlero nuevo
 
Mensajes: 33
Registrado: 2008-02-28 09:30 @438

Notapor creating021 » 2009-06-14 21:09 @923

Hay muchas formas de hacer eso... desde usar fork hasta IO::Multiplex (que es algo complicado) pero yo te recomiendo usar un servidor pre-forking.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use strict;
use IO::Socket;

# Lo mismo de ahora.
my $socket = new IO::Socket::INET(
    Proto => "udp",
    LoacalPort => 3001,
    Timeout => 10,
    Listen => SOMAXCONN,
    Reuse => 1
) or die "failed: $!\n";

while ( my $cliente = $socket->accept() ) {
    my $pid = fork;
    if ( not $pid ) { # El fork se ha hecho bien.
        $socket->autoflush(1);
        # Hacer algo con el cliente.
    }
    else { die "Error con fork: $!\n"; }
}
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


De esa forma, varios clientes se pueden conectar al servidor (el programa "se duplica" al hacer el fork y así no se estanca con los clientes) pero tienes que buscar una forma de comunicar un cliente con otro, yo creo que se podría hacer por medio de un hash que contenga el algo que identifique a un cliente y una referencia de $socket, algo así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use IO::Socket;
my %clientes;
#...
use IO::Socket;

# Lo mismo de ahora.
my $socket = new IO::Socket::INET(
    Proto => "udp",
    LoacalPort => 3001,
    Timeout => 10,
    Listen => SOMAXCONN,
    Reuse => 1
) or die "failed: $!\n";

while ( my $cliente = $socket->accept() ) {
    my $pid = fork;
    if ( not $pid ) { # El fork se ha hecho bien.
        $socket->autoflush(1);
        my $id = <$socket>; # El cliente debe de pasar 1 nombre.
        $clientes{$id} = \$socket; # Guardamos una ref. del socket
    }
    else { die "Error con fork: $!\n"; }
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Lo que queda es hacer un protocolo, con el cual el servidor sepa con quien quiere "chatear"... digamos que el código es algo como esto:
Código: Seleccionar todo
\chat creating021

Entonces el servidor debe de leer el código, tomar el nombre y buscarlo en %clientes y hacer todo lo que necesite... por ejemplo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#$nombre es el nombre enviado por el cliente
if ( defined $clientes{$nombre} ) {
    print ${ $clientes{$nomebre} } "jc.morataya quiere chatear contigo\n";
}
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Espero que me entiendas.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor jc.morataya » 2009-06-15 12:06 @546

Hola Creating021,

Muchas gracias por el ejemplo, muy ilustrativo, pero creo que no me he dado a entender bien, lo que necesito es que el servidor pueda enviar mensajes a todos los clientes, que es lo que yo llamo comunicacion vertical descendente y los clientes puedan enviar mensajes al servidor que es lo que yo llamo comunicacion vertical ascendente, es decir no se necesita comunicacion entre clientes que es lo que yo llamo comunicación horizontal... me entiendes?

Creo que es posible hacerlo con el codigo que me enviaste, pero para entenderlo mejor, estoy queriendo hacer algo con la entrada estándar <STDIN> o sea, el servidor envia los mensajes a los clientes y estos a su vez pueden responderlos sería una comunicación bidireccional descendente-ascendente... creo que esto se puede hacer dentro del forking, puedes ejemplificar como hacerlo?

Muchas gracias!

Saludos,
jc.morataya
Perlero nuevo
Perlero nuevo
 
Mensajes: 33
Registrado: 2008-02-28 09:30 @438

Notapor explorer » 2009-06-15 12:58 @582

JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor explorer » 2009-06-15 14:13 @634

He encontrado un ejemplo de servidor chat que hace justo lo que quieres: varios clientes que se conectan y que a los que el servidor reenvía los mensajes. Incluso hay un control para los clientes que se desconectan.

Está en el capítulo 19 del libro "Network Programming with Perl", de Lincoln D. Stein, Addison Wesley, 2000/12/15, ISBN: 0-201-61571-1.

Son varios centenares de líneas. Y no puedo publicar su código, por estar protegido por los derechos del libro.

Mientras tanto, es imprescindible la lectura de los ejemplos del famoso artículo de la Linux Journal.
Última edición por explorer el 2009-06-15 14:19 @638, editado 1 vez en total
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Siguiente

Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 13 invitados

cron