Multiplexado Usando Procesos

Servidor

Si se especifica la opción Listen en el constructor de IO::Socket::INET el socket creado es un socket para 'escucha', esto es, se asume el lado del servidor.

bash-2.05a$ uname -a
Linux manis 2.4.20-37.7.legacysmp #1 SMP Mon Sep 27 21:38:15 EDT 2004 i686 unknown
bash-2.05a$ cat -n server.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use IO::Socket;
 4
 5  $SIG{CHLD} = sub { wait(); };
 6
 7  my $host = shift || 'localhost';
 8  my $port = shift || 1024;
 9
10  my $mains =IO::Socket::INET->new(
11    LocalHost => $host,
12    LocalPort => $port,
13    Listen => 10,
14    Proto => 'tcp',
15    Reuse => 1,
16  );
17  die "Can't create socket $!\n" unless $mains;
El método accept es únicamente válido cuando se llama a un socket en modo escucha (construido con la opción listen ). Obtiene la siguiente conexión de la cola y retorna la sesión conectada al socket. Esta sesión es un socket manejador que se utilizará para conectar con la máquina remota. El nuevo socket hereda todo los atributos de su padre y además está conectado.

Cuando se llama a accept en un contexto escalar retorna el socket conectado. Cuando se llama en un contexto de lista retorna una lista con dos elementos, el primero de los cuales es el socket conectado y el segundo es la dirección empaquetada del host remoto. También es posible obtener esta información haciendo uso del método peername .

18  my $news;
19  while (1) {
20  while ($news = $mains->accept()) {
21      my $pid = fork() and next;
22
23      print "**********************\n";
24      my $buf = '';
25      my $c = 1;
26      while (sysread($news, $buf, 1024)) {
27        print "'$buf'\n";
28        syswrite($news, "message $c from $host");
29        $c++;
30      }
31      exit(0);
32    }
33  }
34  close($mains);
Aquí hacemos un fork por cada cliente que se conecta. El proceso hijo queda a la espera de mensajes de la máquina remota hasta que esta cierra el canal.

Cliente

Cuando no se especifica Listen y el tipo del socket es SOCK_STREAM (que se deduce del protocolo usado) el método connect es invocado.

-bash-2.05b$ uname -a
Linux millo.etsii.ull.es 2.4.22-1.2188.nptlsmp #1 SMP Wed Apr 21 20:12:56 EDT 2004 i686 i686 i386 GNU/Linux
-bash-2.05b$ cat -n client.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use IO::Socket;
 4
 5  my $server = shift || 'manis';
 6  my $port = shift || 1234;
 7
 8  my $host = $ENV{HOSTNAME};
 9  my $sock = IO::Socket::INET->new(
10    PeerAddr => $server,
11    PeerPort => $port,
12    Proto => 'tcp',
13  );
14  die "Can't create socket $!\n" unless $sock;
15
16  for (1..10) {
17    syswrite($sock,"Message $_ from $host");
18    my $answer = '';
19    sysread($sock, $answer,1024);
20    print "'$answer'\n";
21  }
22  close($sock);

El Método shutdown

Una alternativa a close es el método shutdown :

   $return_val = $socket->shutdown($how)
Cerrará el socket incluso si existen copias en procesos hijos creados con fork. El argumento $how controla que mitad del socket bidireccional será cerrada.

Valor Descripción
0 Cierra el socket para lectura
1 Cierra el socket para escritura
2 Cierra el socket totalmente

Ejecución

Cliente en millo Servidor en manis
-bash-2.05b$ ./client.pl manis 1234
'message 1 from manis'
'message 2 from manis'
'message 3 from manis'
'message 4 from manis'
'message 5 from manis'
'message 6 from manis'
'message 7 from manis'
'message 8 from manis'
'message 9 from manis'
'message 10 from manis'
-bash-2.05b$
bash-2.05a$ ./server.pl manis 1234
**********************
'Message 1 from millo.etsii.ull.es'
'Message 2 from millo.etsii.ull.es'
'Message 3 from millo.etsii.ull.es'
'Message 4 from millo.etsii.ull.es'
'Message 5 from millo.etsii.ull.es'
'Message 6 from millo.etsii.ull.es'
'Message 7 from millo.etsii.ull.es'
'Message 8 from millo.etsii.ull.es'
'Message 9 from millo.etsii.ull.es'
'Message 10 from millo.etsii.ull.es'

Los métodos recv y send

También es posible usar los métodos recv y send para la comunicación:

bash-2.05a$ cat -n server.pl
  1  #!/usr/local/bin/perl -w
  2  use strict;
  3  use IO::Socket;
  4
  5  $SIG{CHLD} = sub { wait(); };
  6
  7  my $host = shift || 'localhost';
  8  my $port = shift || 1024;
  9
 10  my $mains =IO::Socket::INET->new(
 11    LocalHost => $host,
 12    LocalPort => $port,
 13    Listen => 10,
 14    Proto => 'tcp',
 15    Reuse => 1,
 16  );
 17  die "Can't create socket $!\n" unless $mains;
 18  my $news;
 19  while (1) {
 20  while ($news = $mains->accept()) {
 21      my $pid = fork() and next;
 22
 23      print "**********************\n";
 24      my $buf = '';
 25      my $c = 1;
!26      {
!27        $news->recv($buf, 1024);
!28        last unless $buf;
!29        print "'$buf'\n";
!30        $news->send("message $c from $host");
!31        $c++;
!32        redo;
!33      }
 34      exit(0);
 35    }
 36  }
 37  close($mains);

El método send tiene la sintáxis:

$bytes = $sock->send($data [,$flags, $destination])
Envía $data a la dirección especificada en $destination. En caso de éxito retorna el número de bytes enviados. Si falla retorna undef. El argumento flag es un or de estas opciones

Opción Descripcción
MSG_OOB Transmite un byte de datos urgentes
MSG_DONTROUTE Sáltese las tablas de rutas

El formato de recv es:

$address = $socket->recv($buffer, $length [,$flags])

Acepta $length bytes desde el socket y los coloca en $buffer. Los flags tienen el mismo significado que para send. En caso de éxito recv retorna la dirección empaquetada del socket del transmisor. En caso de error retorna undef y la variable $! contiene el código de error apropiado. Realmetne, cuando se usa TCP el método recv se comporta como sysread excepto que retorna la dirección del par. Realmente cuando se aprecia la diferencia es en la recepción de datagramas con el protocolo UDP.



Subsecciones
Casiano Rodríguez León
2012-02-29