Manual Sockets
Manual Sockets
Manual Sockets
Laboratorio
ÍNDICE
1. INTRODUCCIÓN .......................................................................... 3
1.1 La familia de protocolos TCP/IP. ................................................ 3
1.2 Nivel de red (IP). ......................................................................... 3
1.3 Nivel de transporte (TCP, UDP). ................................................ 4
2. SOCKETS EN C ........................................................................... 4
2.1. Conceptos básicos. ................................................................... 4
2.2 Dominios de comunicación. ....................................................... 6
2.3 Tipos de sockets. ........................................................................ 7
2.4 Ordenación de los bytes. ............................................................ 7
2.5 Servicio orientado a conexión (TCP) ......................................... 8
2.5.1 El modelo utilizado ................................................................... 8
2.5.2 Resumen de llamadas al sistema. .......................................... 9
2.6. Creación de un socket ............................................................... 10
2.6.1 Estructuras de datos ............................................................... 11
2.6.2 Asociación de un socket a unos parámetros determinados... 12
2.6.3 Habilitar un socket para recibir peticiones de conexión.......... 14
2.6.4 Aceptar peticiones de conexión ............................................ 14
2.6.5. Lanzar peticiones de conexión ............................................. 16
2.6.6 Conexión entre los sockets del cliente y servidor.................. 17
2.6.7 Enviar y recibir datos a través de un socket TCP.................. 17
2.6.8 Cierre de un socket .................................................................. 19
2.7 Diagrama de estados de una conexión TCP............................. 20
2.8 Servicio no orientado a conexión (UDP)..................................... 21
2.8.1 El modelo utilizado ................................................................... 21
2.8.2 Enviar y recibir datos a través de un socket UDP................. 22
3. OTRAS FUNCIONALIDADES...................................................... 24
3.1 Función gethostname.................................................................. 24
3.2 Creación de un proceso hijo Función fork.................................. 25
3.3 Servidores TCP concurrentes y secuenciales............................ 26
3.4 Servidores UDP concurrentes y secuenciales........................... 27
4. EJEMPLO...................................................................................... 28
4.1 Introducción.................................................................................. 28
4.2 Descripción del programa Servidor............................................. 28
4.3 Descripción del programa Cliente............................................... 31
4.4 Descripción de Servidor concurrente y secuencial..................... 33
4.5 Listados completos de código..................................................... 36
4.6 Bibliografía………………………………………………………… 39
-2-
Laboratorio de Arquitectura de Redes
1. INTRODUCCIÓN
Los niveles 5-7 de OSI se incluyen dentro de los servicios al usuario ofrecidos
por la familia TCP/IP:
Data Link Layer, cubre los niveles 1 y 2 de OSI. Existen multitud de protocolos
usados por TCP/IP ya que también existen muchos canales físicos de
comunicación que permiten la conexión entre dos máquinas: líneas telefónicas,
Ethernet, token ring, línea serie...
-3-
Laboratorio de Arquitectura de Redes
Además utiliza números de 16 bits para identificar varios destinos dentro de una
misma máquina. Son los llamados puertos". Un proceso que quiera contactar con otro
de una máquina remota debe conocer además de la dirección Internet de la otra
máquina, el número de puerto en el que el otro proceso está escuchando.
Un servicio UDP por otro lado, ofrece en esencia lo mismo que el protocolo IP de nivel
3, con dos diferencias fundamentales:
2. SOCKETS EN C
Los sockets son una de las herramientas que ofrecen los Sistemas Operativos para la
comunicación entre diferentes procesos. La particularidad que tienen frente a otros
mecanismos de comunicación entre procesos (IPC – Inter-Process Communication)
es que posibilitan la comunicación aún cuando ambos procesos estén corriendo en
distintos sistemas unidos mediante una red. De hecho, el API de sockets es la base
de cualquier aplicación que funcione en red puesto que ofrece una librería de
funciones básicas que el programador puede usar para desarrollar aplicaciones en
red.
-4-
Laboratorio de Arquitectura de Redes
Este API permite la comunicación sobre una gran variedad de redes, pero en este
manual nos concentraremos en su uso sobre redes TCP/IP.
Los sockets para TCP/IP permiten la comunicación de dos procesos que estén
conectados a través de una red TCP/IP. En una red de este tipo, cada máquina está
identificada por medio de su dirección IP que debe ser única. Sin embargo, en cada
máquina pueden estar ejecutándose múltiples procesos simultáneamente. Cada uno
de estos procesos se asocia con un número de puerto, para poder así diferenciar los
distintos paquetes que reciba la máquina (proceso de multiplexación).
Un socket se identifica unívocamente por la dupla dirección IP + número de puerto.
Una comunicación entre dos procesos se identifica mediante la asociación de los
sockets que estos emplean para enviar y recibir información hacia y desde la red:
identificador de socket origen + identificador de socket destino.
Un socket es una abstracción a través de la cual una aplicación puede enviar y recibir
información de manera muy similar a como se escribe y lee de un fichero. La
información que una aplicación envía por su socket origen puede ser recibida por otra
aplicación en el socket destino y viceversa.
Existen diferentes tipos de sockets dependiendo de la pila de protocolos sobre la que
se cree dicho socket. Nos centraremos en la pila de protocolos TCP/IP.
-5-
Laboratorio de Arquitectura de Redes
En este manual se verá como se le pueden asociar estos parámetros a los sockets
creados.
-6-
Laboratorio de Arquitectura de Redes
El tipo de sockets Stream hace uso del protocolo TCP (protocolo de la capa de
transporte) que provee un flujo de datos bidireccional, orientado a conexión,
secuenciado, sin duplicación de paquetes y libre de errores.
El tipo de sockets Datagram hacen uso del protocolo UDP (protocolo de la capa de
transporte), el cual provee un flujo de datos bidireccional, no orientado a conexión, en
el cual los paquetes pueden llegar fuera de secuencia, puede haber pérdidas de
paquetes o pueden llegar con errores.
El tipo de sockets Raw permiten un acceso a más bajo nivel, pudiendo acceder
directamente al protocolo IP del nivel de Red. Su uso está mucho más limitado ya que
está pensado principalmente para desarrollar nuevos protocolos de comunicación, o
para obviar los protocolos del nivel de transporte.
Por ejemplo, si un ordenador indica que desea establecer una conexión con el puerto
80 (servidor Web) de otro ordenador, si lo envía en formato Big-Endian los bytes que
enviará serán: 0x00 0x50, mientras que en formato Little-Endian enviará los bytes
0x50 0x00. Si el otro ordenador representa los datos en formato diferente, al recibir,
por ejemplo, 0x00 0x50 lo traducirá como el puerto 0x50 0x00, esto es 20480, que
obviamente no corresponde al servidor Web.
-7-
Laboratorio de Arquitectura de Redes
Para solventar este problema, se definió un formato conocido como Orden de los
Bytes en la Red (Network Byte Order), que establece un formato común para los datos
que se envían por la red, como son el número de puerto, etc., de forma que todos los
datos, antes de ser enviados a las funciones que manejan las conexiones en la red,
deben ser convertidos a este formato de red. A continuación, podemos ver las cuatro
funciones que permiten manejar estos datos.
htonl.
#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
La función htonl convierte el entero de 32 bits dado por hostlong desde el orden de
bytes del hosts al orden de bytes de la red.
Ejemplo:
unsigned long int red, host;
...
red=htonl(host);
htons.
#include <netinet/in.h>
unsigned short int htons(unsigned short int hostshort);
La función htons convierte el entero de 16 bits dado por hostshort desde el orden de
bytes del hosts al orden de bytes de la red.
Ejemplo:
unsigned short int red, host;
...
red=htons(host);
ntohl.
#include <netinet/in.h>
unsigned long int ntohl(unsigned long int netlong);
La función ntohl convierte el entero de 32 bits dado por netlong desde el orden de
bytes de la red al orden de bytes del hosts.
Ejemplo:
unsigned long int host, red;
...
host=ntohl(red);
ntohs.
#include <netinet/in.h>
unsigned short int ntohs(unsigned short int netshort);
La función ntohs convierte el entero de 16 bits dado por netshort desde el orden de
bytes de la red al orden de bytes del hosts.
Ejemplo:
unsigned short int host, red;
...
host=ntohs(red);
-8-
Laboratorio de Arquitectura de Redes
-9-
Laboratorio de Arquitectura de Redes
Por otro lado el cliente creará un socket, le dará una dirección local y lanzará una
petición de conexión al servidor (connect()). Si el servidor está disponible (ha
ejecutado accept y no hay peticiones anteriores encoladas) inmediatamente se
desbloquean tanto cliente como servidor. En la parte del servidor se habrá creado un
nuevo socket (cuyo identificador devuelve accept()) que es el que realmente está
conectado con el cliente, de forma que el original sigue sirviendo al servidor para
atender nuevas peticiones de conexión.
Cliente y servidor se intercambiarán datos mediante read() y write()(también se
utilizan las funciones send() y recv() para realizar la transferencia de datos),
pudiendo finalmente cerrar la conexión mediante la llamada close().
Una vez realizada la llamada listen(), todas las peticiones de conexión que lleguen
al socket serán guardadas en su cola de peticiones. A continuación el proceso
propietario del socket podrá atender la primera de ellas mediante la llamada accept().
e) Estableciendo conexión.
- 10 -
Laboratorio de Arquitectura de Redes
f) Transferencia de datos.
Cuando dos procesos ya han quedado "enganchados" por una conexión, pueden
enviar-recibir datos, para ello se usan las llamadas send() y recv(). Estas llamadas
sólo trabajan con sockets "conectados" ya que no permiten especificar dirección
destino.
g) Cerrar la conexión
Cuando hagamos close(), el sistema nos asegurará que antes de destruir el socket
todos los datos que han sido enviados serán recibidos en el otro extremo.
En caso de que se haya producido algún error durante la creación del socket, la
función devuelve -1 y la variable global errno se establece con un valor que indica el
error que se ha producido. La función perror(“...”) muestra por pantalla un
mensaje explicativo sobre el error que ha ocurrido.
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
...
int sockfd;
sockfd = socket ( AF_INET, SOCK_STREAM, 0 );
...
- 11 -
Laboratorio de Arquitectura de Redes
Para poder realizar esta asociación, lo primero es conocer una serie de estructuras
que son necesarias para llevarla a cabo.
struct sockaddr
{
unsigned short sa_family; // AF_*
char sa_data[14]; // Dirección de protocolo.
};
struct sockaddr_in
{
short int sin_family; // AF_INET
unsigned short sin_port; // Numero de puerto.
struct in_addr sin_addr; // Dirección IP.
unsigned char sin_zero[8]; // Relleno.
};
struct in_addr
{
unsigned long s_addr; // 4 bytes.
};
Para poder definir los parámetros que se quieren asociar al socket, se usan
estructuras específicas para cada dominio. La estructura TCP/IP es la sockaddr_in,
equivalente a la estructura sockaddr, pero que permite referenciar a sus elementos
de forma más sencilla.
- 12 -
Laboratorio de Arquitectura de Redes
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
http://linux.die.net/man/2/bind
Ejemplo:
...
struct sockaddr_in sin;
...
sin.sin_family = AF_INET;
sin.sin port = htons ( 1234 );
// Número de puerto donde recibirá paquetes el programa
sin.sin_addr.s_addr = inet_addr ("132.241.5.10");
// IP por la que recibirá paquetes el programa
...
Existen algunos casos especiales a la hora de asignar valores a ciertos
campos:
En caso de asignar el valor cero a sin_port, el sistema fijará el número de
puerto al primero que encuentre disponible.
Para realizar la asignación de la dirección IP de forma automática (sin tener
por que conocer previamente la dirección IP donde se va a ejecutar el
programa) se puede utilizar la constante: INADDR_ANY. Esto le indica al
sistema que el programa recibirá mensajes por cualquier IP válida de la
máquina, en caso de disponer de varias.
- 13 -
Laboratorio de Arquitectura de Redes
Ejemplo:
...
sin.sin_port = 0;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
...
http://linux.die.net/man/2/listen
http://linux.die.net/man/2/accept
- 14 -
Laboratorio de Arquitectura de Redes
Ejemplo:
...
int sockfd, new_sockfd;
struct sockaddr_in server_addr;
struct sockaddr_in remote_addr;
int addrlen;
// Creación del socket.
sockfd = socket (PF_INET, SOCK_STREAM, 0 );
// Definir valores en la estructura server_addr.
sin.sin_family = AF_INET;
sin.sin port = htons ( 1234 ); // Número de puerto donde
// recibirá paquetes el programa
sin.sin_addr.s_addr = htonl(INADDR_ANY);
// Asociar valores definidos al socket
bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct
sockaddr));
// Se habilita el socket para poder recibir conexiones.
- 15 -
Laboratorio de Arquitectura de Redes
http://linux.die.net/man/2/connect
Ejemplo:
...
int sockfd;
struct sockaddr_in remote_addr;
int addrlen;
// Creación del socket.
sockfd = socket (AF_INET, SOCK_STREAM, 0 );
// Definir valores en la estructura server_addr.
sin.sin_family = AF_INET;
sin.sin port = htons ( 1234 ); // Número de puerto donde
// está esperando el servidor
sin.sin_addr.s_addr = inet_addr(“1.2.3.4”); // Dirección IP
// del servidor
addrlen = sizeof (struct sockaddr );
- 16 -
Laboratorio de Arquitectura de Redes
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int s, void *buf, size_t len, int flags);
http://linux.die.net/man/2/recv
- 17 -
Laboratorio de Arquitectura de Redes
La función send() devuelve el número de bytes enviados, que puede ser menor que
la cantidad indicada en el parámetro len.
La función recv() se utiliza para recibir datos, y posee un prototipo similar a la
anterior:
ssize_t recv (int sockfd, void *buf, size_t len, int
flags);
sockfd: Identificador de socket para la recepción de los datos.
buf: Puntero a un buffer donde se almacenarán los datos recibidos.
len: Número máximo de bytes a recibir
flags: Por defecto, 0. Para más información, consultar la ayuda en línea.
http://linux.die.net/man/2/close
- 18 -
Laboratorio de Arquitectura de Redes
#include <sys/socket.h>
int shutdown(int sockfd, int how)
http://linux.die.net/man/2/shutdown
sockfd es el descriptor de socket que se desea desconectar, y how puede tener uno
de los siguientes valores:
0 -- No se permite recibir más datos
1 -- No se permite enviar más datos
2 -- No se permite enviar ni recibir más datos (similar a close())
- 19 -
Laboratorio de Arquitectura de Redes
- 20 -
Laboratorio de Arquitectura de Redes
atravesar una serie de estados intermedios). El evento que provoca este cierre es la
llamada en ambos procesos (cliente y servidor) a la función close().
- 21 -
Laboratorio de Arquitectura de Redes
#include <sys/types.h>
#include <sys/socket.h>
- 22 -
Laboratorio de Arquitectura de Redes
- 23 -
Laboratorio de Arquitectura de Redes
Ejemplo servidor:
...
struct sockaddr_in sserv;
char mens_cli[100];
...
long = sizeof(sserv);
recvfrom(sid,mens_cli,100,0,(struct sockaddr *)&sserv,
&long);
// La estructura sserv ha sido rellenada con la dirección
// IP y número de puerto del cliente
...
sendto(sid,mens_clien, strlen(mens_clien)+1,0, (struct
sockaddr *) &sserv,sizeof(sserv));
...
3. OTRAS FUNCIONALIDADES.
La función devuelve un puntero a una estructura hostent, que se define como sigue:
- 24 -
Laboratorio de Arquitectura de Redes
struct hostent
{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
#define h_addr h_addr_list[0]
h_name: Nombre oficial de la máquina.
h_aliases: Array de nombres alternativos.
h_addrtype: Tipo de dirección de retorno (AF_INET ).
h_length: Longitud de la dirección en bytes.
h_addr_list: Array de direcciones de red para la máquina.
h_addr: La primera dirección en h_addr_list.
pid_t fork(void);
http://linux.die.net/man/2/fork
La función fork() permite crear un proceso dentro del proceso principal (o proceso
padre). El prototipo de la función fork() es el siguiente:
pid_t fork(void);
La función fork() no recibe ningún parámetro. Al término de su ejecución existirán dos
procesos idénticos cuya única diferencia es el valor de la variable que devuelve la
función. Este valor es 0 para el nuevo proceso creado (proceso hijo) y para el proceso
principal (proceso padre) esa variable es un número mayor que 0 (corresponde con el
identificador de proceso del hijo). Si se produce un error, el valor de la variable será -1.
Ejemplo:
pid_t cpid;
...
cpid = fork();
if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); }
if (cpid > 0)
{ // ode executed by parent
printf("Child PID is %ld\n", cpid);
- 25 -
Laboratorio de Arquitectura de Redes
...
}
else { // Code executed by child
printf("My PID is %ld\n", (long) getpid());
...
}
...
El proceso hijo hereda todas las variables del proceso padre. Esta función se puede
utilizar para dotar de concurrencia a los servidores TCP.
- 26 -
Laboratorio de Arquitectura de Redes
- 27 -
Laboratorio de Arquitectura de Redes
Sin embargo, existen servidores en los que debido a la propia lógica del servicio
prestado la concurrencia no es tan sencilla. Estos servicios son todos aquellos que
guarden el “estado” de los clientes. Pongamos como ejemplo un servicio que sea un
juego de azar. El “estado” de cada cliente será el balance de ganancias o perdidas de
cada cliente. Si bien el servidor puede recibir la jugada del cliente, calcular si gana o
pierde y responder con el resultado, debe ser capaz de disociar a que cliente
corresponde cada una de las jugadas que recibe y modificar su estado de la manera
apropiada.
4. EJEMPLO.
4.1 Introducción
En este ejemplo, el cliente nos pedirá que introduzcamos un mensaje por teclado y se
lo enviará al servidor que nos lo mostrará por pantalla.
Se debe ejecutar el cliente y el servidor en hosts independientes (aunque dentro de un
mismo equipo se puede hacer desde dos ventanas de terminales distintas).
Iniciaremos en primer lugar el servidor, y se le deberá asignar un puerto libre con
cualquier número entre 2000 y 65535. . Si el puerto está disponible, el servidor se
bloqueará hasta que reciba una conexión desde el cliente (no tiene que hacer nada
hasta que se establezca una conexión).
Esta es una línea de comandos típica del servidor:
./server 55000
Para ejecutar el cliente se le tiene que pasar dos argumentos, el nombre del host en el
que el servidor está en ejecución y el número de puerto en el que el servidor está
escuchando las conexiones Aquí está la línea de comandos del cliente para conectar
con el servidor:
Si ambos se están ejecutando en el mismo equipo, para averiguar el nombre del host
basta con ejecutar el comando hostname en el servidor.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
- 28 -
Laboratorio de Arquitectura de Redes
char buffer[256];
El usuario tiene que pasar el número de puerto en el que el servidor aceptará las
conexiones como argumento. Este código muestra un mensaje de error si el usuario
no lo hace.
if (argc <2)
{
fprintf(stderr, "Error, no hay ningún puerto");
exit(1);
}
La función bzero() establece todos los valores en un búfer a cero. Tiene dos
argumentos, el primero es un puntero a la memoria intermedia y el segundo es el
tamaño del búfer. Por lo tanto, esta línea inicializa serv_addr a ceros.
bzero((char*)&serv_addr,sizeof(serv_addr));
- 29 -
Laboratorio de Arquitectura de Redes
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
El tercer campo es una estructura del tipo de struct in_addr que contiene sólo un
único campo unsigned long s_addr .
serv_addr.sin_addr.s_addr = INADDR_ANY;
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept (sockfd,(struct sockaddr*)&cli_addr,&clilen);
if (newsockfd <0)
error("Error en aceptar");
- 30 -
Laboratorio de Arquitectura de Redes
Se debe tener en cuenta que sólo se llega a este punto después de que un cliente se
ha conectado a nuestro servidor. Este código inicializa el buffer mediante la función
bzero(), y luego lee desde el socket. La llamada de lectura utiliza el nuevo descriptor
de fichero, el devuelto por accept() , no el descriptor de archivo original devuelto por
socket().También se bloqueará read()hasta que haya algo para leer, es decir,
después de que el cliente ha ejecutado un write().
bzero (buffer,256);
n =read(newsockfd,buffer,255);
if (n <0)
error("Error al leer del socket");
printf("Aquí está el mensaje:% s ", Buffer);
Una vez que la conexión ha sido establecida, ambos extremos pueden leer y escribir
en la conexión.
n = write(newsockfd,"Tengo el mensaje",18);
if (n <0)
error("Error al escribir en el socket");
Todo lo escrito por el cliente será leído por el servidor, y todo lo escrito por el servidor
será leído por el cliente. Este código simplemente escribe un mensaje corto al cliente.
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
Los archivos de cabecera son los mismos que para el servidor con una adición. El
archivo netdb.h define la estructura hostent (***).
La función error()es idéntica a la del servidor, así como las variables sockfd,
portno, y n
- 31 -
Laboratorio de Arquitectura de Redes
.La variable serv_addr contendrá la dirección del servidor al que nos queremos
conectar. La variable server es un puntero a una estructura del tipo hostent.
char buffer[256];
if (argc <3)
{
fprintf(stderr, "el puerto host %s está en uso", Argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd <0)
error("Error de apertura de socket ");
server = gethostbyname(argv[1]);
if (server == NULL)
{
fprintf (stderr, "Error, no hay host ");
exit(0);
}
Si esta estructura es NULL, el sistema no pudo encontrar una máquina con este
nombre.
La función connect es llamada por el cliente para establecer una conexión con el
servidor. Toma tres argumentos, el descriptor de fichero socket, la dirección de la
máquina a la que quiere conectarse (incluyendo el número de puerto), y el tamaño de
esta dirección.
- 32 -
Laboratorio de Arquitectura de Redes
El resto del código pide al usuario que introduzca un mensaje, usa fgets para leer el
mensaje de la entrada estándar, escribe el mensaje en el socket, lee la respuesta y
muestra esta respuesta en la pantalla.
Todo lo visto anteriormente nos lleva a una única ejecución de los servicios del
servidor cuando son requeridos por el cliente. Si deseamos que el servidor siga
proporcionando esos servicios sin tener que ser ejecutado de nuevo, necesitamos que
el proceso sea concurrente, para lo cual debemos realizar una serie de modificaciones
en el código del servidor.
El siguiente código tiene una función ficticia llamada dostuff(int sockfd). Esta
función se encargará de la conexión después de que se haya establecido y prestará
los servicios que el cliente solicita.
void dostuff(int);
Para permitir que el servidor para manejar múltiples conexiones simultáneas, hacemos
los siguientes cambios en el código.
- 33 -
Laboratorio de Arquitectura de Redes
Este es el código:
while (1)
{
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR en aceptar ");
pid = fork();
if (pid < 0)
error("ERROR en fork");
if (pid == 0)
{
close(sockfd);
dostuff(newsockfd);
exit(0);
}
else
close(newsockfd);
} /* end of while */
bzero(buffer,256);
n = read(sock,buffer,255);
if (n < 0)
error("ERROR al leer del socket");
printf("Aquí está el mensaje: %s\n",buffer);
n = write(sock,"Tengo el mensaje",18);
if (n < 0)
error("ERROR al escribir en el socket");
}
(*)
NOMBRE
perror, errno - Mensajes de error del sistema
SINOPSIS
vacío perror (s)
char * s;
# Include <errno.h>
int sys_nerr;
char * sys_errlist [];
int errno;
DESCRIPCIÓN
perror() produce un mensaje de error breve en la norma error que
describe el último error encontrado durante una llamada para un sistema
o una función de biblioteca. Si s no es un puntero NULL y no apunta a
una cadena, la cadena apunta a se imprime, seguida por dos puntos,
seguido de un espacio, si lowed por el mensaje y una nueva línea. Si s
es un puntero NULL o apunta a una cadena nula, sólo se imprime el
mensaje, seguido de una nueva línea. Para ser de mayor utilidad, el
- 34 -
Laboratorio de Arquitectura de Redes
(**)
struct sockaddr_in
{
short sin_family; / * debe ser AF_INET * /
u_short sin_port;
struct in_addr sin_addr;
sin_zero char[8]; / * No se utiliza, debe ser cero * /
};
(***)
struct hostent
{
char * h_name; / * nombre oficial del host * /
char ** h_aliases; / * lista de alias * /
int h_addrtype; / * tipo de dirección de host * /
int h_length; / * longitud de la dirección * /
char ** h_addr_list; / * lista de direcciones de nombres del servidor * /
En las páginas siguientes se añaden los listados completos del cliente y los dos
servidores.
- 35 -
Laboratorio de Arquitectura de Redes
char buffer[256];
if (argc < 3)
{
fprintf(stderr,"El puerto host %s está en uso\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR de apertura de socket");
server = gethostbyname(argv[1]);
if (server == NULL)
{
fprintf(stderr,"ERROR, no hay host \n");
exit(0);
}
bcopy((char *)server->h_addr,(char*)&serv_addr.sin_addr.s_addr,server->h_length);
serv_addr.sin_port = htons(portno);
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR al escribir en socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR al leer del socket");
printf("%s\n",buffer);
close(sockfd);
return 0;
}
- 36 -
Laboratorio de Arquitectura de Redes
SERVIDOR.C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
if (argc < 2)
{
fprintf(stderr,"ERROR, no hay ningún puerto\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR de apertura de socket");
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
listen(sockfd,5);
clilen = sizeof(cli_addr);
if (newsockfd < 0)
error("ERROR en aceptar");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0)
error("ERROR al leer del socket");
n = write(newsockfd,"Tengo el mensaje",18);
if (n < 0)
error("ERROR al escribir en el socket");
close(newsockfd);
close(sockfd);
return 0;
}
- 37 -
Laboratorio de Arquitectura de Redes
SERVIDOR2.C
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void dostuff(int);
void error(const char *msg)
{
perror(msg);
exit(1);
}
if (argc < 2)
{
fprintf(stderr,"ERROR, no hay ningún puerto\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR de apertura de socket");
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1)
{
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR en aceptar");
pid = fork();
if (pid < 0)
error("ERROR en fork");
if (pid == 0)
{
close(sockfd);
dostuff(newsockfd);
exit(0);
}
else
close(newsockfd);
}
close(sockfd);
return 0;
}
/*Gestiona todas las comunicaciones una vez que la conexión ha sido establecida*/
- 38 -
Laboratorio de Arquitectura de Redes
int n;
char buffer[256];
bzero(buffer,256);
n = read(sock,buffer,255);
if (n < 0)
error("ERROR al leer del socket");
4.6 Bibliografía.
- 39 -
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: