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:
-
Nivel 1: Nivel Físico.
- Nivel 2: Nivel de Enlace de
Datos.
- Nivel 3:
Nivel de Red.
- Nivel 4: Nivel de Transporte.
- Nivel 5: Nivel de Sesión.
- Nivel 6: Nivel de
Presentación.
- Nivel 7: Nivel de Aplicación.
Sólo nos
interesará conocer algo sobre los niveles 3 (de Red), nivel 4 (de Transporte) y los niveles 5, 6 y 7.
- Nivel de Red:
En él se
solucionan los problemas de encaminamiento de la información a través de las
distintas subredes. Por tanto, se necesita conocer la topología de la red y la
interconexión de redes. Encontrar una ruta para la comunicación será su
misión fundamental. Aporta dos tipos de
servicios:
- Servicio sin conexión, recibiendo
del nivel superior paquetes de información o datagramas. Se recalcula la
ruta a seguir por cada paquete recibido. INTERNET posee este
mecanismo.
- Servicio orientado a conexión, que
establece un circuito virtual entre las máquinas correspondientes. Así se
asegura el flujo y el control de errores. X25.PLP adopta este
esquema.
- Nivel de Transporte:
Aquí se
establece la comunicación virtual entre aplicaciones y no ya entre máquinas (comunicación extremo a extremo). El servicio más importante que presenta es el
modo conectado (conexión segura, control de flujo, ensamblado,...).
Mediante la multiplexación se podrá establecer varias conexiones a nivel
de transporte mediante el uso de una sola conexión de red. Los protocolos TCP y
UDP pertenecen a este nivel en la familia ARPA
(INTERNET).
- Niveles de
Sesión-Presentación-Aplicación:
Ofrecen
servicios orientados al usuario y no ya exclusivamente orientados a un nivel
superior. El establecimiento de una sesión de comunicación, la gestión
del diálogo y la sincronización son servicios que aportan estos
niveles. También se ubican en este nivel las RPC ( llamadas a
procedimientos remotos ), XDR y sistemas de ficheros tipo NFS.
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:
-
Nivel Interfaz de Red y Hardware:
Agrupa los bits en tramas
para el manejo de la información y se ocupa de las características técnicas de
la red (voltajes, pines, ...)
-
Nivel
Internet:
Se corresponde con el nivel de Red OSI y
controla el direccionamiento de la información. El IP se trata de un
protocolo para el intercambio de datagramas en modo no conectado. Esto no
garantiza la llegada de mensajes (cosa que se hará con el TCP). El
algoritmo de direccionamiento de Internet se basa en tablas de
direccionamiento de los datagramas difundidos por los gateways.
-
Nivel de Transporte:
Se corresponde con el de Transporte OSI,
garantizando la seguridad de la conexión y el control del flujo. Incluye el Protocolo de Control de Transmisión
(TCP) y el Protocolo de
Datagrama de Usuario (UDP).
* El TCP es un protocolo orientado a conexión que transporta de forma segura grupos de octetos (segmentos)
modo duplex (en los dos sentidos).
Utiliza el mecanismo de puerto (al
igual que el protocolo de transporte UDP, pero que actúa en modo
datagrama no conectado). Este mecanismo se basa en la asignación para cada uno
de los protocolos del nivel de transporte (TCP o UDP) de un conjunto de puertos
de E/S identificados mediante un número entero. Así TCP y UDP multiplexarán las conexiones por medio de los números de los puertos.
Existen una serie de puertos reservados a aplicaciones estándares
Internet (ECHO - puerto 7, FTP - puerto 21, TELNET - puerto 23). El
archivo /etc/services contiene la lista de los puertos estándar. En UNIX
están reservados los números de puerto inferiores a 1024. El resto pueden ser
utilizados, cualidad fundamental que aprovechan los programas definidos por el
usuario para el establecimiento de comunicaciones entre hosts.
-
Nivel de Aplicación:
Incluye los niveles OSI de
sesión, presentación y aplicación. Ejemplos de estos niveles son el telnet,
ftp o el sistema de ficheros de red NFS para el nivel de aplicación,
el lenguaje de descripción de información XDR para el nivel de
presentación o la interfaz de llamada a procedimientos remotos RPC.
Por ejemplo, el formato de una trama
telnet sería:

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:
- una dirección de red
- una dirección de la máquina dentro de la red
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.
- La dirección nula se
refiere a la red.
- Para direccionar un mensaje a todas las máquinas (broadcast), se usa
una dirección dentro de la red en la cual todos sus bits son iguales a
1.
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:
- Redes clase
A:
Gran tamaño. El primer bit es 0, [1-127],
con lo que su dirección son los 7 siguientes bits de la dirección de 32 bits
(permite 127 redes de clase A). Los 24 bits restantes se usan para direccionar
sus hosts locales.
- Redes clase
B:
Tamaño medio. Los dos primeros bits son 10,
[128-191], y su dirección la componen los siguientes 14 bits de la dirección de
32 bits (16384 redes de clase B). Los 16 bits restantes se usan para
direccionar sus hosts locales.
- Redes clase
C:
Pequeño tamaño. Su dirección la componen los
siguientes 21 bits a los tres primeros, 110, característicos [192-223] ( 2097152
redes de clase C ). Los 8 bits restantes se usan para direccionar sus hosts
locales (256 hosts: del 0 al 255).
- Redes clase D:
Dirección
multicast: Sus primeros
4 bits son 1110 [224-239], indicando que s etrata de una dirección multicast.
Los restantes 28 bits comprenden un grupo específico multicast. Esta dirección
es una dirección destino para una o varias máquinas (a diferencia de las clases
anteriores, que se referían sólo a una máquina)
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:
- El proceso servidor crea un socket con
nombre y espera la conexión.
- El proceso cliente crea un
socket sin nombre.
- El proceso cliente realiza una
petición de conexión al socket servidor.
- 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:
- El tipo del socket, que indica la
naturaleza del mismo, el tipo de comunicación que puede generarse entre los
sockets.
- El dominio del socket especifica el conjunto de sockets que pueden
establecer una comunicación con el mismo.
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:
- Fiabilidad de transmisión.
- Mantenimiento del orden de los
datos.
- No
duplicación de los datos.
- El "Modo Conectado" en la
comunicación.
- Envío de mensajes urgentes.
Los tipos
disponibles son los siguientes:
- Tipo SOCK_DGRAM:
Sockets para comunicaciones en modo no conectado, con envío de datagramas de tamaño
limitado ( tipo telegrama ). En dominios Internet como la que nos ocupa
el protocolo del nivel de transporte sobre el que se basa es el UDP.
-
Tipo SOCK_STREAM:
Para comunicaciones fiables en modo conectado, de dos vías y con tamaño variable de los mensajes de datos. Por debajo, en dominios Internet,
subyace el protocolo TCP.
- Tipo SOCK_RAW:
Permite el acceso a protocolos de
más bajo nivel como el IP ( nivel de red )
- Tipo SOCK_SEQPACKET:
Tiene las
características del SOCK_STREAM pero además el tamaño de los
mensajes es fijo.
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:
- Dominio AF_UNIX ( Address Family
UNIX ):
El
cliente y el servidor deben estar en la misma máquina. Debe incluirse el fichero
cabecera /usr/include/sys/un.h. La estructura de una dirección en este
dominio es:
struct
sockaddr__un {
short sun__family;
/* en este caso AF_UNIX */
char sun__data[108];
/* dirección */
};
- Dominio AF_INET (
Address Family INET ):
El
cliente y el servidor pueden estar en cualquier máquina de la red Internet.
Deben incluirse los ficheros cabecera /usr/include/netinet/in.h,
/usr/include/arpa/inet.h, /usr/include/netdb.h. La estructura de una
dirección en este dominio es:
struct
in__addr {
u__long s__addr;
};
struct
sockaddr__in {
short sin_family;
/* en este caso AF_INET */
u__short sin_port;
/* numero del puerto */
struct
in__addr sin__addr;
/* direcc Internet */
char sin_zero[8];
/* campo de 8 ceros */
};
Estos
dominios van a ser los utilizados en xshine. Pero existen otros
como:
- Dominio AF_NS:
Servidor y
cliente deben estar en una red XEROX.
- Dominio AF_CCITT:
Para
protocolos CCITT, protocolos X25, ...
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:
- 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)
- 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.
- int listen (int dfServer, int longCola)
Especifica
el máximo número de peticiones de conexión pendientes.
- 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:
- 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)
- 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 );
}