SOCKETS
COMUNICACIÓN ENTRE PROCESOS DISTRIBUIDOS
Miguel Rueda Barranco
mrueda@lsi.us.es
Junio 1.996
 


1.- EL CONCEPTO DE RED. 

Por red entendemos un sistema de ordenadores interconectados, a través de los cuales se podrá compartir recursos e intercambiar información entre las diferentes máquinas. El concepto modo distribuido en contraposición a modo repartido con respecto a los recursos de una red va tomando cada vez más auge en las redes. En el modo repartido, los recursos deben estar localizados explícitamente. En el modo distribuido, el usuario no tiene por qué saber donde se localiza cada uno de los recursos: para ellos cada tipo de recurso es un único recurso virtual, que englobará a todos los recursos de ese tipo repartidos a través de sus distintas localizaciones. Nace así el concepto de Sistemas Abiertos introducido por la ISO ( International Standards Organization ) definiendo las diferentes interacciones entre los equipos que integran una red.

Como consecuencia de ello, ISO define el modelo OSI (Open Systems Interconnection), una arquitectura de red basada en la descomposición en siete niveles de abstracción de red, cada uno de los cuales se apoya en los servicios que lo ofrece el nivel inmediatamente inferior. Cada nivel tiene una función bien definida. El conjunto de reglas que rigen el diálogo ( virtual ) entre un nivel de un sistema y su homólogo en otro sistema se conoce como el protocolo de ese nivel. El modelo OSI publicó estándares para cada uno de estos niveles. Las máquinas UNIX se basan en estos niveles, que son: 

Sólo nos interesará conocer algo sobre los niveles 3 (de Red), nivel 4 (de Transporte) y los niveles 5, 6 y 7. 

2.- REDES ETHERNET. 

Las Redes Ethernet fueron desarrolladas por Xerox a principio de los sesenta, estandarizándose por otros fabricantes posteriormente. Físicamente la red la compone un cable coaxial pasivo, con una longitud máxima de 500 metros antes de tener que instalar un repetidor, siendo dos el número máximo de repetidores que pueden situarse entre dos puntos. La conexión de una máquina a la red se realiza por medio de un transceiver. La topología de este red difusora es en bus, con 10 Mbits/sg. de capacidad y protocolo CSMA/CD. Las direcciones a través de esta red son de 48 bits (6 grupos de 2 dígitos hexadec. hh:hh:hh:hh:hh:hh). 
Estas direcciones las administra el IEEE. 


Las redes Ethernet son LANs (Local Area Networks) que mediante interconexiones a través de bridges (puentes), routers (enrutadores) y gateways (pasarelas) forman lo que se conoce como WANs (large Wide Area Networks). La necesidad de intercambiar información entre todas estas LANs y WANs genera un esquema de direccionamiento y enrutado estándar para todas ellas: Internet agrupa al mayor número de redes actualmente (no todas son Ethernet) y todas ellas utilizan el protocolo de comunicación TCP/IP. 


3.- INTERNET. 

Internet se origina gracias a DARPA (Defense Advanced Research Projects Agency) y a su idea de crear una red lógica que esconda la heterogeneidad de las redes que se interconectan. Debido a este afán, se definen un modelo de interconexión basado en el protocolo internet TCP/IP, que describe su estructura de comunicaciones de red utilizando cinco niveles, a diferencia de lo marcado por el modelo de referencia OSI. 

3.1.- El modelo TCP/IP. 

Los cinco niveles del modelo TCP/IP son: 

         

 

3.2.- Arquitectura y direccionamiento. 

Con respecto a la arquitectura de Internet, la unión de las redes se basa en la existencia de gateways entre ellas. Pero lo más importante dentro de esta incompatibilidad de redes es otorgar a los hosts implicados una dirección lógica que se componga de: 

La dirección completa ocupa 32 bits, dándose en forma de 4 octetos: 
                    n1.n2.n3.n4 

Cada campo es un número decimal entre 0 y 255. 

El valor 127 en el primer campo se llama loopback y se refiere a una interfaz que permite al host enviarse paquetes a sí mismo. 

Existen varias clase de redes: 

Para simplificar el direccionamiento, se utilizan direcciones simbólocas del tipo

host.organización (dominios) 

El fichero /etc/hosts muestra correspondencia entre las direcciones IP y los nombres de los hosts definidos por defecto. 


4.- LOS SOCKETS. 

Los sockets no son más que puntos o mecanismos de comunicación entre procesos que permiten que un proceso hable (emita o reciba información) con otro proceso incluso estando estos procesos en distintas máquinas. Esta característica de interconectividad entre máquinas hace que el concepto de socket nos sirva de gran utilidad. Esta interfaz de comunicaciones es una de las distribuciones de Berkeley al sistema UNIX, implementándose las utilidades de interconectividad de este Sistema Operativo (rlogin, telnet, ftp, ...) usando sockets. 

Un socket es al sistema de comunicación entre ordenadores lo que un buzón o un teléfono es al sistema de comunicación entre personas: un punto de comunicación entre dos agentes (procesos o personas respectivamente) por el cual se puede emitir o recibir información. La forma de referenciar un socket por los procesos implicados es mediante un descriptor del mismo tipo que el utilizado para referenciar ficheros. Debido a esta característica, se podrá realizar redirecciones de los archivos de E/S estándar (descriptores 0,1 y 2) a los sockets y así combinar entre ellos aplicaciones de la red. Todo nuevo proceso creado heredará, por tanto, los descriptores de sockets de su padre.  

La comunicación entre procesos a través de sockets se basa en la filosofía CLIENTE-SERVIDOR: un proceso en esta comunicación actuará de proceso servidor creando un socket cuyo nombre conocerá el proceso cliente, el cual podrá "hablar" con el proceso servidor a través de la conexión con dicho socket nombrado. 

El proceso crea un socket sin nombre cuyo valor de vuelta es un descriptor sobre el que se leerá o escribirá, permitiéndose una comunicación bidireccional, característica propia de los sockets y que los diferencia de los pipes, o canales de comunicación unidireccional entre procesos de una misma máquina. El mecanismo de comunicación vía sockets tiene los siguientes pasos: 

  1. El proceso servidor crea un socket con nombre y espera la conexión.
  2. El proceso cliente crea un socket sin nombre.
  3. El proceso cliente realiza una petición de conexión al socket servidor.
  4. El cliente realiza la conexión a través de su socket mientras el proceso servidor mantiene el socket servidor original con nombre. 

Es muy común en este tipo de comunicación lanzar un proceso hijo, una vez realizada la conexión, que se ocupe del intercambio de información con el proceso cliente mientras el proceso padre servidor sigue aceptando conexiones. Para eliminar esta característica se cerrará el descriptor del socket servidor con nombre en cuanto realice una conexión con un proceso socket cliente. 

Todo socket viene definido por dos características fundamentales: 

Vamos a estudiar con más detalle estos dos aspectos: 

4.1.- Tipos de sockets. 

Define las propiedades de las comunicaciones en las que se ve envuelto un socket, esto es, el tipo de comunicación que se puede dar entre cliente y servidor. Estas pueden ser: 

Los tipos disponibles son los siguientes: 

4.2.- El dominio de un socket. 

Indica el formato de las direcciones que podrán tomar los sockets y los protocolos que soportarán dichos sockets.  

La estructura genérica es 

     struct sockaddr { 
               u__short     sa__family;         /* familia */ 
               char         sa__data[14];       /* dirección */ 
          }; 

Pueden ser: 

Estos dominios van a ser los utilizados en xshine. Pero existen otros como: 

4.3.- FILOSOFIA CLIENTE-SERVIDOR: el Servidor. 

Vamos a explicar el proceso de comunicación servidor-cliente en modo conectado, modo utilizado por las aplicaciones estándar de Internet (telnet, ftp). El servidor es el proceso que crea el socket no nombrado y acepta las conexiones a él. El orden de las llamadas al sistema para la realización de esta función es: 

  1. int socket (int dominio, int tipo, int protocolo)  
    Crea un socket sin nombre de un dominio, tipo y protocolo específico 
    dominio   : AF_INET, AF_UNIX 
    tipo      : SOCK__DGRAM, SOCK__STREAM 
    protocolo : 0 (protocolo por defecto) 
  2. int bind (int dfServer, struct sockaddr* direccServer, int longDirecc) 
    Nombra un socket: asocia el socket no nombrado de descriptor dfServer con la dirección del socket almacenado en direccServer. La dirección depende de si estamos en un dominio AF_UNIX o AF_INET. 
  3. int listen (int dfServer, int longCola) 
    Especifica el máximo número de peticiones de conexión pendientes. 
  4. int accept (int dfServer, struct sockaddr* direccCliente, int* longDireccCli) 
    Escucha al socket nombrado servidor dfServer y espera hasta que se reciba la petición de la conexión de un cliente. Al ocurrir esta incidencia, crea un socket sin nombre con las mismas características que el socket servidor original, lo conecta al socket cliente y devuelve 
    un descriptor de fichero que puede ser utilizado para la comunicación con el cliente. 

4.4.- El Cliente. 

Es el proceso encargado de crear un socket sin nombre y posteriormente enlazarlo con el socker servidor nombrado. O sea, es el proceso que demanda una conexión al servidor. La secuencia de llamadas al sistema es: 

  1. int socket (int dominio, int tipo, int protocolo)  
    Crea un socket sin nombre de un dominio, tipo y protocolo específico 
    dominio   : AF_INET, AF_UNIX 
    tipo      : SOCK__DGRAM, SOCK__STREAM 
    protocolo : 0 (protocolo por defecto) 
  2. int connect (int dfCliente, struct sockaddr* direccServer, int longDirecc) 
    Intenta conectar con un socket servidor cuya dirección se encuentra incluida en la estructura apuntada por direccServer. El descriptor dfCliente se utilizará para comunicar con el socket servidor. El tipo de estructura dependerá del dominio en que nos encontremos. 

Una vez establecida la comunicación, los descriptores de ficheros serán utilizados para almacenar la información a leer o escribir. 

SERVIDOR

CLIENTE

descrServer = socket ( dominio, SOCK_STREAM,PROTOCOLO)  descrClient = socket (dominio, SOCK_STREAM,PROTOCOLO) 
bind (descrServer, PuntSockServer,longServer) 
do { 
listen (descrServer, longCola) 
descrClient = accept (descrServer,PuntSockClient,longClient)          result = connect (descrClient, PuntSockServer,longserver) 
[  close (descrServer) ]            } while ( result == -1 )   
< DIALOGO >   < DIALOGO >  
close (descrClient)    close (descrClient)  

   
COMPARACION SOCKETS-PIPES COMO MECANISMOS DE COMUNICACION ENTRE PROCESOS 

SOCKETS PIPES
refenciado por descriptores  referenciado  por array de descriptores 
admite comunicación entre procesos de distintas máquinas  sólo admite comunicación entre procesos de la misma máquina 
comunicación bidireccional  comunicación unidireccional 
filosofía cliente-servidor  simple intercambio de información 

         
EJEMPLO DE COMUNICACION MEDIANTE SOCKETS TIPO UNIX (EN LA MISMA MAQUINA) 

/************************************************************/ 
/**************    servidor.c          **********************/ 
/************************************************************/ 
/*********   proceso servidor con sockets AF_UNIX  **********/ 
/************************************************************/ 


#include <stdio.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/socket.h> 

#include <sys/un.h>           /*  para sockets UNIX  */ 

#define PROTOCOLO_DEFECTO 0 

/****************************************************/ 
main() 

 int dfServer, dfClient, longServer, longClient; 

 struct sockaddr_un dirUNIXServer; 
 struct sockaddr_un dirUNIXClient; 

 struct sockaddr* puntSockServer; 
 struct sockaddr* puntSockClient; 
 signal ( SIGCHLD, SIG_IGN );    /*  para no crear zombies */ 

 puntSockServer = ( struct sockaddr* ) &dirUNIXServer; 
 longServer = sizeof ( dirUNIXServer ); 
 puntSockClient = ( struct sockaddr* ) &dirUNIXClient; 
 longClient = sizeof ( dirUNIXClient ); 

 dfServer = socket ( AF_UNIX, SOCK_STREAM, PROTOCOLO_DEFECTO ); 
                 /* se crea un socket UNIX, bidireccional */ 
 dirUNIXServer.sun_family = AF_UNIX;    /* tipo de dominio */ 
 strcpy ( dirUNIXServer.sun_path, "fichero" );   /* nombre */ 
 unlink ( "fichero" ); 
 bind ( dfServer, puntSockServer, longServer );   /* crea el fichero */ 
                                          /* o sea, nombra el socket */ 
 printf ("\n estoy a la espera \n"); 
 listen ( dfServer, 5 ); 
 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 */ 





/******** funcion escribeFichero( df ) ***************/ 
escribeFichero ( int df ) 

 static char* linea1 = "esta es la linea 1, "; 
 static char* linea2 = "y esta la linea 2. "; 

 write ( df, linea1, strlen (linea1) + 1 );  
  write ( df, linea2, strlen (linea2) + 1 ); 

  
/******************   fin de servidor.c   ***********************/ 



/************************************************************/ 
/**************                         cliente.c           **********************/ 
/************************************************************/ 
/*********                   proceso cliente con sockets AF_UNIX   **********/ 
/************************************************************/ 

#include <stdio.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/socket.h> 

#include <sys/un.h>           /*  para sockets UNIX  */ 

#define PROTOCOLO_DEFECTO 0 

/****************************************************/ 

main() 

 int dfClient, longServer, resultado; 
 struct sockaddr_un dirUNIXServer; 
 struct sockaddr* puntSockServer; 

 puntSockServer = ( struct sockaddr* ) &dirUNIXServer; 
 longServer = sizeof ( dirUNIXServer ); 

 dfClient = socket ( AF_UNIX, SOCK_STREAM, PROTOCOLO_DEFECTO ); 
                 /* se crea un socket UNIX, bidireccional */ 

 dirUNIXServer.sun_family = AF_UNIX;    /* tipo de dominio server */ 
 strcpy ( dirUNIXServer.sun_path, "fichero" );   /* nombre server */ 

 do   
  { 
   resultado = connect ( dfClient, puntSockServer, longServer ); 
   if ( resultado == -1 ) sleep (1);   /* reintento */ 
 } 
 while ( resultado == -1 ); 

 leeFichero ( dfClient );     /* lee el fichero */ 
 close ( dfClient );      /* cierra el socket */ 
 exit (0);     /* buen fin */ 






/*************     leeFichero ( df )  *****************/ 

leeFichero ( int df ) 

 char cad[200]; 
 while ( leeLinea ( df, cad ) )      /* lee hasta fin de la entrada */ 
   printf ("%s\n", cad );            /* e imprime lo leido */ 




/*************   leeLinea ( df, cad )  ******************/ 
leeLinea ( int df, char *cad ) 

 int n; 
 do 
  { 
    n = read ( df, cad, 1 );          /* lectura de un caracter */ 
  } 
 while ( n > 0 && *cad++ != NULL );   /* lee hasta NULL o fin entrada */ 

 return ( n > 0 );   /* devuelve falso si fin de entrada */ 





/************* fin de cliente.c ****************************/ 


EJEMPLO DE COMUNICACION CON SOCKETS INET 
(ENTRE DIFERENTES MAQUINAS) 

/*************************************************/ 
/****************   hora.c ***********************/ 
/*************************************************/ 
/*    visualiza el dia y la hora de un host      */ 
/*************************************************/ 

#include <stdio.h>  
#include <signal.h>  
#include <ctype.h>  
#include <sys/types.h>  
#include <sys/socket.h>  

#include <netinet/in.h>          /* socket INET */ 
#include <arpa/inet.h>  
#include <netdb.h>  


#define PUERTO_HORA     13 
#define PROTOCOLO_DEFECTO 0 

unsigned long promptForINETAddress (); 
unsigned long nameToAddr (); 
/***********************************/ 
main () 

int clientFd; 
int serverLen; 
int result; 



struct sockaddr_in serverINETAddress; 
struct sockaddr* serverSockAddrPtr; 
unsigned long inetAddress; 

serverSockAddrPtr = (struct sockaddr *) &serverINETAddress; 
serverLen = sizeof (serverINETAddress); 

while (1) 
 { 
  inetAddress = promptForINETAddress (); 
  if (inetAddress == 0) break; 

  bzero ((char *)&serverINETAddress , sizeof(serverINETAddress)); 
  serverINETAddress.sin_family = AF_INET; 
  serverINETAddress.sin_addr.s_addr = inetAddress; 
  serverINETAddress.sin_port = htons ( PUERTO_HORA ); 

  clientFd = socket ( AF_INET,  SOCK_STREAM,  PROTOCOLO_DEFECTO ); 

  do 
   { 
    result = connect( clientFd, serverSockAddrPtr, serverLen ); 
    if (result == -1) sleep(1); 
   } 
  while (result == -1); 

  readTime(clientFd); 
  close(clientFd); 
 } 
 exit(0);  
 } 



/******************  promptForINETAddress () **************************/ 
unsigned long promptForINETAddress () 

 char hostName [100]; 
 unsigned long inetAddress; 
  
  do 
  { 
   printf ("Nombre maquina (q = salir, s = maquina propia): "); 
   scanf("%s",hostName); 
   if ( strcmp (hostName,"q") == 0 ) return(0); 
   inetAddress = nameToAddr (hostName); 
   if (inetAddress == 0 ) printf ("\n Maquina no encontrada\n"); 
  } 
 while ( inetAddress == 0 ); 


/*******************  nameToAddr ( name ) *************************/ 
unsigned long nameToAddr (char *name) 

 char hostName[100]; 
 struct hostent* hostStruct; 
 struct in_addr* hostNode; 

 if (isdigit (name[0])) return (inet_addr (name)); 
 if (strcmp (name,"s") == 0) 
  { 
   gethostname (hostName,100); 
   printf("Nombre de la propia maquina es %s\n",hostName); 
  } 
 else 
  strcpy(hostName,name); 



 hostStruct = gethostbyname (hostName); 
 if (hostStruct == NULL) return (0);                         /** maquina no encontrada **/ 

 hostNode = (struct in_addr*) hostStruct->h_addr;   /* saca la dir. IP de la struct */ 
 printf("Direccion IP = %s\n", inet_ntoa (*hostNode)); 
 return (hostNode->s_addr);                                     /* devuelve la dir IP */ 


/******************** readTime ( fd ) **********************/ 

readTime (int fd) 

 char str[200]; 

 printf("La hora en el puerto destino es "); 

 while (readLine (fd,str)) 
  printf("%s\n",str); 


/************   readLine ( fd, str ) ******************************/ 
readLine (int fd,char* str) 

 int n; 
 do 
  { 
   n = read(fd,str,1); 
 } 
 while (n>0 && *str++ != '\n'); 
 return (n>0); 



CODIGO FUENTE DE LAS PRINCIPALES FUNCIONES 
DE COMUNICACION DE LA SHELL DE COMUNICACIONES 

/* 
*----------------------------------------------------------------- 

*   NOMBRE:      xshine.c 
*                Moisés Fernández Andrés & Miguel Rueda Barranco 
*               
 *   DESCRIPCION: Xwindow Shell for Internet network 

*   FECHA:       06.09.94 

*------------------------------------------------------------------ 
*/ 

/* 
*---------------------------------------------------------------- 
*   NOMBRE:      shell.h 
*                 
 *   DESCRIPCION: cabeceras para la shell de comunicaciones  
 *                de xshine 
*           
 *   FECHA:       05.09.94  
 * 
*--------------------------------------------------------------- 
*/ 

/* 
*------------------------------------------------- 
*      includes necesarios 
*------------------------------------------------- 
*/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <signal.h> 
#include <ctype.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/file.h>   
#include <sys/ioctl.h>  
#include <sys/socket.h> 
#include <sys/socketvar.h> 
#include <sys/un.h>  
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 

#ifdef RS6000         /* necesario para sistemas RS/6000 */ 
#include <malloc.h> 
#endif 


/* 
*------------------------------------------- 
*       definicion de constantes                     
 *------------------------------------------- 
*/ 


#define MAX_STRING          50 
#define MAX_TOKENS          100 
#define LONG_MAX_TOKEN      30 
#define MAX_SIMPLE          3 
#define MAX_PIPES           3 
#define NO_ENCONTRADO       -1 
#define REGULAR             -1 
#define PERMISO_DEFECTO     0660 
#define PROTOCOLO_DEFECTO   0 
#define LONG_COLA_DEFECTO   5 
#define SOCKET_DORMIR       1 
#define FALSE               0 
#define TRUE                1 


/* 
*----------------------------------------- 
*             macros                          
 *----------------------------------------- 
*/ 



#define obtienePuntNodo(a)  (a *) malloc(sizeof(a))      /* para la asignacion */ 
                                                        /* de memoria         */ 


/* 
*------------------------------------------ 
*            tipos enumerados                  
 *------------------------------------------ 
*/ 


enum descriptorEnum { STDIN, STDOUT, STDERR };  

enum pipeEnum { READ, WRITE }; 

enum IOEnum { NO_REDIREC, REDIREC_FICHERO, 
              REDIREC_SERVIDOR, REDIREC_CLIENTE, REDIREC_PANT }; 

enum socketEnum { CLIENTE, SERVIDOR }; 

enum tiposock{ SOCKET_DOS_VIAS, SOCKET_ENTRADA, SOCKET_SALIDA }; 


/* 
*---------------------------------------------- 
*                 estructuras                     
 *---------------------------------------------- 
*/ 


struct simple 
 { 
   char *token[MAX_TOKENS];            /* tokens del comando      */ 
   int contoken;                       /* numero de tokens        */ 
   int redirecSalida;                  /* tipo de IO_enum         */ 
   int redirecEntrada;                 /* tipo de IO_enum         */ 
   int adicion;                        /* VERDAD para modo añadir */ 
   char fichSalida[MAX_STRING];        /* nombre fichero salida   */ 
   char fichEntrada[MAX_STRING];       /* nombre fichero entrada  */ 
   char socketSalida[MAX_STRING];      /* nombre socket salida    */ 
   char socketEntrada[MAX_STRING];     /* nombre socket entrada   */ 
 }; 


struct pipeline 
 { 
   struct simple simple [MAX_SIMPLE];      /* comandos en pipe           */ 
   int contcom;                            /* numero de comandos simples */ 
 }; 

struct secuencia 
 { 
   struct pipeline pipeline[MAX_PIPES];    /* pipes en secuencia  */ 
   int contpipe;                           /* numero de pipes     */ 
   int background;                         /* en background o no  */ 

 }; 

/* 
*------------------------------------------------------------ 
*  
 *   PROTOTIPO:   void ejecutaSimple ( struct simple* ) 

*   DESCRIPCION: ejecuta un comando simple         
 * 
*-------------------------------------------------------------- 
*/ 


void ejecutaSimple ( struct simple *p ) 



       

      if (redireccion (p) == TRUE)  
      { 

     ejecutaPrimitiva (p);  

      }       
      

}  /* fin de ejecutaSimple */ 


/* 
*------------------------------------------------------------------ 
*  
 *   PROTOTIPO:   void ejecutaPrimitiva ( struct simple* ) 

*   DESCRIPCION: ejecuta una primitiva simple a través del comando        
 *                "execvp" 

*------------------------------------------------------------------ 
*/ 

void ejecutaPrimitiva ( struct simple *p )  

{   
   
   if ( ((p->redirecSalida != REDIREC_SERVIDOR) && (p->redirecEntrada != REDIREC_SERVIDOR)) 
      
     && 
          


     (  
       (p->redirecSalida == REDIREC_CLIENTE)  || 
       (p->redirecEntrada == REDIREC_CLIENTE) || 
       (p->redirecSalida == REDIREC_FICHERO)  || 
       (p->redirecEntrada == REDIREC_FICHERO)  
     )  
      ) 
        
       mandaPadre(SIGDIBUJAR); 

      execvp (p->token[0], p->token); 
   
   
      if (errno != 0)   /* ha ocurrido un error */ 
     { 

       if (raiz->background == TRUE)         
         { 
            mandaPadreExit(SIGERRCMD); 
         } 
       else 
         { 
            mandaPadre(SIGERRCMD); 
         } 

     }  
     
}  /* fin de ejecutaPrimitiva */ 
    


