Ir al contenido principal

Socket en perl 3/3




$sudo apt-get install xinetd
$ sudo nano echo
           
# default: off
            # description: An xinetd internal service which echo's characters back to
            # clients.
            # This is the tcp version.
            service echo
            {
            disable         = no
            type            = INTERNAL
            id              = echo-stream
            socket_type     = stream
            protocol        = tcp

            user            = root
            wait            = no
            }
# This is the udp version.
service echo
{
        disable         = yes
        type            = INTERNAL
        id              = echo-dgram
        socket_type     = dgram
        protocol        = udp
        user            = root
        wait            = yes
}

$ sudo netstat -putan|grep xine
           
tcp        0      0 0.0.0.0:7               0.0.0.0:*               ESCUCHAR    3583/xinetd

$ telnet localhost 7
           
Escribo string y este me lo repite
Abro nuevos telnets de la misma forma en terminales distintos y hace lo mismo. Escribo string y me lo contesta repitiéndolo.
Si este telnet lo hago al puerto 80, este esperará una petición asiciado a su protocolo, espera una petición web.
Telnet es mi vehículo para conectar con el servidor, por lo tanto se usa en la parte cliente.




$ sudo netstat -putan|grep xin


Telnet a la linea que das agrega \r\n. Si escribimos papa, al pulsar intro, nos devuelve papa\r\n.
1º - Telnet envía agregando siempre \r\n
2º - flush buffers cuando usamos funciones librerías para escribir en un stream(canal)
buferring es en la memoria del servidor, al escribir lo descarga, mientras que unbuffering es en memoria del PC

xinetd sirve para ahorrar recursos.
Si tengo 3, o 5 o 10 servicios esperando peticiones, significa que tengo 3, 5, o 10 procesos o demonios escuchando y esperando petición.
Con xinetd puedo tener la misma funcionalidad, pero con un unico servidor en memoria. Estará previamente configurado para esperar las peticiones de varios programas.
Cuando recibe una petición, por ejemplo de correo, este levanta el servidor de correo para atenderlo.

Echo es un claro ejemplo de como un servidor atiende varias peticiones iniciando un nuevo proceso. Pero nuestros códigos socket comienza un proceso, lo atiende y acaba. Cada proceso que comienza un servidor sería igual que el funcionamiento del socket.
Podríamos variar mi programa para que cuando el cliente lo llama, no se lo de este y acabe, sino que este cree un nuevo proceso, le atienda y acabe dicho proceso, pero mi programa sigue escuchando.
Un nuevo proceso, o un nuevo hilo.

Especificaciones de la ACTIVIDAD:
          #Escribir un servidor de echo con puerto 7777
          #prueba con telnet \r\n
          #Recordar flush
          #Acaba cuando el cliente envia "fin"
          #Acaba el server con el string 99


  
CLIENTE 1: echo telnet - use IO::Socket::UNIX;
#!/usr/bin/perl
use IO::Socket::UNIX;
#uso libreria socket UNIX = socket local
my $socket = IO::Socket::UNIX->new(
            Type => SOCK_STREAM,
            Peer => '/tmp/mysocket',
);
die "Can't create socket: $!" unless $socket;
#SE ESTABLECE LA CONEXIÓN CON EL SERVIDOR
#Si la conexión del socket da error, termina
$socket->autoflush;
while() {
#Mientras se escriba entradas por teclado,...
            print $socket $_;
            #Envia al servidor por SOCKET estas entradas
            if(my $line=<$socket>) {
           
#Si se guarda en variable lo que trae el canal desde el servidor, ...
                        print $line;
           
            #Imprime esta variable
                        my $salir="fin";
           
            #Guardo en variable string salir
                        if ($line =~ $salir) {
                       
#Si en la variable se guarda el string salir, ...
                                   close $socket;
                                  
#Cierra canal socket
                                   exit();
                                  
#Salir
                        }
            }         
}


SERVIDOR 1: echo telnet - use IO::Socket::UNIX;
#!/usr/bin/perl -w
use IO::Socket::UNIX;
#uso libreria socket UNIX = socket local
my $socket_path = '/tmp/mysocket';
unlink $socket_path if -e $socket_path;
my $socket = IO::Socket::UNIX->new(
            Local  => $socket_path,
            Type   => SOCK_STREAM,
            Listen => SOMAXCONN,
);
die "Can't create socket: $!" unless $socket;
#Si la conexión del socket da error, termina
while (1) {
#Se crea un bucle infinido. Significa que si queremos salir deberemos poner un exit().
            my $connection = $socket->accept;
            #ACEPTO LA CONEXIÓN
            # se crea el tunel de conexión.
            $connection->autoflush(1);
            while (my $line = <$connection>) {
            #Mientras se guarde en variable lo que manda el cliente por el SOCKET
            chomp($line);
            $nueva="$line" . "\n";
            print $connection $nueva;
           
#MANDA al CLIENTE por SOCKET la variable

            my $salir="fin";
           
#En variable guarda string fin
                        if ($line =~ $salir){
           
            #Si lo que manda cliente es salir, ...
                                   print "99\n";
                       
            #Imprime por pantalla 99
                                   exit();
                                  
#Sale del programa
                        }
            }         
}


CLIENTE 2: echo telnet - use Socket;
#!/usr/bin/perl
use Socket;
use IO::Handle;
socket(S,PF_INET,SOCK_STREAM,6);
$dest=sockaddr_in(7777,inet_aton('127.0.0.1')) ;
$paddr = connect(S, $dest);
S->autoflush;
while() {
            print S $_;
            if($line=) {
                        print $line;
            }
            else { last; }
}
close S;


SERVIDOR 2: echo telnet - use Socket;
#!/usr/bin/perl
use Socket;
socket (Server, PF_INET, SOCK_STREAM, 6) || die ("socket");
$pbind = sockaddr_in(7777,INADDR_ANY) || die ("sockaddr_in");
bind(Server,$pbind) || die ("bind");
listen(Server,SOMAXCONN) || die ("listen");
my $connection;
while ($connection = accept(Client, Server)) {
            ($port,$iaddr) = sockaddr_in($connection);
            $name = gethostbyaddr($iaddr,AF_INET);
            print "Atiendo petición desde puerto ", $port, " de máquina ",$name, " dirección ",inet_ntoa($iaddr),"\n";
            Server->autoflush;
            while (my $line=) {
                        chomp($line);
                        $nueva="$line" .
"\n";
                        print $nueva;
            }
            print "99\n";
}


Especificaciones de la ACTIVIDAD:
Un cliente que permita conversar con un servidor.
Pensar, aquí en el cliente hay poca variación, la adaptación viene en el SERVIDOR.
Pasaríamos de tener un servidor de echo a un servidor talk.
El usuario que opera el SERVIDOR lee lo que recibe el servidor y escribe una respuesta, por teclado claro. Esto es lo que no hace tu servidor de echo

CLIENTE 2: talk
#!/usr/bin/perl -W
use IO::Socket;
$host=;
$port=;
$cliente = IO::Socket::INET->new(
                        Proto       => 'tcp',
                        PeerAddr    => $host,
                        PeerPort    =>  $port,
                        Reuse       =>  1,
) || die print "Error, no se pudo conectar al servidor :(";
print "Se conecto correctamente :)\n";
while ($cliente) {
                        print "->";
                        $msg=;
                        print "Enviaste:$msg";
                        $cliente->send($msg);
                        $serv=<$cliente>;
                        print "Servidor:$serv";
}
close $cliente;

  
SERVIDOR 2: echo telnet - use Socket;
#!/usr/bin/perl  -W
use IO::Socket;
$port=;
$server=IO::Socket::INET->new(
                        PeerAddr => 127.0.0.1,
                        LocalPort => $port,
                        Listen => 1,
                        Reuse => 1,
)|| die "Error no se pudo crear el servidor";
print "Servidor Corriendo, esperando a cliente...\n";
while ($conexion = $server->accept() ) {
                        print "Conectado:",inet_ntoa((unpack_sockaddr_in(getpeername $conexion))[1])," \n";
            while ($conexion) {
                       $msg=<$conexion>;
                       print "Cliente: $msg";
                       print "->";$env=;
                       print "Enviaste:$env";
                       $conexion->send($env) || die "No se pudo enviar el mensaje.Por favor reinicie el servidor";
                       }
}
close($conexion);

Los servidores no suelen responder a la conexión del cliente, es decir, suele llamar el cliente y el servidor no le suele contestar “que quieres” ni nada similar.
Cuando se conecta el cliente al servidor, este crea algo similar a un Open de File. Abrir un socket es el equivalente a un Open.
Leer FILE:
#!/usr/bin/perl
open(FILE,"
@source = ;
foreach $linea (@source){
print $linea;
}
close(FILE);
Leer y Escribir en un FILE:
#!/usr/bin/perl
open(FILE,">archivo.txt");
while ($contador >= 5){
$contador++;
print FILE "Nuestra linea numero $contador\n";
}
close(FILE);
exit(1);

En socket: connect (SOCK, $paddr);
Guarda en SOCK , la conexión de dirección y puerto de esta conexión.
En file: open(FILE,"
Guarda en FILE, el contenido del fichero.


En el SERVIDOR:
Cuando el servidor hace la aceptación de la conexión (accept), este es bloqueante.
Un servidor siempre tiene un bucle, esperando la conexión.
while(1) es igual que while(accept(Z,S))
            while() Mientras fluye el accept, es un read
            Ahora el servidor bloquea el canal y espera leer del cliente
En el CLIENTE:
           
connect(S, …)
           
while()
                        print S
                        print S
                        print S

IPC mediante socket:
El bucle de interacción es el que puede variar según protocolo.
Server                                                Cliente
connect                                              while(accept){
while(){                                                         while(){

                                   IO
funcion librería                       call system                  en perl
funcion real                            read                 →        sysread
                                             write               →        syswrite
Las funciones se dividen en 2, funciones de sistema y funciones de librerías.


Qué pasa si el canal para la transferencia de datos creado, en el script Servidor, es cerrado con la función 'close', y el cliente va a ejecutar una lectura.
  connect                     <---------------->           accept
   escribe al canal        <---------------->           lee del canal
   lee del canal             <---------------->           escribe al canal
   escribe al canal        <---------------->           lee del canal
    ---- hasta aquí todo bien, ambos están coordinados ----
   lee del canal             <---------------->           close el canal.
Como vemos el cliente esta ejecutando una lectura (está bloqueado, esperando) mientras que el Servidor nunca escribirá, ya que lo que hace es cerrar el canal con lo cual 'no hay más canal'.
En el Servidor está todo bien, podrá ir a recibir una nueva conexión cliente, es decir un nuevo ciclo del bucle.
En el cliente "no están bien las cosas", el kernel reacciona ....
Si llamamos a close() no se podrá escribir o leer usando ese socket, y si alguien trata de hacerlo recibirá un mensaje de error(excepción).
El kernel genera la señal |broken(señal PIPE).
Cuando el servidor cierra el socket (close()), es END FILE.

  
Las señales a los programas los envía los kernels, por ejemplo con kill -9 proceso, en realidad se le está diciendo al kernel, envía la señal -9 al programa. Las señales siempre son para procesos.
La señal que se envía al kernel realiza una acción por defecto, por ejemplo, el CONTROL-C envía también una señal (kill -2 proceso).
            #!/usr/bin/perl
            print "Escribe: ";
            while(){
                        print $_;
                        print "Escribe: ";
            }

            #Se corta la entrada de datos(STDIN) con control-D

            #!/usr/bin/perl
            open(F, $ARGV[1]) or die "ERROR de apertura de fichero";
            while(){
                        print $_;
            }
            close F;

            #Se corta la entrada de datos

  
Especificaciones de la ACTIVIDAD:
Definir una función en el programa cliente que en ningún momento se va a llamar, pero se va a tener que agregar que equivale a signal (man 2 signal), que indique al kernel que debe ejecutar.
#!/usr/bin/env perl
$SIG{'INT'} = \&sigIntHandler;
sleep 100;
sub sigIntHandler {
            print "INT caught!\n";
            exit;
}

#kill -s SIGINT proceso = kill -2 proceso

#!/usr/bin/env perl
$SIG{'INT'} = \&sigIntHandler;
sleep 100;
print hola;
sleep 20;
print "luego del sleep\n\n";
#Este es tu programa
sub sigIntHandler {
            print "INT caught!\n";
            print "NO ME MATAS, JEJEJEJE t!\n";
}
#kill -s SIGINT proceso = kill -2 proceso
#Si se hace CONTROL-C, llama a la función  y muestra el print
#La señal -9, -17, -23 no puede ser atrapada



Socket UDP
El cliente crea la primera función, función socket
use Socket;
socket (S, ….)
…....
…. Canal creado, etc
….
Trabajo, interactúo con el servidor
…. acabo
Ahora no me sirve para conectar con un nuevo servidor.
No puede usarse para iniciar y conectar con un servidor distinto una vez terminado con el actual.
ESTO OCURRE EN TCP!!!
Hay que crear de nuevo el socket.
Mientras que en UDP si te sirve, ...



Como funcionan los servicios:
En los servicios, existe una conexión del cliente con el servidor, pero luego este servidor le pasa el “tubo” a un hijo para que pueda este interactuar con el cliente, mientras el servidor padre siguen en la conexión y a la escucha.
 while (1) {
     dfClient = accept ( dfServer, puntSockClient, &longClient );
                          
   /* acepta la conexion cliente */
     printf ("\n acepto la conexion \n");
     if ( fork() == 0 )  /* crea hijo y envia fichero */
       {
         escribeFichero ( dfClient );
         close ( dfClient );     
  /* cierra el socket */
         exit ( 0 );  
        }else {
       close ( dfClient );    
 /* cierra el descriptor cliente */
   }                                
   /* en el padre */

/*El padre le retorna al hijo 0 y el hijo le retorna al padre su PID*/


#!/usr/bin/perl
$pid = fork();

if( $pid == 0 ){
print "Esto es un proceso HIJO ",$$,"\n";
sleep 50;
print "Proceso HIJO acabado\n";
exit 0;
}
print "**Esto es un proceso PADRE $$ y HIJO y su PID es $pid **\n";
sleep 30;
print "Proceso PADRE acabado\n";
exit 0;


Comentarios

Entradas populares de este blog

Configuración y Uso de Pandora FMS 5.0 SP3

CONFIGURACIÓN DE LA RED Lo primero que se debe de hacer es conocer la red que necesitas monitorizar, las distintas redes que posees y configurar las interfaces del CentOS donde tienes instalado Pandora FMS para que este sistema pueda verlas. Si haces un ping a alguna de estas redes, este debería responderte. Un ejemplo de como configurar distintas redes en CentOS sería el siguiente, donde las X son las que corresponda a tú red: # cd /etc/sysconfig/network-scripts/ # cat ifcfg-eth0 DEVICE=eth0 HWADDR=00:0C:29:75:A5:F2 TYPE=Ethernet BOOTPROTO=static IPADDR=10.x.x.155 BROADCAST=10.x.x.254 GATEWAY=10.x.x.1 NETMASK=255.255.255.0 DNS1=10.x.x.x DOMAIN=dominio.local DEFROUTE=yes IPV4_FAILURE_FATAL=yes IPV6INIT=no UUID=920d0ead-e3ad-4c99-8c79-617812986fb4 ONBOOT=yes # cat ifcfg-eth0:0 DEVICE=eth0:0 HWADDR=00:0C:29:75:A5:F2 TYPE=Ethernet BOOTPROTO=static IPADDR=10.x.x.155 BROADCAST=10.x.x.254 GATEWAY=10.x.x....

Mensajería OpenFire: Instalación y Configuración

Exportamos una nueva OVA de DEBIAN. Configuramos su interfaz: # rm /etc/udev/rules.d/70-persistem-net.rules # reboot Preparamos la conexión remota: # apt-get install ssh #ifconfig -a Instalación paso a paso: $ ssh root@192.168.1.198 Necesitaremos en la instalación de  mensajería   OpenFire : Servidor LAMP •           Linux •           Apache •           MySQL •           PHP phpadmin y Java # apt-get install apache2 php5 mysql-server phpmyadmin # apt-get install openjdk-6-jdk Descarga y puesta en funcionamiento de OpenFire: # wget http://download.igniterealtime.org/openfire/openfire_3.9.1_all.deb # dpkg -i openfire_3.9.1_all.deb Para saber que está funcionando: # netstat...

pfsense: Proxy Squid3 instalación y configuración

Nuestra máquina de pfsense con 2 adaptadores , uno en adaptador puente y otra en solo anfitrión: Me conecto desde el interfaz anfitrión al pfsense desde el navegador: Nos movemos a: System / Package/Available Packages/instalamos squid3 Service / Proxy server Configuramos en la pestaña de General las siguientes opciones: LAN Proxy Port: 3128 Allow users on interface(picado) Transparent Proxy(picado) Enabled logging(picado) Log Store Directory: /var/squid/logs Administrator email: email administrador Language: es SAVE Nos aseguramos que squid está corriendo: Status / Service Con esta configuración debemos tener internet en nuestro cliente Windows. RESTRICCIONES Y PERMISOS Podemos configurar las restricciones en la pestaña Service / Proxy server / (pestaña)ACLs Por ejemplo, restringimos el acceso a google.es: En Service / Proxy server / Local Cache o...

FireWall de PFsense: NAT

NAT Network address transletion - Traducciones de direcciones de red Se diferencia de la tabla filter, que con estas reglas se redirecciona. Si queremos que nuestro servidor web de servicio al exterior, tiene una dirección interna, pero si sale con esta IP, al salir se corta la IP. Que el router coja esta IP interna de este servidor Web y la cambie por la IP publica del route se llama NAT. Esto que hace el router automáticamente es lo que vamos a hacer ahora. SNAT: source NAT(origen) DNAT: destination NAT(destino) SNAT, cambiamos el origen. Windows manda el paquete y llega a pfsense, y este cambia el origen PAQUETE: origen: wXP (192.168.56.2) ↔ WAN (80.21.21.21) destino: google.es DNAT, cambiamos el destino Queremos dar servicio web, pero mi servidor esta en una red privada, en la 10.0.0.2. Al dar la IP de mi WAN, debo hacer que llegue a mi servidor. Cada vez que llegue un paquete a la Wan al puerto, en este caso, 80, l...

FireWall de PFsense: Reglas de Filtro 1/3

FILTER RULES (Reglas de Filtro) Para instalar el PFsense te recuerdo el enlace donde se explica en mi blog: http://mjesussuarez.blogspot.com.es/2013/12/instalacion-y-uso-de-pfsense.html A partir de este punto, entramos en PFsense a partir de la IP LAN: 192.168.56.2 Las 2 opciones mas interesantes de ver sobre cortafuegos en PFsense es NAT y Rules . Aunque usaremos también los Aliases . Dentro de interfaces tenemos LAN y WAN, pero también podemos asignar interfaces, como por ejemplo una zona demilitarizada(DMZ) para alojar en esta rama un Servidor Web . Para crear una interfaz nueva debemos ir a Interfaces / assign / VLANs . Pero que es una VLAN . Significa que físicamente están todas las redes unidas, pero Virtualmente se han formado LANs independientes y que no se ven unas a otras, o si se ven, pero están separadas virtualmente como si estuvieran separadas de forma física. Pero este tema lo trataremos mas adelante, ahora haremos...