/* 
*------------------------------------------------------------ 
*  
 *   PROTOTIPO:   int redireccion ( struct simple* ) 

*   DESCRIPCION: implementa la redirección correspondiente         
 * 
*-------------------------------------------------------------- 
*/ 


int redireccion ( struct simple *p ) 


 int masc; 




 switch (p->redirecEntrada) 
   { 

     case REDIREC_FICHERO: 
       
      if (!dupDf (p->fichEntrada, O_RDONLY, STDIN))  
         return (FALSE); 
      break; 
       

      case REDIREC_SERVIDOR: 
      
      if (raiz->background == FALSE)     /* se ha mandado un socket servidor en foreground */ 
        { 
          mandaPadre(SIGERRSERBACK); 
          return(FALSE); 
        }   
        
      if (!servidor (p->socketEntrada, SOCKET_ENTRADA)) 
         return(FALSE);  
      break; 
       

      case REDIREC_CLIENTE: 
       
      if (!cliente (p->socketEntrada, SOCKET_ENTRADA)) 
          return (FALSE); 
      break; 
         
    } 

  
  switch (p->redirecSalida) 
   { 
    
      case REDIREC_FICHERO: 
      
      masc = O_CREAT|O_WRONLY|(p->adicion?O_APPEND:O_TRUNC); 
      if (!dupDf (p->fichSalida, masc, STDOUT)) 
          return (FALSE);  
      break; 
      

     case REDIREC_SERVIDOR: 

         if (raiz->background == FALSE)     /* se ha mandado un socket servidor en foreground */ 
        { 
          mandaPadre(SIGERRSERBACK); 
          return(FALSE); 
        }  
       
      if (!servidor (p->socketSalida, SOCKET_SALIDA))  
         return (FALSE); 
      break; 
       

      case REDIREC_CLIENTE: 
       
     if (!cliente (p->socketSalida, SOCKET_SALIDA))  
          return (FALSE); 
      break; 
      
      case REDIREC_PANT: 
      
       masc=O_CREAT|O_WRONLY|O_TRUNC; 

       if (!dupDf ("resultado", masc, STDOUT)) 
          return (FALSE); 
      
       break; 

   }  
   
  return (TRUE); 

}  


/* 
*----------------------------------------------------------------- 
*  
 *   PROTOTIPO:   int dupDf ( char *, int, int ) 

*   DESCRIPCION: realiza la redirección duplicando los descriptores         
 *                de ficheros 

*----------------------------------------------------------------- 
*/ 



int dupDf ( char *nombre , int mascara , int dfStd )  


 int df; 

 df = open (nombre, mascara, PERMISO_DEFECTO); 

 if (df == -1) 
   { 

     mandaPadre(SIGERRREDIREC); 
     return (FALSE);  

    } 

 dup2 (df, dfStd);  /* copia sobre el descriptor de fichero estándar */ 
  
  close (df);        /* cierra el original */ 

 return (TRUE);  

}  /* fin de dupDf */ 




/* 
*-------------------------------------------------------- 

*    PROTOTIPO:   int direccionInternet ( char* ) 

*    DESCRIPCION: obtiene la dirección Internet del host 

*-------------------------------------------------------- 
*/ 



int direccionInternet ( char *nombre )      


 return (strpbrk (nombre, "01234567890") != NULL); 




/* 
*--------------------------------------------------------------- 

*    PROTOTIPO:   void obtenerHostyPuerto ( char* , char*, int* ) 

*    DESCRIPCION: almacena en "nombre" y "puerto" el nombre del  
 *                 nodo y su puerto de comunicaciones respectivamente 
*                 a partir de la variable "cad" 

*-------------------------------------------------------------- 
*/ 




void obtenerHostyPuerto ( char *cad, char *nombre, int* puerto) 


 char *tok1,  *tok2;        /* decodifica Host y Puerto de una */ 
                           /* entrada cadena tipo NOMBRE.PUERTO */ 
 tok1 = strtok (cad, "."); 
 tok2 = strtok (NULL,"."); 

if (tok2 == NULL )       /* nombre perdido y toma por defecto el local */ 
   { 
     strcpy (nombre, ""); 
     sscanf (tok1, "%d", puerto); 
   } 
 else 
   { 
     strcpy (nombre, tok1); 
     sscanf (tok2, "%d", puerto); 
   } 



/* 
*-------------------------------------------------------- 

*    PROTOTIPO:   int cliente ( char* , int ) 

*    DESCRIPCION: implementa el manejo del socket cliente 

*-------------------------------------------------------- 
*/ 


int cliente ( char *nombre, int tipo ) 


 int dfCliente, resultado, internet, dominio, longServidor, puerto, time_out=0; 
 char nombreHost[100]; 
 struct sockaddr_un direccUNIXServidor; 
 struct sockaddr_in direccINETServidor; 
 struct sockaddr* puntDireccSockServidor; 
 struct hostent* estructHost; 
 struct in_addr* nodoHost; 

/*  Abriremos un socket cliente con un nombre y tipo específico */ 

  internet = direccionInternet ( nombre );   /* ¿es socket internet? */ 
 dominio = internet ? AF_INET : AF_UNIX; 

 dfCliente = socket (dominio, SOCK_STREAM, PROTOCOLO_DEFECTO); 

 if (dfCliente == -1) 
   { 
     mandaPadre(SIGERRCLISOCK);  
      return(FALSE); 

   } 

 if (internet)    /* es un socket internet */ 
   { 

     obtenerHostyPuerto (nombre, nombreHost, &puerto); 
     if (nombreHost[0] == NULL) gethostname (nombreHost,100); 
     direccINETServidor.sin_family = AF_INET; 
     estructHost = gethostbyname (nombreHost); 

     if (estructHost==NULL) 
      { 

        mandaPadre(SIGERRCLISOCK);  
         return (FALSE); 

      } 

     nodoHost = (struct in_addr*) estructHost->h_addr; 
     /*printf("Direccion IP %s \n", inet_ntoa (*nodoHost));*/ 
      
      direccINETServidor.sin_addr = *nodoHost;    /* nos da la direccion IP */ 
     direccINETServidor.sin_port = puerto;       /* nos da el puerto de comunic */  
      puntDireccSockServidor = (struct sockaddr*) &direccINETServidor; 
     longServidor = sizeof (direccINETServidor); 

   } 

 else    /* socket del dominio UNIX */ 
   { 

     direccUNIXServidor.sun_family = AF_UNIX; 
     strcpy (direccUNIXServidor.sun_path, nombre); 
     puntDireccSockServidor = (struct sockaddr*) &direccUNIXServidor; 
     longServidor = sizeof (direccUNIXServidor); 

   } 

 do  /* conexion a un servidor */ 
   { 
  
      resultado = connect (dfCliente, puntDireccSockServidor, longServidor); 
     if (resultado == -1) { sleep (SOCKET_DORMIR); time_out++; } 
     if (time_out == 2 ) { 
                         mandaPadre(SIGERRCLISOCK);  
                          return(FALSE);  
                          } 
   } 
 while (resultado == -1); 

 if (tipo == SOCKET_SALIDA) dup2 (dfCliente, STDOUT); 
 if (tipo == SOCKET_ENTRADA) dup2 (dfCliente, STDIN);   
     
  close (dfCliente);  /* cierra el descriptor del fichero cliente original */ 
 return (TRUE); 

}  /* fin de cliente */ 

/* 
*-------------------------------------------------------- 

*    PROTOTIPO:   int servidor (char* , int ) 

*    DESCRIPCION: implementa el manejo del socket servidor 

*-------------------------------------------------------- 
*/ 


int servidor ( char *nombre, int tipo ) 


 int dfServidor, dfCliente, longServidor, longCliente; 
 int dominio, internet, puerto; 
 char nombreHost[100]; 
 struct sockaddr_un direccUNIXServidor; 
 struct sockaddr_un direccUNIXCliente; 
 struct sockaddr_in direccINETServidor; 
 struct sockaddr_in direccINETCliente; 
 struct sockaddr* puntDireccSockServidor; 
 struct sockaddr* puntDireccSockCliente; 


  internet = direccionInternet (nombre); 
 dominio = internet ? AF_INET : AF_UNIX; 

 dfServidor = socket (dominio, SOCK_STREAM, PROTOCOLO_DEFECTO); 

 if (dfServidor == -1) 
   { 
     mandaPadre (SIGERRSERSOCK); 
     return(FALSE); 
   } 

 mandaPadre (SIGDIBUJAR);   /* envia la señal de dibujar */ 

 if (internet)   /* es un socket internet */ 
   { 
  
      sscanf (nombre, "%d", &puerto);  /* obtiene numero y puerto */ 
      
      longServidor = sizeof (direccINETServidor); 
     bzero ((char*) &direccINETServidor, longServidor); 
     direccINETServidor.sin_family = AF_INET; 
     direccINETServidor.sin_addr.s_addr = htonl (INADDR_ANY);    
      direccINETServidor.sin_port = htons (puerto); 
     puntDireccSockServidor = (struct sockaddr*) &direccINETServidor; 

   } 

 else   /* socket dominio UNIX */ 
   { 

     direccUNIXServidor.sun_family = AF_UNIX; 
     strcpy (direccUNIXServidor.sun_path, nombre); 
     puntDireccSockServidor = (struct sockaddr*) &direccUNIXServidor; 
     longServidor = sizeof (direccUNIXServidor); 
      
      unlink (nombre);  /* borra el socket si ya existe */ 

   } 


 if (bind (dfServidor, puntDireccSockServidor, longServidor) == -1) 
   { 

     mandaPadre (SIGERRSERSOCK); 
     return (FALSE); 

   } 

 if (listen (dfServidor, LONG_COLA_DEFECTO) == -1) 
   { 

     mandaPadre (SIGERRSERSOCK); 
     return (FALSE);  

    } 

 if (internet) 
   { 

     longCliente = sizeof (direccINETCliente); 
     puntDireccSockCliente = (struct sockaddr*) &direccINETCliente; 

   } 

 else 
   { 

     longCliente = sizeof (direccUNIXCliente); 
     puntDireccSockCliente = (struct sockaddr*) &direccUNIXCliente; 

   } 


   
  dfCliente = accept (dfServidor, puntDireccSockCliente, &longCliente); 

 close (dfServidor); 

 if (dfCliente == -1) 
   { 

     mandaPadre (SIGERRSERSOCK); 
     return (FALSE); 

   } 

 if (tipo == SOCKET_SALIDA) dup2 (dfCliente, STDOUT); 
 if (tipo == SOCKET_ENTRADA) dup2 (dfCliente, STDIN);   
     
  close (dfCliente);  /* cierra el descriptor del fichero cliente original */ 
 return (TRUE); 
    
}  /* fin de servidor */  


/* 
*-------------------------------------------------------- 

*    PROTOTIPO:   int borraServer ( char* ) 

*    DESCRIPCION: desactiva un socket servidor creado 

*-------------------------------------------------------- 
*/ 


int borraServer ( char *nombre ) 


 int dfCliente, resultado, internet, dominio, longServidor, puerto, time_out=0; 
 char nombreHost[100]; 
 struct sockaddr_un direccUNIXServidor; 
 struct sockaddr_in direccINETServidor; 
 struct sockaddr* puntDireccSockServidor; 
 struct hostent* estructHost; 
 struct in_addr* nodoHost; 

/*  Abriremos un socket cliente con un nombre y tipo específico */ 

  internet = direccionInternet ( nombre );   /* ¿es socket internet? */ 
 dominio = internet ? AF_INET : AF_UNIX; 

 dfCliente = socket (dominio, SOCK_STREAM, PROTOCOLO_DEFECTO); 

 if (dfCliente == -1) 
   { 
     Xerror(ERRORBORRAR);  
      return(FALSE); 

   } 

 if (internet)    /* es un socket internet */ 
   { 

     obtenerHostyPuerto (nombre, nombreHost, &puerto); 
     if (nombreHost[0] == NULL) gethostname (nombreHost,100); 
     direccINETServidor.sin_family = AF_INET; 
     estructHost = gethostbyname (nombreHost); 

     if (estructHost==NULL) 
      { 

        Xerror(ERRORBORRAR);  
         return (FALSE); 

      } 
           
      nodoHost = (struct in_addr*) estructHost->h_addr; 
     /*printf("Direccion IP %s \n", inet_ntoa (*nodoHost));*/ 
      
      direccINETServidor.sin_addr = *nodoHost;    /* nos da la direccion IP */ 
     direccINETServidor.sin_port = puerto;       /* nos da el puerto de comunic */  
      puntDireccSockServidor = (struct sockaddr*) &direccINETServidor; 
     longServidor = sizeof (direccINETServidor); 

   } 

 else    /* socket del dominio UNIX */ 
   { 

     direccUNIXServidor.sun_family = AF_UNIX; 
     strcpy (direccUNIXServidor.sun_path, nombre); 
     puntDireccSockServidor = (struct sockaddr*) &direccUNIXServidor; 
     longServidor = sizeof (direccUNIXServidor); 

   } 

 do  /* conexion a un servidor */ 
   { 



  
      resultado = connect (dfCliente, puntDireccSockServidor, longServidor); 
     if (resultado == -1) { sleep (SOCKET_DORMIR); time_out++; } 
     if (time_out == 2 ) { 
                         Xerror(ERRORBORRAR);  
                          return(FALSE);  
                          } 
   } 
 while (resultado == -1); 
    
  close (dfCliente);  /* cierra el descriptor del fichero cliente */ 
 return (TRUE); 

}  /* fin de borraServer */ 






/* 
*--------------------------------------------------------------------- 

*   PROTOTIPO:   void trataSignal ( void ) 

*   DESCRIPCION: especifica la acción a tomar al llegar cada  
 *                una de las señales definidas posibles 

*--------------------------------------------------------------------- 
*/ 


void trataSignal ( void ) 
 { 
   signal ( SIGPANT, XmanejaPantalla ); 
   signal ( SIGERRCMD, manejaErrorCmd ); 
   signal ( SIGERRSERBACK, manejaErrorSerBack ); 
   signal ( SIGERRCLISOCK, manejaErrorCliSock ); 
   signal ( SIGERRSERSOCK, manejaErrorSerSock ); 
   signal ( SIGERRREDIREC, manejaErrorRedirec ); 
   signal ( SIGDIBUJAR, manejaDibujar ); 
 }