Centro de Estudios NOVA – Cartagena 868-97-38-38

Anuncio
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Para poder realizar la conexión entre ambos programas son necesarias varias cosas:
Dirección IP del servidor.
Servicio que queremos crear / utilizar: Estos números son enteros normales y van
de 1 a 65535. Los número bajos, desde 1 a 1023 están reservados para servicios
Centro de Estudios NOVA – Cartagena
868-97-38-38
habituales de los sistemas operativos (www, ftp, mail, ping, etc). El resto están a
disposición del programador
EL SERVIDOR
•
Apertura de un socket, mediante la función socket(). Esta función devuelve un
descriptor de fichero normal, como puede devolverlo open(). La función socket() no
hace absolutamente nada, salvo devolvernos y preparar un descriptor de fichero que el
sistema posteriormente asociará a una conexión en red.
•
Avisar al sistema operativo de que hemos abierto un socket y queremos que asocie
nuestro programa a dicho socket. Se consigue mediante la función bind(). El sistema
todavía no atenderá a las conexiones de clientes, simplemente anota que cuando
empiece a hacerlo, tendrá que avisarnos a nosotros. Es en esta llamada cuando se
debe indicar el número de servicio al que se quiere atender.
•
Avisar al sistema de que comience a atender dicha conexión de red. Se consigue
mediante la función listen(). A partir de este momento el sistema operativo anotará la
conexión de cualquier cliente para pasárnosla cuando se lo pidamos. Si llegan clientes
más rápido de lo que somos capaces de atenderlos, el sistema operativo hace una
"cola" con ellos y nos los irá pasando según vayamos pidiéndolo.
•
Pedir y aceptar las conexiones de clientes al sistema operativo. Para ello hacemos
una llamada a la función accept(). Esta función le indica al sistema operativo que nos
dé al siguiente cliente de la cola. Si no hay clientes se quedará bloqueada hasta que
algún cliente se conecte.
•
Escribir y recibir datos del cliente, por medio de las funciones write() y read(), que
son exactamente las mismas que usamos para escribir o leer de un fichero.
Obviamente, tanto cliente como servidor deben saber qué datos esperan recibir, qué
datos deben enviar y en qué formato. Puedes ver cómo se pueden poner de acuerdo
en estos mensajes en el apartado de mensajes.
•
Cierre de la comunicación y del socket, por medio de la función close(), que es la
misma que sirve para cerrar un fichero.
EL CLIENTE
Los pasos que debe seguir un programa cliente son los siguientes:
•
Apertura de un socket, como el servidor, por medio de la función socket()
•
Solicitar conexión con el servidor por medio de la función connect(). Dicha función
quedará bloqueada hasta que el servidor acepte nuestra conexión o bien si no hay
servidor en el sitio indicado, saldrá dando un error. En esta llamada se debe facilitar la
dirección IP del servidor y el número de servicio que se desea.
•
Escribir y recibir datos del servidor por medio de las funciones write() y read().
•
Cerrar la comunicación por medio de close().
Centro de Estudios NOVA – Cartagena
868-97-38-38
sockfd = socket ( int dominio, int tipo, int protocolo );
sockfd
Es el descriptor de socket devuelto. Luego se utilizará para conectarse, recibir conexiones,
enviar y recibir datos, etc.
Retorna un descriptor de socket, que es tipo int.
Si hubo algún error, socket() retorna -1 y la variable global errno se establece con un valor
que indica el error que se produjo ( ver man 3 perror).
Dominio
El dominio de comunicación nos dice donde se encuentran los procesos que se van a
intercomunicar.
Si los procesos están en el mismo sistema, el dominio de comunicación será AF_UNIX, si
los procesos están en distintos sistemas y estos se hallan unidos mediante una red
TCP/IP, el dominio de comunicación será AF_INET.
Cabe aclarar que existen otros dominios de comunicación.
Los sockets no se han diseñado solamente para TCP/IP. La idea original fue que se usase
la misma interfaz también para distintas familias de protocolos.
En esta introducción solo trataremos el dominio AF_INET.
Algunos dominios:
•
•
•
AF_INET ( unidos mediante una red TCP/IP).
AF_UNIX (en el mismo sistema).
Otros dominios.
Para este tutor siempre será AF_INET.
tipo
Podrá ser SOCK_STREAM o SOCK_DGRAM o SOCK_RAW.
protocolo
0 (cero, selecciona el protocolo más apropiado).
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
El socket se crea sin nombre, debemos asignarle uno para poder recibir conexiones.
bind () se utiliza para darle un nombre al socket, osea una dirección IP y número de puerto
del host local por donde escuchará, al especificar una IP del host local le estamos diciendo
Centro de Estudios NOVA – Cartagena
868-97-38-38
por cual interfaz física escuchará (el sistema puede tener varias interfaces ethernet, ppp,
etc).
Es
necesario
llamar
a
bind()
cuando
se
está
programando
un
servidor.
Cuando se está programando un cliente generalmente no se utiliza esta función, el kernel
le asignará al socket la dirección IP y número de puerto disponible al llamar a ciertas
funciones, por ejemplo cuando llamamos a connect() para conectarnos con un sistema
remoto.
En el servidor es necesario llamar a bind() debido a que el número de puerto debe ser
conocido para que los clientes puedan conectarse. Por ejemplo si estamos programando
un servidor de telnet debemos llamar a bind() para asignarle al socket el puerto 23.
En la aplicación cliente se puede llamar a bind() y asignarle un número de puerto, pero no
es necesario porque nadie va a tratar ni podra conectarse con él.
•
sockfd :
Es el descriptor de socket devuelto por la función socket().
•
my_addr :
Es un puntero a una estuctura sockaddr que contiene la IP del host local y el
número de puerto que se va a asignar al socket.
(Mas abajo se detalla).
•
addrlen :
Debe ser establecido al tamaño de la estuctura sockaddr. sizeof(struct
sockaddr).
Ejemplo :
int Descriptor;
struct sockaddr_in Direccion;
struct servent* se = getservbyname("telnet", "tcp");
Descriptor = socket (AF_INET, SOCK_STREAM, 0);
if (Descriptor == -1)
printf ("Error\n");
Direccion.sin_family = AF_INET;
Direccion.sin_port = se->s_port;
Direccion.sin_addr.s_addr =INADDR_ANY;
if (bind (Descriptor,(struct sockaddr *)&Direccion, sizeof(Direccion))==-1){
printf ("Error\n");
}
La función getservbyname() recibe el nombre del servicio y si es "tcp" o "udp". Busca en el
fichero /etc/services y nos devuelve una estructura servent con lo que ha encontrado (NULL en
caso de no encontrar nada o de error). Una vez rellena la estructura servent, tenemos en el campo
s_port el número del puerto. Basta asignarlo a Direccion.sin_port.
Centro de Estudios NOVA – Cartagena
868-97-38-38
Estructuras.
struct sockaddr{
unsigned short sa_family; // AF_*
char sa_data[14]; // Direccion de protocolo.
};
almacena la dirección de protocolo para muchos tipos de protocolos.
sa_family puede ser AF_INET, AF_UNIX u otros dominios, para nuestro tutorial solo será
AF_INET.
sa_data contiene la dirección IP y número de puerto asignado al socket.
struct in_addr
{
unsigned long int s_addr; // Dirección local a la que se asigna el socket
}
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.
};
Se creó la esctuctura sockaddr_in para el caso de internet, para poder referenciar los
elementos de forma más fácil.
•
•
•
•
sin_family sera AF_INET. No debe convertirse a network byte order porque es solo
usado por el kernel y no es enviado por la red.
sin_port (número de puerto) deberá estar en network byte order, osea habrá que
usar htons().
sin_addr (dirección IP) deberá estar en network byte order, osea habrá que usar
htons().
sin_zero se utiliza para rellenar la estructura a la longuitud de sockaddr, debe estar
inicializada a cero con la función bzero(). Ver la página del manual
Los punteros a la estructura sockaddr_in deben ser precedidos con un cast tipo * struct
sockaddr antes de pasarlos como parámetros a funciones.
struct sockaddr_in mi_direc;
mi_direc.sin_family = AF_INET;
mi_direc.sin_port=htons(3490);
mi_direc.sin_addr.s_addr= =inet_addr(“10.12.110.57);
memset(&(mi_direc.sin_zero), ’\0’, 8 );
if (bind(Descriptor, (struct sockaddr *)&mi_direc, sizeof(struct sockaddr)) == -1) {
perror(“Asociando socket”);
exit(1);
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
Ordenación de bytes
„ Host byte order (little-endian): Byte menos significativo primero
„ Network byte order (big-endian): Byte más significativo primero
Todos los campos de socketaddr_in han de almacenarse en Network byte order
Network Byte Order & Host Byte Order
Cuando se estan enviando datos por algun tipo de red, estos deben de viajar en un
orden tal, que puedan ser leidos, por lo regular se convierten al tipo Network Byte
Order (Orden de Byte de Red), y cuando esos datos se procesan en un ordenador se
deben de convertir a Host Byte Order (Orden de Byte de Red). Esto se puede hacer
con funciones predefinidas de las API de Socket como son:
•
•
•
•
htons()
htonl()
ntohs()
ntohl()
-> ``Nodo a variable corta de Red''
-> ``Nodo a variable larga de Red''
-> ``Red a variable corta de Nodo''
-> ``Red a variable larga de Nodo''
int inet_aton(const char *cp, struct in_addr *inp);
inet_aton() convierte la dirección de Internet cp desde la notación estándar de números
y puntos a la representación binaria, y la guarda en la estructura a la que apunte inp.
Devuelve no-cero si la dirección es válida, cero si no.
struct sockaddr_in antelope;
inet_aton("10.0.0.1", &antelope.sin_addr); // store IP in antelope
in_addr_t inet_addr(const char *cp);
La función inet_addr() convierte la dirección de Internet cp desde la notación de
números y puntos a la de datos binarios en orden de bytes del ordenador local. Si la
entrada no es válida, devuelve INADDR_NONE (usualmente -1). Ésta es una interfaz
obsoleta a inet_aton, descrita anteriormente; es obsoleta porque -1 es una dirección
válida (255.255.255.255), e inet_aton porporciona una manera más clara para indicar
que ha ocurrido un error.
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = inet_addr("128.105.40.13");
Centro de Estudios NOVA – Cartagena
868-97-38-38
in_addr_t inet_network(const char *cp);
La función inet_network() extrae el número de red en orden de bytes de red desde la
dirección cp a la notación de números y puntos. Si la entrada es inválida, devuelve -1.
char *inet_ntoa(struct in_addr in);
La función inet_ntoa() convierte la dirección de Internet in dada en orden de bytes de
red a una cadena de caracteres en la notación estándar de números y puntos. La cadena
se devuelve en un búfer reservado estáticamente, que será sobreescrito en siguientes
llamadas.
// if added to the above inet_addr() code, this would print "128.105.40.13":
char *s = inet_ntoa(destAddr.sin_addr);
printf("dotted decimal address is %s\n", s);
struct in_addr inet_makeaddr(int net, int host);
La función inet_makeaddr() construye una dirección de Internet en orden de bytes de
red combinando el número de red net con la dirección local host en la red net, ambas en
orden de bytes de ordenador local.
in_addr_t inet_lnaof(struct in_addr in);
La función inet_lnaof() devuelve la parte del ordenador local de la dirección de Internet
in. La dirección de ordenador local se devuelve en orden de bytes de ordenador local.
in_addr_t inet_netof(struct in_addr in);
La función inet_netof() devuelve la parte de número de red de la dirección de Internet
in. El número de red se devuelve en orden de bytes de ordenador local.
int listen ( int sockfd, int backlog)
Se llama desde el servidor, habilita el socket para que pueda recibir conexiones.
Solo se aplica a sockets tipo SOCK_STREAM.
sockfd :
Es el descriptor de socket devuelto por la función socket() que será utilizado para recibir
conexiones.
backlog :
Es el número máximo de conexiones en la cola de entrada de conexiones. Las conexiones
entrantes quedan en estado de espera en esta cola hasta que se aceptan ( accept () ).
Así que la secuencia de funciones correctas seria la siguiente:
socket();
bind();
listen();
Centro de Estudios NOVA – Cartagena
868-97-38-38
…accept()…
int accept ( int sockfd, void *addr, int *addrlen)
Se utiliza en el servidor, con un socket habilitado para recibir conexiones ( listen() ). Esta
función retorna un nuevo descriptor de socket al recibir la conexión del cliente en el puerto
configurado.
La llamada a accept() no retornará hasta que se produce una conexión o es interrumpida
por una señal.
sockfd :
Es el descriptor de socket habilitado para recibir conexiones.
addr :
Puntero a una estructura sockadd_in. Aquí se almacenará informacion de la conexión
entrante. Se utiliza para determinar que host está llamando y desde qué número de puerto.
addrlen :
Debe ser establecido al tamaño de la estuctura sockaddr.
struct sockaddr_in remote_addr;
int addrlen;
new_sockfd = accept ( sockfd, &remote_addr, &addrlen);
close ( sockfd)
Finalizan la conexión del descriptor de socket. Despues de utilizar close, el socket queda
desabilitado para realizar lecturas o escrituras.
read (int sockfd, void *buff, int len)
Devuelve longitud de lo leído si lectura OK y -1 si no.
sockfd: descriptor de socket.
buff: direccionamiento al búfer de almacenamiento de la información.
Len: tamaño del búfer. Para su medición funciones sizeof() y strlen().
Usada por clientes y servidores.
write (int sockfd, void *buff, int len)
Devuelve longitud de lo escrito si escritura OK y -1 si no.
sockfd: descriptor de socket.
buff: direccionamiento al búfer de lectura de la información.
Len: tamaño del búfer. Para su medición funciones sizeof() y strlen().
Usada por clientes y servidores.
int connect(int sockfd, const struct sockaddr *serv_addr, int addrlen);
Centro de Estudios NOVA – Cartagena
868-97-38-38
La función connect solicita poder conectar el socket especificado por sockfd a
un socket remoto cuya direccíon y puerto se especifica en serv_addr. Los
parámetros son:
sockfd: Descriptor del socket creado con anterioridad.
serv_addr: Estructura de datos donde se especifica la dirección y puerto con el
que deseamos establecer la conexión.
addrlen: Longitud de la estructura de datos anterior.
La función devuelve el valor -1 si error o 0 si su llamada tiene éxito.
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";
exit(1);
}
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= htonl (INADDR_ANY);
servaddr.sin_port=htons (SERVER_PORT);
bzero(&servaddr, sizeof (servaddr));
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0){
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el cliente.
read (connfd, datos, strlen(datos)); //Se entiende que es lo primero que se hace.
write (connfd, respuesta, strlen(respuesta)); // Resuelve
close (connfd);
close (listenfd).
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n"; exit(1);
}
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons (SERVER_PORT);
bzero(&servaddr, sizeof (servaddr));
clilen=sizeof(servaddr);
connfd =connect(listenfd, (struct_sockaddr *) &servaddr, clilen); //Se inicia la conexión.
write (listenfd, peticion, strlen(peticion)); // Se realiza petición
read (listenfd respuesta strlen(respuesta)); //Se entiende que se recibe respuesta
close (listenfd).
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
EL SERVIDOR
Los pasos que debe seguir un programa servidor son los siguientes:
•
•
•
•
Abrir un socket con la función socket()
Asociar el socket a un puerto con bind()
Leer mensaje con recvfrom().
Responder mensaje con sendto()
EL CLIENTE
Los pasos que debe seguir un cliente son los siguientes:
•
•
•
Abrir un socket con socket()
Enviar mensaje al servidor con sendto()
Leer respuesta con recvfrom()
int recvfrom ( int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen )
sockfd : Descriptor socket por donde se recibirán los datos.
buf : Puntero a un buffer donde se almacenarán los datos recibidos.
len : Longitud del buffer buf.
flags : Ver la página del manual de recv(). Normalmente 0
from : Puntero a una estructura sockaddr que contiene la dirección IP y número de puerto del
host origen de los datos.
fromlen : Debe ser inicializado al tamaño de struct sockaddr ( sizeof (struct sockaddr) ).
Centro de Estudios NOVA – Cartagena
868-97-38-38
Si no hay datos a recibir en el socket , la llamada a recvfrom() no retorna (bloquea) hasta que
llegan datos, se puede establecer al socket como no bloqueante (ver: man 2 fcntl ) de manera
que cuando no hay datos para recibir la función retorna -1 y establece la variable
errno=EWOULDBLOCK. Más adelante se hablará de esto.
recvfrom() retorna el numero de bytes recibidos, igual que recv().
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)
sockfd : Descriptor socket por donde se enviarán los datos.
msg : Puntero a los datos a ser enviados.
len : Longitud de los datos en bytes.
flags : Leer : man 2 sendto. Normalmente 0.
to : Puntero a una estructura sockaddr que contiene la dirección IP y número de puerto destino.
tolen : Debe ser inicializado al tamaño de struct sockaddr
( sizeof (struct sockaddr) ).
sendto() retorna el número de bytes enviados, el cual puede ser menor que len, igual que en
send().
#define MAXLINE 256
#define SERVER_PORT 3000
main ( ) {
int sockfd;
struct sockaddr_in, server_addr, cli_addr;
int n;
socklen_t len;
char mess[MAXLINE];
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= htonl (INADDR_ANY);
servaddr.sin_port=htons (SERVER_PORT);
bzero(&servaddr, sizeof (servaddr));
if ((listenfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n"; exit(1);
}
if ((bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))<0) {
printf ("ERROR \n"; exit(1);
}
for ( ; ; ) {
clilen=sizeof(cliaddr);
n = recvfrom(listenfd, mess, MAXLINE, 0, (struct sockaddr *) &cliaddr, &clilen);
//Se recibe del cliente (sin establecimiento).
sendto (listenfd, mess, n, 0, (struct sockaddr *) &cliaddr, len);
//Se responde inmediatamente (ECO).
};
Centro de Estudios NOVA – Cartagena
868-97-38-38
close (listenfd).
};
#define MAXLINE 256
#define SERVER_PORT 3000
main (int argc, char* argv ) {
int sockfd;
struct sockaddr_in server_addr;
int n;
char sendline[MAXLINE], recvline[MAXLINE +1];
servaddr.sin_family= AF_INET;
inet_pton (AF_INET, argv[1], &serveraddr.sin_addr.s_addr);
servaddr.sin_port=htons (SERVER_PORT);
bzero(&servaddr, sizeof (servaddr));
if ((sockfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n"; exit(1);
}
while (fgets (sendline, MAXLINE,stdin) != NULL) {
sendto (sockfd, sendline, MAXLINE, 0, (struct_sockaddr *) &serverddr, sizeof(serveraddr));
//Se envía PAQUETE (ORIGINAL).
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
//Se recibe PAQUETE (ECO).
recvline [n]=‘\0’; //Terminar texto.
fputs (stdout, recvline, n+1); // Presentacion
};
close (listenfd)
};
int inet_pton(int af, const char *src, void *dst);
inet_pton - Crea una estructura de dirección de red
Esta función convierte la cadena de caracteres src en una estructura de dirección de red de la familia af , y copia la estructura de dirección de red a dst.
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Ejemplo 1: Conexión Cliente – Servidor. Envio de Mensaje de
Bienvenida indicando ip del servidor en el cliente.
SERVIDOR
/* Estos son los ficheros de cabecera usuales */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 3550 /* El puerto que será abierto */
#define BACKLOG 2 /* El número de conexiones permitidas */
main()
{
int fd, fd2; /* los ficheros descriptores */
struct sockaddr_in server; /* para la información de la dirección del servidor */
struct sockaddr_in client; /* para la información de la dirección del cliente */
int sin_size;
if ((fd=socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
printf("error en socket()\n");
exit(-1);
}
server.sin_family = AF_INET;
server.sin_port = htons(PORT); /* ?Recuerdas a htons() de la sección "Conversiones"? =) */
server.sin_addr.s_addr = INADDR_ANY; /* INADDR_ANY coloca nuestra dirección IP automáticamente
bzero(&(server.sin_zero),8); /* escribimos ceros en el reto de la estructura */
if(bind(fd,(struct sockaddr*)&server, sizeof(struct sockaddr))==-1) {
printf("error en bind() \n");
exit(-1);
}
if(listen(fd,BACKLOG) == -1) {
printf("error en listen()\n");
exit(-1);
}
while(1) {
sin_size=sizeof(struct sockaddr_in);
if ((fd2 = accept(fd,(struct sockaddr *)&client, &sin_size))==-1) {
printf("error en accept()\n");
exit(-1);
}
printf("Se obtuvo una conexión desde %s\n",inet_ntoa(client.sin_addr) ); /* mostrar la IP del cliente */
send(fd2,"Bienvenido a mi servidor.\n",22,0);
/* que envia el mensaje de bienvenida al cliente */
close(fd2);
/* cierra fd2 */
}
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
CLIENTE
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
/* netbd.h es necesitada por la estructura hostent ;-) */
#define PORT 3550
/* El Puerto Abierto del nodo remoto */
#define MAXDATASIZE 100 /* El número máximo de datos en bytes */
int main(int argc, char *argv[])
{
int fd, numbytes;
/* ficheros descriptores */
char buf[MAXDATASIZE]; /* en donde es almacenará el texto recibido */
struct hostent *he;
/* estructura que recibirá información sobre el nodo remoto */
struct sockaddr_in server;
/* información sobre la dirección del servidor */
if (argc !=2) {
/* esto es porque nuestro programa sólo necesitará Un argumento, (la IP) */
printf("Uso: %s <Dirección IP>\n",argv[0]);
exit(-1);
}
if ((he=gethostbyname(argv[1]))==NULL){
printf("gethostbyname() error\n");
exit(-1);
}
/* llamada a gethostbyname() */
if ((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){
printf("socket() error\n");
exit(-1);
}
/* llamada a socket() */
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
/* htons() es necesaria nuevamente ;-o */
server.sin_addr = *((struct in_addr *)he->h_addr); /*he->h_addr pasa la información de ``*he'' a "h_addr"
bzero(&(server.sin_zero),8);
if(connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr))==-1){
printf("connect() error\n");
exit(-1);
}
if ((numbytes=recv(fd,buf,MAXDATASIZE,0)) == -1){
printf("Error en recv() \n");
exit(-1);
}
/* llamada a connect() */
/* llamada a recv() */
buf[numbytes]='\0';
printf("Mensaje del Servidor: %s\n",buf);
/* muestra el mensaje de bienvenida del servidor =) */
close(fd); /* cerramos fd =) */
}
struct hostent *gethostbyname(const char *name);
struct hostent {
char
*h_name;
char
**h_aliases;
int
h_addrtype;
int
h_length;
/*
/*
/*
/*
official name of host */
alias list */
host address type */
length of address */
Centro de Estudios NOVA – Cartagena
char
**h_addr_list;
868-97-38-38
/* list of addresses */
}
struct hostent *hent;
hent = gethostbyname("www.foobar.net");
Programa cliente - servidor mediante sockets en C que en la parte
servidor me genera un numero aleatorio entre 1 y 100 y los clientes le
van escribiendo numero y el server comprueba si es mayor, menor o han
acertado...
Cliente.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int sockfd;
int len;
struct sockaddr_in address;
int result;
char numero;
char respuesta ='1';
//Inicializamos a 1 para que entre en el while
while(respuesta != '0') {
printf("Escribe un número entre 1 y 100: ");
scanf("%s",&numero);
//Para capturar caracteres utiliza "%s"
sockfd = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(9734);
len = sizeof(address);
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1) {
perror("oops: client3");
exit(1);
}
write(sockfd, &numero, 1);
read(sockfd, &respuesta, 1);
//printf("El cliente envia: %c\n",numero);
//printf("El cliente recibe: %c\n",respuesta);
if (respuesta == '1') {
printf("El número es mayor que %c\n", numero);
}
if (respuesta == '2') {
printf("El número es menor que %c\n", numero);
Centro de Estudios NOVA – Cartagena
}
close(sockfd);
}
printf("Has acertado!!! %c era el número!!!\n", numero);
exit(0);
}
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Servidor.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
int main() {
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
int aleatorio;
aleatorio = (1+rand()%100);
listen(server_sockfd, 5); //Punto A
while(1) {
char numero;
char respuesta;
printf("Servidor esperando peticiones...\n");
client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, &client_len);
read(client_sockfd, &numero, 1); //1 un numero de 1 cifra, 2 de 2 cifras
int numeroacomp = atoi(&numero);
if(aleatorio > numeroacomp) {
respuesta = '1'; //Si retorna 1 es que el valor es menor al número del cliente
}else{
if(aleatorio < numeroacomp){
respuesta = '2'; //Si retorna 2 es que el valor es mayor al núm del cliente
}else{
respuesta = '0'; //Si retorna 0 es que el valor es el correcto.
}
}
write(client_sockfd, &respuesta, 1);
close(client_sockfd);
}
exit(0);
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/select.h>
#define LISTENQ 5 // Tamaño de lista de clientes (4 también sería válido)
#define MAXCLIENT 5 // Número máximo de clientes
#define MSG_SIZE 1024 //Tamaño máximo del mensaje de texto
#define SERVER_PORT 3000
int main (int argc, char **argv) {
int i, maxi, maxfd, listenfd, connfd, sockfd, nready;
int client[MAXCLIENT];
ssize_t n;
fd_set rset, allset;
char msg[MSG_SIZE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n"; exit(1);
}
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(SERVER_PORT);
bzero(&servaddr, sizeof (servaddr));
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)<0) {
printf("ERROR \n"); exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de 5 clientes
maxfd=listenfd;
maxi=-1;
for (i=0; i<MAX_CLIENT;i++) //Inicialización del array de clientes
client[i]=-1;
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd,&allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //CLIENTE NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el
cliente
for (i=0; i<MAX_CLIENT; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd;
if (i>maxi)
maxi=i;
}
if (--nready<=0) continue; //Previsión para más de una interrupción
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
Centro de Estudios NOVA – Cartagena
} //Del bucle principal
} //Del main.
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
En la E/S síncrona, el proceso que realiza la petición
entra en un estado de espera hasta que ésta ha
finalizado. Un proceso que realiza una petición de E/S
asíncrona, realizar una petición de E/S al núcleo. Si
ésta es aceptada, el proceso continua realizando otras
tareas hasta que el núcleo le avisa de que la operación
ha finalizado. En ese momento el proceso interrumpe
la tarea que estaba realizando y procesa la información
obtenida de la operación de E/S.
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
#include <sys/select.h>
#include <sys/time.h>
int select (int numfds, fd_set* readfds, fd_set* writefds, fd_set* expectfds, struct timeval
*timeout);
Esta función permite controlar varios sockets (en general E/S) al mismo tiempo. Es capaz de indicar cuál de ellos
está listo, tiene una condición de excepción o si ha expirado un cierto tiempo sin ningún nuevo evento.
Select devuelve*:
-1, en caso de error.
N, el número de descriptores (sockets) que han saltado a la vez.
Campos que incluye:
Numfds:
Es un entero que indica el número de E/S a observar, pero de una forma especial. Hay que indicar cuántos, pero
contando desde el descriptor 0 e incluyendo todos los intermedios aunque no sean objeto de atención. Así, por
ejemplo, si no interesa el socket con valor 5, aunque sólo sea ese, habrá que indicar que interesan el 0, 1, 2,
3, 4 y 5, en total 6.
Como regla general, se indica que es el valor del descriptor mayor que es objeto de interés más uno. Por ejemplo,
5+1=6.
Los conjuntos “fd_set”:
Los conjuntos “fd_set” enumeran desde 0 hasta el valor máximo de descriptores que el sistema permite para una
aplicación, todos los descriptores de E/S posibles de esa aplicación. El número máximo de descriptores varía
según el sistema operativo pero en general 1024 es el número más utilizado.
Los valores de este conjunto aunque sean enteros, sólo pueden tomar dos posibles valores 0 ó 1.
La función select () utiliza tres conjuntos, readfds, writefds y exceptfds, para lectura entrada), escritura
salida) y excepciones (errores) respectivamente.
Manipulación de los conjuntos “fd_set”:
Existen cuatro funciones:
• void FD_ZERO(fd_set* set); //Pone todo el conjunto a 0.
• void FD_SET(int fd, fd_set* set); //Pone el descriptor número fd a 1.
• void FD_CLR(int fd, fd_set* set); //Pone el descriptor número fd a 0.
• int FD_ISSET(int fd, fd_set* set); //Comprueba el valor del descriptor fd. //Si está activo devuelve 1, si
no 0.
NO es posible modificar un conjunto fd_set con expresiones como set[fd].
Si un conjunto no interesa, se pone a NULL su posición en select.
Funcionamiento de los conjuntos “fd_set”:
Los conjuntos fd_set son parámetros de entrada y salida en la función select. Por ello, poseen valores distintos
antes y después de su ejecución.
Al hacer una llamada a select hay que especificar además del número de descriptores a atender, cuáles de ellos son y
por qué motivo (entrada, salida o excepción). Para ello, habrá que ACTIVAR (poner a 1) el descriptor correspondiente
del conjunto correspondiente. Por ejemplo, si interesa el descriptor 8 para lectura (read) habrá que ejecutar:
FD_SET(8, &readfds).
Centro de Estudios NOVA – Cartagena
868-97-38-38
Después de la ejecución de select el conjunto “readfds” tendrá activado (puesto a 1) el descriptor correspondiente
sólo si ha sido este descriptor el culpable del fin de la ejecución de select. En otro caso este descriptor se
devolverá inactivo (puesto a 0). Si se quiere comprobar este valor, se utiliza la función FD_ISSET. Select devolvería
como valor el número de descriptores que hubieran saltado.
El parámetro ”timeout”:
Es una estructura timeval (un tiempo descrito en seg. y usec.):
struct timeval {
long tv_sec;
long tv_usec;
};
Es un parámetro únicamente de entrada.
Podemos hacer que select espere siempre, (como si fuera múltiples read, write,…) para lo que lo ponemos a NULL.
Esperar un tiempo finito. Además, de activar descriptores, habrá que añadir un tiempo configurando “timeout”
con segundos y microsegundos.
Llamar a select pero de forma instantánea sin esperar a recibir nada (Ejecución tipo polling). En este caso, hay
que configurar los valores de timeout, ambos a 0.
*SELECT devuelve 0, si el tiempo configurado en timeout expira.
Programa que espere 2,5 segundos para leer algo del teclado y mostrarlo por pantalla mensaje
de acierto o fallo:
#include <sys/unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#define STDIN 0
main () {
struct timeval tv;
fd_set conjunto;
tv.tv_sec=2;
tv.tv_usec=500000; //Configuración del tiempo
FD_ZERO(&conjunto);
FD_SET(STDIN, &conjunto); //Configuración del descriptor a observar
select (STDIN+1, &conjunto, NULL,NULL,&tv); //Ejecución de select
if (FD_ISSET (STDIN, &conjunto)) printf (“Se ha pulsado tecla\n”); //Comprobación
else printf(“Tiempo expirado\n”);
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Condiciones para la activación de fd(I)
Ya se sabe que:
•
•
•
El descriptor fd hay que activarlo para que pueda ser atendido.
Si ese descriptor vence se devuelve un número >0 en select, y el descriptor queda activo.
Si ese descriptor no ha vencido, se quedará a 0.
La función select devuelve:
•
•
•
•
0, si vence el timeout.
N, un número mayor de 0 si vence algún descriptor e indica el número de descriptores vencidos vencidos.
-1, en caso de error.
Otra información relevante es la variable FD_SETSIZE que identifica el valor máximo que puede tomar un
descriptor (normalmente 1024).
siempre
Un socket está listo para leer siem
pre que se cumpla alguna de estas condiciones:
•
Exista algún dato a la entrada.
•
Se reciba un paquete de Fin de la Conexión en TCP. (Paquete FIN).
•
•
Un socket escuchando por conexiones (Listening socket). Cuando se realiza accept, si llega un paquete SYN
no se cerrará.
Hay un error. No existe bloqueo y read o select no se bloquean devolviendo -1.
Un socket está listo para escribir siempre que se cumpla alguna de estas condiciones:
•
•
Exista posibilidad de mandar datos al destino.
Cuando se quiere mandar un paquete de desconexión (Paquete FIN) en un cierre a dos
bandas y el otro extremo era el que había comenzado la desconexión.
Centro de Estudios NOVA – Cartagena
•
868-97-38-38
Hay un error. No existe bloqueo y write o select no se bloquean devolviendo -1.
Centro de Estudios NOVA – Cartagena
868-97-38-38
En Resumen
Conclusiones para la activación de fd
UN SOCKET (una E/S) NO SE PUEDE MARCAR A LA VEZ, SIMULTÁNEAMENTE, PARA ENTRADA Y SALIDA.
La razones son:
• En TCP, se realiza o bien read o bien write nunca los dos simultáneamente.
• EN UDP, siempre es escribible (sendto es siempre posible), y no se realiza a la vez de recvfrom
Centro de Estudios NOVA – Cartagena
868-97-38-38
Concurrencia con select.
En TCP, el proceso consiste en crear múltiples procesos servidores hijos, individuales para cada uno de los clientes.
En general, este proceso es muy ineficiente. Se deben duplicar pilas, recursos, etc.
La concurrencia buscada se basa en los siguientes principios:
• No se pueden multiplicar recursos (como con fork).
• El funcionamiento ha de ser concurrente (al modo de multiplexación de procesos).
• Se basa en el funcionamiento de select en donde:
o Todos los descriptores se puede agrupar en conjuntos (fd_set).
o Select encapsula el funcionamiento (E/S) de read, write, accept, close etc
o Los descriptores se pueden agrupar no solo en conjuntos sino también en arrays (de enteros).
Se definen:
• Servidor concurrente, aquel que tiene un puerto abierto esperando por conexiones de clientes, (listening socket)
y disponibilidad para aceptar n conexiones de clientes:
•
Conjunto de descriptores (fd_set) que indique en select los puertos a los que se presta atención:
•
Un array de descriptores que relacione la posición de conexión de un cliente con su descriptor:
•
•
•
•
El descriptor correspondiente al descriptor del “listening socket” ha de estar siempre abierto, por lo que su
descriptor (sockfd) estará siempre a 1 en el conjunto rset:
Cada nuevo cliente produce dos entradas. Primero una modificación del conjunto de descritpres (rset) poniendo a
1 el descriptor correspondiente. Segundo una nuevo valor en client[ ] donde aparece una nueva valor que será el
del descriptor, incorporándose en la primera posición libre:
Por ejemplo un segundo nuevo cliente, se añade de forma secuencial:
Un cierre de uno de los clientes, se transforma en un modificación, desactivandolo, en su posición del conjunto de
descriptores (rset) e inicializando su posición dentro del array de descriptores (client[ ]):
Centro de Estudios NOVA – Cartagena
868-97-38-38
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 5999 //Dato
#define SERVIDOR “192.168.5.254”
int main (int argc, char **argv) {
int maxi, maxfd, listenfd, connfd, nready;
int client [N]; // Array de descriptores de cada línea
ssize_t len;
fd_set rset, allset;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";
exit(1);
}
bzero(&servaddr, sizeof ( servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd; //Mayor de todos los descriptores
maxi=-1; //Máxima posición del array de descriptores ocupada
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset) { //cliente NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta usu.
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD SET(connfd &allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
} //Del bucle principal
} //Del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
for (i=0; i<=maxi; i++){
if ((sockfd =client[i])<0) // Se van a comprobar todos los CLIENTES
continue;
if (FD_ISSET(sockfd, &rset)) //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd, msg_rec,MSG_SIZE))==0){ //Proceso cierre en caso de FIN DEL CLIENTE.
close(sockfd);
FD_CLR(sockfd,&allset);
client[i]=-1;
if (maxi==i) --maxi;
if (sockfd==maxfd) maxfd=sockfd;}
if (--nready<=0) break;
else continue;
}else {
//ESPACIO DE CÓDIGO DE ATENCIÓN A CLIENTE CONECTADO
}
} //Fin del bucle for de atención a los clients
Centro de Estudios NOVA – Cartagena
868-97-38-38
Se desea desarrollar un servidor como el de la práctica 3 de LSC (Chat) pero sobre conexiones fiables. Por ello, deberá ofrecer:
Multiplexación E/S para al menos 5 clientes simultáneos (Utilizar obligatoriamente la función select).
Las conexiones serán fiables, previamente establecidas. Los mensajes que se pueden transmitir no superarán los 1024 bytes.
El servidor se encargará de escuchar todos los clientes al mismo tiempo. A continuación de aquel que “interrumpa”, leerá el paquete,
el cual retransmitirá al resto de los clientes.
B) Escriba el código del servidor referente a la gestión del socket: apertura y admisión de conexiones de los clientes (30%).
C) Escriba el código para conseguir el funcionamiento del servidor para dos clientes (25%).
D) Extienda el código para que gestione los 5 clientes (Evitar usar código redundante) (20%).
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/select.h>
#define LISTENQ 5 // Tamaño de lista de clientes (4 también sería válido)
#define MAXCLIENT 5 // Número máximo de clientes
#define MSG_SIZE 1024 //Tamaño máximo del mensaje de texto
#define SERVER_PORT 3000
int main (int argc, char **argv) {
int i, maxi, maxfd, listenfd, connfd, sockfd, nready;
int client[MAXCLIENT];
ssize_t n;
fd_set rset, allset;
char msg[MSG_SIZE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de 5 clientes
maxfd=listenfd;
maxi=-1;
for (i=0; i<MAX_CLIENT;i++) //Inicialización del array de clientes
client[i]=-1;
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd,&allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //CLIENTE NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta cli
for (i=0; i<MAX_CLIENT; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) maxfd=connfd;
if (i>maxi) maxi=i;
Centro de Estudios NOVA – Cartagena
868-97-38-38
}
if (--nready<=0) continue; //Previsión para más de una interrupción
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
} //Del bucle principal
} //Del main.
Centro de Estudios NOVA – Cartagena
868-97-38-38
C) /* CÓDIGO SERVIDOR PARA DOS CLIENTES*/
i=0;
while(i<=maxi && client[i]<0){
i++;
}
if(i<=maxi) sockfd1=client[i];
j=i;
while(j<=maxi && client[j]<0){
j++;
}
if(j<=maxi) sockfd2=client[j];
if (FD_ISSET(sockfd1, &rset))
if ((n=read(sockfd1, msg,1024))==0){ //Para el caso de FIN del primero de ellos
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
}
else
write (sockfd2, msg, 1024); //Se transmite al segundo cliente
if (FD_ISSET(sockfd2, &rset))
if ((n=read(sockfd2, msg,1024))==0){ //Para el caso de FIN del segundo
close(sockfd1);
FD_CLR(sockfd2,&allset);
client[j]=-1;
}
else
write (sockfd1, msg, 1024); //Se transmite al primero
D) /*Código para n=5 clientes*/
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todos los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Para el caso de que uno de ellos escriba algo
if ((n=read(sockfd1, msg,1024))==0){ //Proceso de cierre en caso de FIN
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
}else{
j=0;
while(j<=maxi){ //Envío al resto de clientes
if(sockfd2=client[j]>0)
if(client[j]!=sockfd1) //NO se transmite al origen
write (sockfd2, msg, 1024);
j++;
}
}
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
Se desea desarrollar un servidor de un servicio de transmisión de datos bancarios. En concreto, el servicio consiste en recoger el
número secreto que el cliente teclea en un cajero automático y comprobar que efectivamente es ese el número secreto de ese cliente.
Así, el servidor recogerá la información de número de tarjeta y del número secreto de cada uno de los cajeros y enviará la
confirmación con un “RIGHT” o “WRONG” según sea el número correcto o no. La lista de números secretos se pasa al servidor (no hay
que programarla), donde en cada elemento de la lista se identifica el número de tarjeta y el número secreto según la estructura “struct
tarjeta”. Las conexiones se realizan sobre comunicaciones UDP ya que la transmisión de información debe ser rápida. Sin embargo, se
debe asegurar que el mensaje de respuesta “RIGHT” o “WRONG” sí que llegue por lo que se debe implementar un sistema de
confirmación de recepción de dicho mensaje. Se debe esperar la confirmación no más de 5 segundos utilizando para ello la función
“select”.
Se supone que cada cajero es un único proceso que transmite los números de tarjeta y secreto en un único mensaje y
que recibe el mensaje RIGHT o WRONG. Si recibe dicho mensaje envía un mensaje indicando “OK”. En el caso de no recibirlo en un
periodo de tiempo de 8 segundos volverá a ejecutar la petición.
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <time.h>
#include <sys/select.h>
#define MSG_SIZE 12 //Tamaño máximo del mensaje de texto
#define SERVER_PORT 3000
#define RIGHT 1
#define WRONG 0
int main (int argc, char **argv) {
struct tarjeta {
long numero_tarjeta;
int numero_secreto;
struct tarjeta* siguiente;
};
struct tarjeta2 {
long numero_tarjeta2;
int numero_secreto2;
};
struct lista {
struct tarjeta *primera;
struct tarjeta *ultima;
};
struct lista lista_tarjeta;
struct tarjeta * comparativa;
struct tarjeta2 * datos_cajero;
int n, maxfd, sockfd, respuesta;
char *confirmacion;
int data, envio;
fd_set rset;
socklen_t clilen;
struct sockaddr_in servaddr;
struct sockaddr client;
struct timeval tiempo_espera;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= htonl (INADDR_ANY);
servaddr.sin_port= htons (SERVER_PORT);
if (bind (sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in))<0) {
printf("ERROR \n");
exit(1);
}
Centro de Estudios NOVA – Cartagena
// Código reservado para el funcionamiento del servidor
close (sockfd);
}
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
C) Funcionamiento del servidor.
for (;;){
data=recvfrom (sockfd, datos_cajero, MSG_SIZE, 0, client, cli_len); //Preparado recibir datos
if (data != sizeof (struct tarjeta)) {
printf (“ERROR \n);
continue;
} //Se comprueban datos son correctos.
Comparativa=lista_tarjeta->primera; //Inicialización de variables
respuesta = WRONG;
FD_SET(sockfd, &rset);
tiempo_espera.tv_sec= 5;
tiempo_espera.tv_usec=0;
while (comparativa!= NULL) { //Comparar valores
if ( comparativa->numero_tarjeta == datos_cajero->numero_tarjeta2){
if (comparativa ->numero_secreto==datos_cajero->numero_secreto2)respuesta =RIGHT;
else respuesta = WRONG;
break;
}
comparativa=comparativa->siguiente;
}
if (respuesta==RIGHT) //Se envía respuesta.
envio= sendto (sockfd, “RIGHT”, strlen(“RIGHT”), 0, client, sizeof(&clilen));
else
envio= sendto (sockfd, “WRONG”, strlen(“WRONG”), 0, client, sizeof(&clilen));
nready= select (maxfd+1, &rset, NULL, NULL, &tiempo_espera); // Proceso de confirmación . Se espera.
if (nready==0) continue; // Se termina por Timeout
if (FD_ISSET(sockfd, &rset)) { //Llega respuesta. Se confirma que es buena.
recvfrom (sockfd, confirmacion, 2, 0, client, cli_len);
if (strcmp(&confirmacion, “OK”)) continue;
else{
printf (“ERROR”); continue;
}
} // Fin proceso de confirmación
} // Fin del servidor
Centro de Estudios NOVA – Cartagena
868-97-38-38
25 DE JUNIO DE 2004
Se desea desarrollar el software de comunicaciones de un cliente de un servicio de cajero automático bancario. En
concreto, el servicio consiste en recoger el número de tarjeta que se obtiene al introducir la tarjeta del usuario y
el número secreto que el cliente teclea en el teclado. Para cada evento se asigna estáticamente un descriptor que se
usarán para leer los datos de entrada (número de tarjeta y número secreto). A continuación se deberá enviar al
servidor esa información. La información se transmite a través de un socket UDP que conectará con el servidor y le
transmitirá los datos a través de una estructura “struct tarjeta2”. El cajero automático deberá quedar bloqueado a
través de la función “select” durante 8 segundos esperando la respuesta “RIGHT” o ”WRONG” dependiendo de si el número
secreto es correcto o no. En caso de que no llegue dicha respuesta o se reciba errónea se volverá a realizar la
petición.
Otra información:
#define teclado 0
#define numerotarjeta 3
#define servidor “192.168.5.254”
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <time.h>
#include <sys/select.h>
#define TECLADO 0 //descriptor correspondiente a la entrada estándar.
#define NUMEROTARJETA 3 //descriptor correspondiente a la entrada del lector de la tarjeta de crédito.
#define SERVER_PORT 3000 //Número de puerto (indiferente).
#define SERVIDOR “192.168.5.354” // Servidor del servicio de datos bancarios
int main (int argc, char **argv) {
struct tarjeta2 {
long numero_tarjeta;
int numero_secreto;
};
struct tarjeta2 * datos_envio;
char * respuesta;
long *numero_tarjeta_in;
int *numero_secreto_in;
int n_ready;
fd_set rset;
socklen_t servlen;
struct sockaddr_in servaddr;
struct timeval tiempo_espera;
if (read (NUMEROTARJETA, numero_tarjeta_in, sizeof(numero_tarjeta_in)) < sizeof (long)) {
printf (“Error, tarjeta no válida\n “);
exit(1);
}
if (read (TECLADO, numero_secreto_in, sizeof(numero_secreto_in)) < sizeof(int)) {
printf (“Error, numero secreto incorrecto \n”);
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf ("ERROR \n"; exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port= htons (SERVER_PORT);
datos_envio-> numero_tarjeta= *numero_tarjeta_in;
datos_envio->numero_secreto= *numero_secreto_in;
for (;;){
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (sendto (sockfd, datos_envio, sizeof (struct tarjeta2),0, (struct sockaddr *) &servaddr,
sizeof(struct sockaddr)) <0) {
printf (“Servidor fuera de servicio, espere mejor ocasión \n”);
exit (1);
}
tiempo_espera.tv_sec= 8;
tiempo_espera.tv_usec=0;
FD_ZERO (&rset);
FD_SET(sockfd, &rset);
nbytes=strlen(“RIGHT”);
nready= select (maxfd+1, &rset, NULL, NULL, &tiempo_espera);
if (n_ready<0) {
printf (“Error de datos, no se puede atender su petición\n”);
exit(1);
}
else
if (n_ready==0) {
printf (“Servidor ocupado. Espere una nueva petición \n”);
continue;
}
if (FD_ISSET (sockfd, &rset)) {
if (recvfrom (sockfd, respuesta, nbytes, 0, (struct sockaddr *) &servaddr,
servlen) < nbytes){
printf (“Error, en datos de servidor\n”);
continue;
}
if (strcmp (respuesta, “RIGHT”)==0 || strcmp (respuesta, “WRONG”)==0) {
printf (“Servidor activo, llamada correcta. \n”);
break;
}else{
continue;
} //Se recibe algo pero no es lo esperado
}
}
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
18 DE DICIEMBRE DE 2004
Se diseña un servidor central concurrente TCP destinado al control automático de una central de envasado de zumos
naturales. Como existen diversos tipos de zumos (N) existen N líneas de producción. El sistema se detalla en el gráfico:
Cada operario tiene acceso al control sobre todas y cada una de las líneas de producción según le convenga en cada instante. Por tanto el
servidor debe:
− Gestionar las conexiones (sockets) permanentes con las líneas. Éstas se conectan inmediatamente al arrancar el sistema y no se
desconectan salvo error o cierre. Habrá un total de n conexiones de ese tipo. Su conexión no forma parte del problema. Asignar el
descriptor de cada línea a n+3 siendo n el número de línea, entre 1 y N (p. ej. Para la línea 1 su descriptor es 4, para la línea 2, el
descriptor es 5, etc.).
− Aceptar y gestionar las conexiones de los operarios, hasta un total de 4.
− Recoger las órdenes de los operarios, transmitírselas a las líneas, esperar por las respuestas de las líneas y reenviarlas al operario
que las solicitó (de forma concurrente).
Se pide:
A) Flujograma de funcionamiento del servidor. Visualiza la gestión de conexiones concurrentes y el servicio ofrecido a los operarios.
Supóngase respuestas de las líneas inmediatas. (20%)
B) Código del arranque del servidor concurrente y establecimiento de la conexión de los operarios. Recuerde que además hay N
conexiones establecidas para las líneas (éstas no hay que conectarlas). (20%)
C) Servicio a los usuarios. Código del programa necesario para que los usuarios puedan solicitar y recibir información de las líneas
que le interesen (sólo de una por petición). Suponer que las respuestas de las líneas se producen de forma inmediata. (25%)
D) Considerar ahora que el tiempo de respuestas de las líneas es elevado. Modificar el código para que el servidor siga funcionando
de forma concurrente a pesar de este problema, es decir, que el servidor no se bloquee esperando las respuestas de las líneas. No se limita
el tiempo de respuesta de las líneas y cada línea acepta sólo una petición simultánea. (25%)
E) Código de desconexión de operarios propio de un servidor concurrente. (10%)
Otra información:
#define N 20
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define PUERTO 3000
#define SERVIDOR “192.168.5.254”
NOTAS IMPORTANTES:
1.- Los mensajes que transmite el operario, se pueden retransmitir a las líneas de productos tal y como llegan. Idem para las
respuestas de las líneas. Se consideran mensajes de texto.
2.- Antes de transmitir una orden, el operario envía un “int” identificando la línea (n) a la que se está refiriendo. Mientras no cambie,
todos los mensajes de ese operario se refieren a la última línea identificada. El envío del número de línea y del mensaje se realiza de
forma consecutiva e inmediata (no es necesario ocuparse de la concurrencia en este período).
3.- NO es necesario incluir las macros y librerías habituales en el código de la solución.
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/select.h>
#define LISTENQ 4 // Tamaño de lista de entrada de conexiones. Como no se indica nada se asigna 4.
#define MAXOPERARIO 4 // Número máximo de operarios
#define MSG_SIZE 1024 // Tamaño máximo del mensaje de texto, válido para mensajes de operarios y de
// líneas.
#define SERVER_PORT 3000
#define SERVIDOR “192.168.5.254”
#define N 20
int main (int argc, char **argv) {
Centro de Estudios NOVA – Cartagena
868-97-38-38
int i, maxi, maxfd, listenfd, connfd, sockfd, nready;
int *linea_solicitada;
int client[MAXOPERARIO]; // Array de Operarios
int client2[MAXOPERARIO]; //Sockets (o línea) con los que se comunica cada operario.
int operario[N]; //Necesario para saber a cada línea quién le ha preguntado
int linea[N]; // Necesario para asignar a cada línea un descriptor. Se podría hacer: (descriptor =n+3);
ssize_t len;
fd_set rset, allset;
char msg_env[MSG_SIZE];
char * msg_rec;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr=htonl (INADDR_ANY); // o bien servaddr.sin_addr.s_addr = inet_addr(SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los 4 posibles operarios.
maxfd=listenfd;
maxi=-1;
for (i=0; i<MAXOPERARIO;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
client2[i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for (i=0; i<N; i++)
linea[i]=i+4; //Se asigna descriptor a cada línea, n+3 (como el array empieza en 0 => i+4)
maxfd=N+3; //Se supone que las 20 líneas se han conectado ya, luego maxfd es igual al último descriptor.
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //OPERARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el operario
for (i=0; i<MAXOPERARIO; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
}
if (--nready<=0) continue; //Previsión para más de una interrupción
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
} //Del bucle principal
Centro de Estudios NOVA – Cartagena
868-97-38-38
-----------------------
for (i=0; i<=maxi; i++){
if ((sockfd1=client[i])<0) // Se van a comprobar todos los OPERARIOS
continue;
if (FD_ISSET(sockfd1, &rset)) //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))==0){
//Proceso de cierre en caso de FIN DEL OPERARIO (APARTADO E)
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
client2[i]=-1;
if (maxi==i) --maxi;
}
if (--nready<=0) continue;
else break;
else{
if (len==sizeof(int)) { // Se recibe el número de línea al que se quiere dirigir
linea_solicitada=(int *) &msg_rec;
// Se pasa a apuntar en formato int. Ahora ya se puede dirigir el mensaje.
client2[i]=linea[*linea_solicitada-1];
// Socket de la línea que quiere el operario se guarda en client2.
len=read(sockfd1, msg_rec, MSG_SIZE);
// Se lee el mensaje del operario. Se puede hacer control
} // de errores.
write (client2[i], msg_rec, len+1); //Se envía el mensaje a la línea.
/* Las dos próximas líneas no se aplican en el apartado D */
len=read(client2[i], msg_env, MSG_SIZE); //Esperamos recibir el mensaje de la línea.
write(sockfd1, msg_env, len+1); //Envíamos mensaje al operario.
} //FIN del servicio al operario
if (--nready<=0) continue; //Previsión para más de un operario simultáneo.
} // Fin del for de atención a los usuarios
------------------------------------for (i=0; i<=maxi; i++){
if ((sockfd1=client[i])<0) // Se van a comprobar todos los OPERARIOS
continue;
if (FD_ISSET(sockfd1, &rset)) //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))==0){
//Proceso de cierre en caso de FIN DEL OPERARIO (APARTADO E)
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
client2[i]=-1;
if (maxi==i) --maxi;
}
if (--nready<=0) continue;
else break;
else{
if (len==sizeof(int)) {
// Se recibe el número de línea al que se quiere dirigir
client2[i]=linea[*linea_solicitada-1];
// Socket de la línea que quiere el operario se guarda en client2.
len=read(sockfd1, msg_rec, MSG_SIZE);
// Se lee el mensaje del operario. Se puede hacer control
} // de errores.
write (client2[i], msg_rec, len+1); //Se envía el mensaje a la línea.
if (operario[client2[i]] < 0) //Por si la línea estuviera ya ocupada.
operario[client2[i]-4]=sockfd1;
// actualización del array donde se guardan los operarios que solicitaron servicio para saber quién mandó el mensaje.
Centro de Estudios NOVA – Cartagena
868-97-38-38
FD_SET(client2[i], &allset);
// Para que el descriptor del socket de esa línea pase a ser atendido.
/* ATENCIÓN A LAS LÍNEAS */
for (j=0; j<N; j++){
if ((sockfd1 =linea[j])<0) // Se van a comprobar todos las LÍNEAS
continue;
if (FD_ISSET(sockfd1, &rset))
//Para el caso de que uno de las líneas haya mandado algo
if ((len=read(sockfd1, msg_rec, MSG_SIZE))==0){ //Se leen los datos
close(sockfd1);
FD_CLR(sockfd1,&allset);
linea[j]=-1; //Proceso de cierre en caso de FIN DE LÍNEA (APARTADO E)
}else{
write (operario[j], msg_rec, len+1);
FD_CLR(sockfd1,&allset);
}
}
} //FIN del servicio al operario
if (--nready<=0) continue; //Previsión para más de un operario simultáneo.
} // Fin del for de atención a los usuarios
18 DE DICIEMBRE DE 2004
Se diseña un único servidor central concurrente TCP (sin generar nuevos procesos) destinado al control de la automatización de una
empresa química. Como existen diversas máquinas para su control (N) existen N conexiones, una para cada máquina. El sistema se
detalla en el gráfico:
Cada operario tiene acceso al control sobre todas y cada una de las máquinas según le convenga en cada instante.
Por tanto el servidor debe:
− Aceptar y gestionar las conexiones (sockets) con las máquinas. Éstas se conectan independientemente entre ellas y lo hacen una
vez hayan arrancado y se hayan configurado por sí mismas. Habrá un total de N conexiones de ese tipo. Sus descriptores van desde
4 hasta N+3.
− Aceptar y gestionar las conexiones de los operarios, hasta un total de P conexiones de operarios. A los operarios se le asigna
descriptor desde N+4 en adelante.
− Recoger las órdenes de los operarios, transmitírselas a las máquinas, esperar por las respuestas de éstas y reenviarlas al operario
que las solicitó (de forma concurrente).
− La transmisión de las órdenes a las máquinas se realiza inmediatamente recibidas desde el operario. Las respuestas de las
máquinas se reciben también de forma inmediata y se envían a su vez a los operarios ordenantes.
Se pide:
A) Flujograma de funcionamiento del servidor. Visualiza la gestión de conexiones concurrentes tanto de máquinas como de operarios
y el servicio ofrecido a los operarios. Se valorará el utilizar un cronograma de apoyo sobre el funcionamiento del servidor. El proceso
se realiza “estáticamente” para el servidor, es decir, que primero se conectan todas las máquinas (N) y luego todos los operario (P).
Suponer máquinas desocupadas cuando se les envía una petición. (20%)
B) Código del arranque del servidor concurrente y establecimiento de la conexión de las máquinas y de los operarios.(20%)
C) Código de servicio a los usuarios. Código del programa necesario para que los usuarios puedan enviar información al servidor y
que éste lo reenvíe a las máquinas (lo referente a recibir el número de máquina y mensaje y transmitir dicho mensaje a la máquina).
Incluye también el código para que las máquinas respondan de forma inmediata y reenvío de las respuestas a los operarios. Controlar
el caso de que la máquina no esté conectada. Supóngase que las máquinas están desocupadas. (25%)
D) Flujograma de funcionamiento en el caso de que la conexión de las líneas y operarios se haga de forma aleatoria. En este caso, el
primer mensaje después de la conexión de cada uno de los extremos es OPERARIO o MAQUINA según sea operario o máquina
seguido del número de operario o máquina correspondiente. De esta formas los descriptores de máquinas y operarios no son
consecutivos y pasan a estar mezclados. Destaca claramente el control de este hecho. (15%)
E) Modifica el código de los apartados B y C cuando la conexión de los operarios y máquinas se realice de forma aleatoria. Presta
especial atención al establecimiento de la conexión, distinción entre máquinas y operarios y la existencia de las conexiones con
operarios y máquinas cuando se realiza el servicio. (20%)
Centro de Estudios NOVA – Cartagena
868-97-38-38
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes de operarios a máquinas y viceversa
#define PUERTO 3000 //Puerto del servicio
#define SERVIDOR “192.168.5.254” //Dirección IP de la máquina servidora
NOTAS IMPORTANTES:
1.- Los mensajes que transmite el operario, se pueden retransmitir a las máquinas tal y como llegan. Idem para las respuestas de las
máquinas. Se consideran mensajes de texto.
2.- Antes de transmitir una orden, el operario envía un “int” identificando la máquina (n) a la que se está refiriendo. Por simplicidad, el
operario siempre envía el número de máquina antes de una orden. El envío del número de máquina y del mensaje se realiza de forma
consecutiva e inmediata en mensajes distintos (no es necesario ocuparse de la concurrencia en este período).
3.- Cada operario sólo puede enviar un mensaje cada vez y esperar una sola respuesta.
4.- NO es necesario incluir las macros y librerías habituales en el código de la solución.
5.- P+N<1020.
Centro de Estudios NOVA – Cartagena
868-97-38-38
#define P //Número máximo de operarios
#define N // Número de máquinas
#define LISTENQ P+N // Tamaño de lista de entrada de conexiones. Libre para aceptarlos todos a la vez
#define MAXOPERARIO P // Número máximo de operarios
#define MSG_SIZE 1024 // Tamaño máximo del mensaje de texto, válido para mensajes de operarios y de máquinas.
#define SERVER_PORT 3000
#define SERVIDOR “192.168.5.254”
int main (int argc, char **argv) {
int i, maxi, maxfd, listenfd, connfd, sockfd, nready, sockfd1;
int *maq_solicitada;
int conexion_maquinas_completo=0;
int client[MAXOPERARIO]; //Array de descriptores de los operarios.
int machine[N]; //Array de descriptores de las máquinas.
int client2[MAXOPERARIO]; //Sockets (o máquina) con los que se comunica cada operario.
int operario[N]; //Necesario para saber a cada máquina quién le ha preguntado
ssize_t len;
fd_set rset, allset, writeset, writeallset;
char msg_env[MSG_SIZE];
char * msg_rec[P]; //Array de mensajes recibidos de cada operario.
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0 || (listenfd>2)) {
printf ("ERROR \n";
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr = inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in))<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los 4 posibles operarios.
maxfd=listenfd;
maxi=-1;
for (i=0; i<MAXOPERARIO;i++){ //Inicialización de los arrays referentes a los operarios
client[i]=-1;
client2[i]=-1;
}
for (i=0; i<N;i++){ //Inicialización de los arrays de las maquinas
machine[i]=-1;
operario[i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
maxfd=listenfd; //La única conectada
for(;;){
rset=allset;
nready=select(maxfd+1,&rset,NULL,NULL,NULL);
if (FD_ISSET(listenfd,&rset)){
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen);
if (!conexion_maquina_completo) {//Si es máquina
for (i=0; i<N; i++)//Actualización del array de máquinas
if (machine[i]<0) {
machine[i]=connfd;
break;
Centro de Estudios NOVA – Cartagena
868-97-38-38
}//No se actualiza el conjunto allset porque no es necesario.
if (i==N) conexion_maquina_completo=1;
//Actualización fin de conexión de máquinas
if (i>maxi) maxi=i;
}else {// Es un operario
for (i=0; i<MAXOPERARIO; i++)//Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset);//Actualización del conjunto de descriptores
if (connfd > maxfd)//Actualización de variables
maxfd=connfd;// Los operarios van después de las máquinas
if ((i+N)>maxi) maxi=i+N;
}
if (--nready<=0) continue;//Previsión para más de una interrupción
}
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
} //Del bucle principal
} //Del main.
C) /* CÓDIGO SERVIDOR PARA ATENDER LOS MENSAJES DE CADA OPERARIO*/
for (i=N+3; i<=maxi; i++){
// Se van a comprobar todos los OPERARIOS
if ((sockfd1 =client[i-N-3])<0)
continue;
if (FD_ISSET(sockfd1, &rset)){
//Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec, MSG_SIZE))>0){
if (len==sizeof(int)) {
maq_solicitada=(int *) &msg_rec;
client2[i]=machine[*maq_solicitada-1];
if (machine[*maq_solicitada-1]<0) {
--nready;
continue;
}
len=read(sockfd1, msg_rec, MSG_SIZE);
write (client2[i], msg_rec, len+1); //TX Mensaje.
len=read(client2[i], msg_env, MSG_SIZE);
write(sockfd1, msg_env, len+1);
}
if (--nready<=0) continue;
}
}
}
D)
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL , NULL, NULL); //Bloqueo en espera de entradas o salidas
if ( FD_ISSET(listenfd, &rset){ //OPERARIO NUEVO o Máquina nueva
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta la conexión
for (i=0; i<N; i++)//Actualización del array de máquinas
if (conexiones[i]<0) {
conexiones[i]=connfd;
break;
}//No se actualiza el conjunto allset porque no es necesario.
if (i==N+P) conexion =1; //Actualización fin de conexión posibles
if (i>maxi) maxi=i;
FD_SET(connfd,&allset);
if (connfd > maxfd)
maxfd=connfd;
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (--nready<=0) continue;
}
for (i=0; i<=maxi; i++){
if ((sockfd1 =conexiones[i])<0)
continue;
//Actualización del conjunto de descriptores
//Actualización de variables
// Los operarios van después de las máquinas
//Previsión para más de una interrupción
// Se van a comprobar todos los OPERARIOS
if (FD_ISSET(sockfd1, &rset)){//Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec, MSG_SIZE))==sizeof (“OPERARIO”)){
num_operario=(int *) &msg_rec;
client[*num_operario-1]=sockfd1;
Tipo_conexion[i]=1;
}else if(len==sizeof(“MAQUINA”){
num_maquina=(int *) &msg_rec;
maquina[*num_maquina-1]=sockfd1;
Tipo_conexion[i]=0;
FD_CLR(&allset, sockfd1);
} //Las máquinas no hay que escucharlas
else{
if (len==sizeof(int)) {
client2[i]=maquina[*maq_solicitada-1
if (maquina[*maq_solicitada-1]<0) {
--nready;
continue;
}
sock_maquina=maquina[*maq_solicitada-1];
for (int k=0; k<N+P; k++){
sock_conexiones=conexiones[k];
if (sock_conexiones==sock_maquina)
break;
}
}
if(tipo_conexión[k]>0){
--ready;
continue;
}
len=read(sockfd1, msg_rec, MSG_SIZE);
write (client2[i], msg_rec, len+1); //TX Mensaje.
len=read(client2[i], msg_env, MSG_SIZE);
write(sockfd1, msg_env, len+1);
}
if (--nready<=0) continue;
}
} //Del bucle principal
} //Del main.
Centro de Estudios NOVA – Cartagena
868-97-38-38
28 DE JUNIO DE 2005 – EJERCICIO 1
Se diseña un servidor central concurrente TCP (puerto 5999) destinado al servicio de telefonía móvil. El servicio acepta la conexión de
los distintos clientes y los mantiene conectados mientras no reciba la acción de desconexión. El gráfico representa el sistema una vez
conectados los usuarios:
Entre las tareas del servidor, se encuentran:
− Gestionar las conexiones (sockets) permanentes con los usuarios. Éstos se conectan cuando desean tener acceso a la red. Habrá
un total de N conexiones de este tipo.
− Gestionar la conexión con el resto de la red a través del descriptor 500 (fijo y siempre activo).
− Gestionar el establecimiento de las conexiones entre usuarios inscritos en el propio servidor y entre usuarios locales y externos.
Cuando un usuario desea la conexión manda un número destino. Con el número, se comprueba si el destino pertenece a este
servidor y, en caso negativo, se manda al resto de la red, estableciéndose, en su caso, la comunicación a través de un nuevo
descriptor para el usuario destino. No se contempla el caso de peticiones desde usuarios externos.
Se pide:
A) Código para aceptar la conexión de los usuarios y proporcionar la concurrencia. El “resto de la red” está conectada desde el
arranque del servidor. (20%)
B) Código de establecimiento de las llamadas entre los usuarios locales. No incluya la gestión de las llamadas una vez establecidas,
sólo lo referente a la recepción de los números, y petición al usuario destino (con el número origen) y espera de la respuesta (no hay
que responder al usuario origen). La conexión se realiza si se recibe un “SI” y no se realiza si es un “NO”. (20%)
C) Código de establecimiento de las llamadas con usuarios remotos. En este caso las peticiones hay que encaminarlas por el
descriptor “resto de la red”. La respuesta en este caso puede ser un “NO” si es negativa con lo que se rechaza, y en caso afirmativo
se recibe un paquete de conexión SYN a través del descriptor “resto de la red”. En este caso se procederá a la conexión del nuevo
cliente y pasará a ser un usuario local más. No hay que responder al cliente origen.(20%)
Otra información:
#define N 20
#define SERVIDOR “192.168.5.254”
#define N 20
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 5999 //Dato
#define SERVIDOR “192.168.5.254”
#define RESTO 500 //Descriptor del resto de la red
#define MSG_SIZE 1024 //No se dice nada, mínimo mayor de 4
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, connfd, sockfd, sockfd1, nready,*numero, numero_resto;
int client [N]; // Array de descriptores de cada línea
int numeros[N]; //Posición-client<->Número
int conexiones [N]; //Relacionales entre números
int masclient [FD_SETSIZE]; //Para clientes externos que se conecten (apartado C)
int conexiones2 [FD_SETSIZE];//Para los relacionales de los externos (apartado C)
int numeros2 [FD_SETSIZE];// Para los números externos (apartado C)
ssize_t len;
fd_set rset, allset;
char * msg_env; //Mensaje enviado de conexión
char * msg_rec; // Mensaje recibido de conexión
char *valor1, *valor2; //Auxiliares para formar mensaje
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
Centro de Estudios NOVA – Cartagena
868-97-38-38
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ);
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
numeros[i]=-1;
conexiones [i]=-1;
}
FD_ZERO(&allset);
FD_ZERO(&rset);
//Inicialización de los conjuntos de descriptores
//Preparado para aceptar la entrada de los posibles clientes.
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<MAXOPERARIO; i++)//Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset);//Actualización del conjunto de descriptores
if (connfd > maxfd)//Actualización de variables
maxfd=connfd;// Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue;//Previsión para más de una interrupción
}
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
} //Del bucle principal
} //Del main
B) /* CÓDIGO SERVIDOR PARA ATENDER LAS PETICIONES DE CADA CLIENTE*/
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0)
continue;
// Se van a comprobar todos los CLIENTES LOCALES
if (FD_ISSET(sockfd1, &rset))//Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))==0){
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
numeros[i]=-1;
if (maxi==i) --maxi;
}
if (--nready<=0) break;
else continue;
else{
if (conexiones[i]<0) //Conexiones. No conversación.
if (len==sizeof(int)){ // Se recibe un número de teléfono
if (numeros[i]<0){
numero=(int *) &msg_rec;
numeros[i]=*numero;// Actualización número propio
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (--nready<=0) break;
else continue;
}else{// No es el número propio, es el destino
numero=(int *) &msg_rec;
conexiones[i]=numero;// Actualización conexiones
j=0;
while (numeros[j]!=numero) j++;
if (j<20) //Número local
write(client[j], &numeros[i], sizeof(numeros[i]));
if (j>=20){ //Número externo
valor1=(char*)&numeros[i];
valor2=(char *)conexiones[i];
msg_env=strcat(valor1, “:”);
msg_env=strcat(msg_env, valor2);
write(RESTO, msg_env, strlen(msg_env));
//Envíamos mensaje de conexión .
FD_SET(RESTO, &allset);
//Se activa el descriptor del RESTO de la red. Solución óptima
numero_resto=numero;
} //Variable para actualizar la petición
if (--nready<=0) break; else continue;
} //Previsión para más de una interrupción
else // (sizeof(int)) No es número de télefono, será respuesta...
if (len=strlen(“SI”)) // Se puede comprobar que es lo esperado...
if (strcmp (msg_rec,”SI”)==0){ // Es un SI
j=0;
while (conexiones[j]!=numeros[i]) j++;
//Actualización de arrays de conexión
conexiones[i] = numeros[j];
}else{ //Es un NO
j=0;
while (conexiones[j]!=numeros[i]) j++;
//Actualización de arrays no conexión
conexiones[j] = -1;
}
else break; //error
else // De conexiones... ES TEXTO
if (--nready<=0) continue;//Previsión para más de un operario simultáneo.
} // Fin del for de atención a los usuarios
C) Ahora las llamadas pueden ser externas...
if (FD_ISSET(RESTO, &rset)) {//Para el caso de que uno de ellos escriba algo
if ((len=read(RESTO, msg_rec,MSG_SIZE))!=0) // Va a ser un “NO”
if (strcmp(msg_rec,“NO”)==0){ // Se puede comprobar que es lo esperado...
FD_CLR (RESTO, &allset);
j=0;
while (conexiones[j]!=numero_resto) j++; //Actualización de arrays no conexión
conexiones[j] = -1;
}else continue; //del strcmp (caso de error), no es un “NO” y tampoco un SYN
else{ //del read
//Como se recibe len =0 y no puede ser un CLOSE, es el paquete SYN, con lo que deberemos hacer la
//conexión.
clilen=sizeof(cliaddr);
connfd =accept(RESTO, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el operario
for (i=0; i<(FD_SETSIZE); i++)//Actualización del array
if (masclient[i]<0) {//Nuevo array, el viejo se queda pequeño
masclient[i]=connfd;
break;
}
FD_SET(connfd,&allset);//Actualización del conjunto de descriptores
if (connfd > maxfd)//Actualización de variables
maxfd=connfd;// Los operarios van después de las líneas
if (i>maxi) maxi=i;
Centro de Estudios NOVA – Cartagena
868-97-38-38
//Faltaría añadir la conexión y el número a los nuevos arrays
j=0;
while (conexiones[j]!=numero_resto) j++;
conexiones2 [i] =numeros[j];
numeros2 [i]= numero_resto;
FD_CLR (RESTO, &allset);
if (--nready<=0) continue;
}
//Previsión para más de una interrupción que realmente no existe
//En este último caso en la gestión de la comunicación (no se pide) debería incluir la gestión no
//sólo de los arrays conexiones, numeros y client, sino que además habría que tener en cuenta los
// arrays masclient, conexiones2 y numeros2.
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se pretende realizar un servidor NTP (servidor de tiempo de red) de forma que todas aquellas máquinas que ejecuten el
cliente apropiado (que se puede descargar sin ningún tipo de restricción) se puedan sincronizar con esta máquina y se
permita el mantenimiento de esta sincronización. La sincronización es necesaria por ejemplo para programación remota,
en las que se evita generaciones de código posteriores en unas máquinas que en otras. En el funcionamiento de la
aplicación una vez llega un nuevo cliente, éste no vuelven a transmitir nada más.
Se pide:
A) Explique razonadamente el tipo de protocolo a usar en esta aplicación. (10%)
B) Realice el código necesario para que el servicio opere en el puerto 123 del servidor y sea capaz de mantener hasta
100 usuarios simultáneamente. Recuerde que el sistema será concurrente, por lo que prepárelo para ello. (10%)
C) Realice el código necesario para que el servicio sea capaz de generar y enviar la información que ofrece esta
aplicación, es decir, los valores temporales que sirvan de sincronización. Dicha información tiene esta estructura:
struct timeval original | struct timeval actual
El campo actual se obtiene de la ejecución del comando int gettimeofday(struct timeval *tv1, struct timezone *tv2)
donde el
parámetro tv1 devuelve la hora exacta, siendo original la hora exacta del momento en que llegó el nuevo cliente.
(15%)
D) Realice el código para que la aplicación sea capaz de mantener el servicio, reactualizando la información
planteada cada 10 msec. de forma concurrente. (15%)
Otra información:
#define MSG_SIZE 32 //Tamaño de los mensajes
#define Direccion_local “192.168.3.1”
Considere los tiempos de ejecución de código despreciable.
NOTA IMPORTANTE PARA LOS DOS PROBLEMAS:
1.- NO es necesario incluir las macros y librerías habituales en el código de la solución.
#define MSG_SIZE 32 //Tamaño máximo de los mensajes
#define Direccion_local “192.168.3.254”
#define PUERTO_SERVIDOR 123
int main (int argc, char **argv) {
int maxfd, listenfd, nready, i ;
ssize_t len;
fd_set rset;
char msg_rec[MSG_SIZE]; // Mensajes recibidos
socklen_t clilen;
char *buff;
struct timeval *original[100]; //Hora de funcionamiento original
struct sockaddr_in direcciones[100]; //Los usuarios posibles…
struct sockaddr_in servaddr, cliaddr;
struct timeval *tv1,*tv2,*tvselect ; //La variables temporales que se harán necesarias
// Inicializaciones temporales
tv1->tv_sec=0;
tv1->tv_usec=0;
tv2->tv_sec=0;
tv2->tv_usec=0;
nready=0;
//La preparación para 100 clientes consiste en abrir el puerto 123 y crear los arrays para 100 posiciones.
for (i=0; i<100;i++){ //Inicialización de los arrays de clientes
original[i]=NULL;
bzero(&direcciones[i],sizeof(struct sockaddr_in));
}
//Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (Direccion_local);
servaddr.sin_port=htons(PUERTO_SERVIDOR);
Centro de Estudios NOVA – Cartagena
868-97-38-38
maxfd=-1 ;
if((listenfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {printf ("ERROR \n"; exit(1);}
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {printf("ERROR \n"); exit (1);};
maxfd=listenfd;
//Servidor UDP preparado
msg_rec[0]=’\0’;
gettimeofday(tv2, NULL); // Para el funcionamiento del timeout
for(;;){
tvselect->tv_sec= 0;
if (nready==0){ //se salio por timeout el valor son 10000 usecs
tvselect->tv_usec=10000;
tv1->tv_sec= tv2->tv_sec;
tv1->tv_usec= tv2->tv_usec;
}
else //Si no es que expiró antes y hay que recalcular el timeout
tvselect->tv_usec= 10000-(tv2->tv_usec-tv1->tv_usec);
FD_SET(listenfd,&rset);
nready=select (maxfd+1, &rset, NULL, NULL, tvselect);
if (nready==0) //Salta timeout //reenviar información
for (i=0, i<maxi, i++){
buff=NULL;
buff=strcat(buff, (char *) original[i]);
n=gettimeofday(tv2, NULL);
buff=strcat(buff, (char *)tv2);
sendto(listenfd, buff, strlen (buff), 0, (struct sockaddr*)&direcciones[i],
sizeof (struct sockaddr_in));
};
if (FD_ISSET(listenfd, &rset)) { //Se recibe algo -> NUEVO CLIENTE
recvfrom (listenfd, msg_rec, MSG_SIZE, 0, (struct sockaddr*)&cliaddr, sizeof (struct
sockaddr_in));
gettimeofday(tv2, NULL);
direcciones [maxi]=cliaddr;
original[maxi]=tv2;
maxi++;
buff=NULL;
buff=strcat(buff, (char *)tv2);
buff=strcat(buff, (char *)tv2);
sendto(listenfd, buff, strlen (buff), 0, (struct sockaddr*)&cliaddr, sizeof (struct
sockaddr_in));
};
} //Fin for
}//Fin main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 1 (50% Ejercicios) 29 DE JUNIO DE 2010
Se diseña un servicio TCP concurrente en el que los clientes se conectan a este servicio para descargarse información de
aplicaciones propias del servicio que se genera de forma aleatoria. El gráfico representa las conexiones de las aplicaciones:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con los clientes. Éstos se conectan cuando desean acceder a la información de las
aplicaciones. Habrá un total de N conexiones de este tipo en el servidor.
− Gestionar la conexión de las aplicaciones de lectura (independientes de la aplicación servidor) que generan la información aleatoria.
Éstas se conectan en cualquier momento sin que exista ninguna situación predeterminada previa. Habrá un total de M conexiones de
este tipo.
− Gestionar la transmisión de la información al cliente.
Se pide:
A) Código de aceptación de conexiones de los clientes y aplicaciones. No olvide realizar el proceso de forma concurrente, utilizando
un único select. Los clientes se encuentran necesariamente en máquinas remotas del servidor. (15%)
B) Código para la lectura de la información de las aplicaciones y reenvío a los clientes de este contenido de forma concurrente. Una
vez el servicio recibe la información desde las aplicaciones, se procede al envío a todos los clientes. La información reenviada estará
compuesta por el número de la aplicación, que es tomado del número de puerto de ésta, seguida de la información recibida según
formato “PUERTO:INFORMACION”. Se mantendrá el uso de un único select. (15%)
C) Ahora suponga que los clientes quieren que el servidor les filtre la información de forma que no reciban la información de todas las
aplicaciones, sino sólo de algunas que cada cliente determine. Por ello, realice el código que permite que el cliente determine las
aplicaciones de las que requiere la información mediante un paquete del formato “FILTRAR:PUERTO1:PUERTO2… :PUERTOM”,
donde PUERTOX es el número de puerto de las aplicaciones en las que está interesado. (20%)
Otra información:
#define N 20
#define M 30
#define SERVIDOR “192.168.3.254” //Suponga que el servidor no tiene ningún otro inferfaz
#define SERVER_PORT 6999
#define MSG_SIZE 256 // Tamaño máximo para todos los mensajes
#define N 20
#define M 30
#define SERVIDOR “192.168.3.254” //Suponga que el servidor no tiene ningún otro inferfaz
#define SERVER_PORT 6999
#define MSG_SIZE 256 // Tamaño máximo para todos los mensajes
int main (int argc, char **argv) {
int i, j, maxi, maxj, maxfd, listenfd, connfd, sockfd1, nready, numero, valor;
int client [N]; // Array de descriptores de los usuario
int descriptores_aplicaciones[M]; //Se crea un array de “descriptores locales” para los servicios.
int aplicaciones[M]; Array de los puertos de las aplicaciones.
char* relaciones[N];//Array de relaciones de clientes, para cuando los clientes indiquen filtrar(apartado C)
ssize_t len;
buff[MSG_SIZE]; //Buffer para generar los mensajes
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensaje recibido
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {printf ("ERROR \n"; exit(1);}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR); //htonl(INADDR_ANY);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {printf("ERROR \n"); exit (1);}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
Centro de Estudios NOVA – Cartagena
868-97-38-38
client[i]=-1;
relaciones[i]=NULL;
}
for(i=0; i<M; i++){
aplicaciones [i]=-1;
descritpres_aplicaciones[i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
//Ahora la conexiones de clientes y aplicaciones
if ( FD_ISSET(listenfd, &rset){ // NUEVO no se puede distinguir si es cliente o aplicación
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
if (!strcmp(inet_ntoa(cliaddr.sin_addr), SERVIDOR)){ //Es un cliente no aplicación
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd;
if (i>maxi) maxi=i;
else{ // Es una aplicación no cliente
for (i=0; j<M; j++) //Actualización del array
if (descriptores_aplicaciones[j]<0) {
descriptores_aplicaciones[j]=connfd;
break;
}
aplicaciones[j]=ntohs(cliaddr.sin_port); //Guardamos el puerto;
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd;
if (i>maxj) maxj=j;
};
if (--nready<=0) continue; //Previsión para más de una interrupción
}
for (j=0; j<maxj; j++){
if ((sockfd1 =descriptores_aplicaciones[j])<0) // Se van a comprobar todas los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if((len=read(sockfd1, msg_rec, MSG_SIZE-strlen((char *)&aplicaciones[j])-1)> 0)
//Se lee. No es vacío
msg_env[0]=’\0’;
msg_env=strcat(msg_env, (char *)&aplicaciones[j]);
msg_env=strcat(msg_env, “:”);
msg_env=strcat(msg_env, msg_rec);
for (i=0,i>maxi, i++)
if (client[i]>0){
if(listado=strdup(relaciones[j])!=NULL); //HAY Filtrado
for (k=0, k<maxj, k++){
if (strtol(listado, listado,
10)==aplicaciones [k]){
write(client[i], msg_env,
strlen(msg_env));
break;
}
listado++;
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (listado==NULL)
break;
}
};
};
//CÓDIGO atención clientes (filtrado).
for (i=0; i<maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todas los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0) //Se lee. No es vacío
msg_rec=strchr(msr_rec, ‘:’)++;
relaciones[i]=strdup(msg_rec);
} // FIN ATENCIÓN CLIENTES
if(listado=strdup(relaciones[j])!=NULL); //HAY Filtrado
for (k=0, k<maxj, k++){
if (strtol(listado, listado, 10)==aplicaciones [k]){
break;
}
listado++;
if (listado==NULL)
break;
}
}// Fin for
} // Fin main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
28 DE JUNIO DE 2005 – EJERCICIO 1
Se diseña un servidor central concurrente TCP (puerto 5999) destinado al servicio de telefonía móvil. El servicio acepta la conexión de los distintos clientes y los mantiene conectados
mientras no reciba la acción de desconexión. El gráfico representa el sistema una vez conectados los usuarios:
Entre las tareas del servidor, se encuentran:
− Gestionar las conexiones (sockets) permanentes con los usuarios. Éstos se conectan cuando desean tener acceso a la red. Habrá un total de N conexiones de este tipo.
− Gestionar la conexión con el resto de la red a través del descriptor 500 (fijo y siempre activo).
− Gestionar el establecimiento de las conexiones entre usuarios inscritos en el propio servidor y entre usuarios locales y externos. Cuando un usuario desea la conexión manda un número
destino. Con el número, se comprueba si el destino pertenece a este servidor y, en caso negativo, se manda al resto de la red, estableciéndose, en su caso, la comunicación a través de un
nuevo descriptor para el usuario destino. No se contempla el caso de peticiones desde usuarios externos.
Se pide:
A) Código para aceptar la conexión de los usuarios y proporcionar la concurrencia. El “resto de la red” está conectada desde el arranque del servidor. (20%)
B) Código de establecimiento de las llamadas entre los usuarios locales. No incluya la gestión de las llamadas una vez establecidas, sólo lo referente a la recepción de los números, y
petición al usuario destino (con el número origen) y espera de la respuesta (no hay que responder al usuario origen). La conexión se realiza si se recibe un “SI” y no se realiza si es un “NO”.
(20%)
C) Código de establecimiento de las llamadas con usuarios remotos. En este caso las peticiones hay que encaminarlas por el descriptor “resto de la red”. La respuesta en este caso puede
ser un “NO” si es negativa con lo que se rechaza, y en caso afirmativo se recibe un paquete de conexión SYN a través del descriptor “resto de la red”. En este caso se procederá a la
conexión del nuevo cliente y pasará a ser un usuario local más. No hay que responder al cliente origen.(20%)
Otra información:
#define N 20
#define SERVIDOR “192.168.5.254”
#define N 20
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 5999 //Dato
#define SERVIDOR “192.168.5.254”
#define RESTO 500 //Descriptor del resto de la red
#define MSG_SIZE 1024 //No se dice nada, mínimo mayor de 4
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, connfd, sockfd, sockfd1, nready,*numero, numero_resto;
int client [N]; // Array de descriptores de cada línea
int numeros[N]; //Posición-client<->Número
int conexiones [N]; //Relacionales entre números
int masclient [FD_SETSIZE]; //Para clientes externos que se conecten (apartado C)
int conexiones2 [FD_SETSIZE];//Para los relacionales de los externos (apartado C)
int numeros2 [FD_SETSIZE];// Para los números externos (apartado C)
ssize_t len;
Centro de Estudios NOVA – Cartagena
fd_set rset, allset;
char * msg_env; //Mensaje enviado de conexión
char * msg_rec; // Mensaje recibido de conexión
char *valor1, *valor2; //Auxiliares para formar mensaje
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ);
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
numeros[i]=-1;
conexiones [i]=-1;
}
FD_ZERO(&allset);
FD_ZERO(&rset);
//Inicialización de los conjuntos de descriptores
//Preparado para aceptar la entrada de los posibles clientes.
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<MAXOPERARIO; i++)//Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
868-97-38-38
Centro de Estudios NOVA – Cartagena
}
FD_SET(connfd,&allset);//Actualización del conjunto de descriptores
if (connfd > maxfd)//Actualización de variables
maxfd=connfd;// Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue;//Previsión para más de una interrupción
}
/* ESPACIO RESERVADO PARA EL PROCESADO DEL SERVIDOR*/
} //Del bucle principal
} //Del main
B) /* CÓDIGO SERVIDOR PARA ATENDER LAS PETICIONES DE CADA CLIENTE*/
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0)
continue;
// Se van a comprobar todos los CLIENTES LOCALES
if (FD_ISSET(sockfd1, &rset))//Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))==0){
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
numeros[i]=-1;
if (maxi==i) --maxi;
}
if (--nready<=0) break;
else continue;
else{
if (conexiones[i]<0) //Conexiones. No conversación.
if (len==sizeof(int)){ // Se recibe un número de teléfono
if (numeros[i]<0){
numero=(int *) &msg_rec;
numeros[i]=*numero;// Actualización número propio
if (--nready<=0) break;
else continue;
}else{// No es el número propio, es el destino
numero=(int *) &msg_rec;
conexiones[i]=numero;// Actualización conexiones
j=0;
while (numeros[j]!=numero) j++;
if (j<20) //Número local
write(client[j], &numeros[i], sizeof(numeros[i]));
if (j>=20){ //Número externo
valor1=(char*)&numeros[i];
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
valor2=(char *)conexiones[i];
msg_env=strcat(valor1, “:”);
msg_env=strcat(msg_env, valor2);
write(RESTO, msg_env, strlen(msg_env)); //Envíamos mensaje de conexión .
FD_SET(RESTO, &allset); //Se activa el descriptor del RESTO de la red. Solución óptima
numero_resto=numero;
} //Variable para actualizar la petición
if (--nready<=0) break; else continue;
} //Previsión para más de una interrupción
else // (sizeof(int)) No es número de télefono, será respuesta...
if (len=strlen(“SI”)) // Se puede comprobar que es lo esperado...
if (strcmp (msg_rec,”SI”)==0){ // Es un SI
j=0;
while (conexiones[j]!=numeros[i]) j++; //Actualización de arrays de conexión
conexiones[i] = numeros[j];
}else{ //Es un NO
j=0;
while (conexiones[j]!=numeros[i]) j++; //Actualización de arrays no conexión
conexiones[j] = -1;
}
else break; //error
else // De conexiones... ES TEXTO
if (--nready<=0) continue;//Previsión para más de un operario simultáneo.
} // Fin del for de atención a los usuarios
C) Ahora las llamadas pueden ser externas...
if (FD_ISSET(RESTO, &rset)) {//Para el caso de que uno de ellos escriba algo
if ((len=read(RESTO, msg_rec,MSG_SIZE))!=0) // Va a ser un “NO”
if (strcmp(msg_rec,“NO”)==0){ // Se puede comprobar que es lo esperado...
FD_CLR (RESTO, &allset);
j=0;
while (conexiones[j]!=numero_resto) j++; //Actualización de arrays no conexión
conexiones[j] = -1;
}else continue; //del strcmp (caso de error), no es un “NO” y tampoco un SYN
else{ //del read
//Como se recibe len =0 y no puede ser un CLOSE, es el paquete SYN, con lo que deberemos hacer la
//conexión.
clilen=sizeof(cliaddr);
connfd =accept(RESTO, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el operario
for (i=0; i<(FD_SETSIZE); i++)//Actualización del array
if (masclient[i]<0) {//Nuevo array, el viejo se queda pequeño
masclient[i]=connfd;
break;
}
Centro de Estudios NOVA – Cartagena
FD_SET(connfd,&allset);//Actualización del conjunto de descriptores
if (connfd > maxfd)//Actualización de variables
maxfd=connfd;// Los operarios van después de las líneas
if (i>maxi) maxi=i;
//Faltaría añadir la conexión y el número a los nuevos arrays
j=0;
while (conexiones[j]!=numero_resto) j++;
conexiones2 [i] =numeros[j];
numeros2 [i]= numero_resto;
FD_CLR (RESTO, &allset);
if (--nready<=0) continue;
}
//Previsión para más de una interrupción que realmente no existe
//En este último caso en la gestión de la comunicación (no se pide) debería incluir la gestión no
//sólo de los arrays conexiones, numeros y client, sino que además habría que tener en cuenta los
// arrays masclient, conexiones2 y numeros2.
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (40%)
Se diseña un servicio de telefonía móvil, en el que la estación base ofrece servicios muy diversos, entre los que se encuentran los mensajes de texto. Se pretende realizar el software de los
terminales móviles, de manera que sean capaces en enviar y recibir los mensajes de texto referentes a este servicio.
De esta forma, el sistema consiste en atender simultáneamente al usuario del teléfono móvil por si desea mandar el mensaje y atender al servidor por si es éste el que nos manda el
mensaje de otro usuario.
Se pide:
A) Realice el código necesario de conexión del terminal al servidor y mantener la concurrencia entre el usuario local y el servidor conectado. (20%)
B) Realice el código para mandar y recibir mensajes. En caso del envío, el teléfono incluye en el mensaje el propio número de teléfono de la forma: “Número_propio:mensaje”. De igual
forma, en la recepción se recibe un mensaje del mismo tipo y se deben separar los dos campos, mostrando sólo el mensaje por la pantalla. (20%)
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define PUERTO 3000
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 3000 //Dato
#define SERVIDOR “192.168.5.254” //Dato
#define MSG_SIZE 1024 //Se sobreentiende que completo
#define TECLADO 0 // Descriptor para el teclado
#define PANTALLA 1 // Descriptor para la pantalla
int main (int argc, char **argv) {
int maxfd, listenfd, numero, sockfd1;
char* numero propio;
ssize_t len;
fd_set rset, allset;
char * msg_env; //Mensaje enviado de conexión
char * msg_rec; // Mensaje recibido de conexión
char *valor1, *valor2; //Auxiliares para formar mensaje
socklen_t clilen;
struct sockaddr_in servaddr;
numero_propio= argv[1]; //Por ejemplo, se puede pasar así, no viene especificado en el enunciado.
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(connect(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (listenfd>TECLADO)
maxfd=listenfd;
else maxfd =TECLADO;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
numeros[i]=-1;
conexiones [i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset); //Socket hay que activarlo
FD_SET(TECLADO, &allset); //Teclado hay que activarlo
for(;;){
rset=allset;
select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset)){ //SERVIDOR
//ATENCIÓN A LA INTERRUPCIÓN DEL SERVIDOR, se recibe mensaje
if (len=read(listenfd, msg_rec, MSG_SIZE)==0) // Caso de cierre
close (listendfd);
else{ // Mensaje
msg_env=strchr(msg_rec, “:”); //Buscamos los “:”
write(PANTALLA, msg_env++, strlen(msg_env));
}
}
if ( FD_ISSET(TECLADO, &rset)){ //USUARIO
//ATENCIÓN A LA INTERRUPCIÓN DEL USUARIO, se tx mensaje
if (len=read(TECLADO, msg_rec, MSG_SIZE-strlen(numero_propio)-1)==0){ // Caso de cierre
close (listendfd);
exit(1);
}else{ // Mensaje
valor=numero_propio;
msg_env=strcat(valor, “:”); //Ponemos los “:”
msg_env=strcat(msg_env,msg_rec); // Componemos el mensaje
write(listenfd, msg_env, strlen(msg_env)); // Y tx al servidor
}
}
} Fin del for
} Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
2 DE FEBRERO DE 2010
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP concurrente en el que el los clientes se conectan para acceder a información que están generando aplicaciones que corren de manera autónoma en la misma
máquina en el que se ejecuta el servicio. El gráfico representa las conexiones de las aplicaciones:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con los clientes. Éstos se conectan cuando desean acceder a una aplicación. Habrá un total de N conexiones de este tipo en el servidor.
− Gestionar la conexión con las aplicaciones suministradoras de información (independientes de la aplicación servidor). Éstas son conectadas cuando un cliente pide su contenido.
− Gestionar la devolución del contenido a los clientes. También se realiza la gestión de un estadístico.
Se pide:
A) Código de aceptación de conexiones de los clientes y lectura del nombre de la aplicación de la que se lee la información. Este proceso se realiza de forma concurrente, con un único
select. (20%)
B) Código para la comunicación con las aplicaciones y lectura de su contenido. Una vez el servicio recibe el nombre de la aplicación, se procede a la conexión con la aplicación de lectura y
lectura de los datos y transmisión de su contenido de forma concurrente. Para saber qué aplicación ha de vincularse, el nombre de la aplicación lleva un número
en su nombre según formato: “nombreXX” que indica el número de aplicación que lee ese contenido. Se mantendrá el uso de un único select. (20%)
C) Código para componer la temporización del tiempo de servicio. Para ello discretize el tiempo por una décima de segundo y obtenga el instante de comienzo de petición del cliente y el de
devolución de la información. El tiempo será la diferencia de ambos. Informe de este tiempo en un mensaje explícito al usuario. (10%)
Otra información:
#define N 20
#define M 29
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 2399
#define MSG_SIZE 256 // Tamaño máximo para todos los mensajes.
#define APLICATTION_PORT 300M //Siendo M el número de la aplicación [3001..30M]
#define N 20 //dato
#define M 29 //dato
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2399 //Dato
#define APLICACION_PORT 3000
Centro de Estudios NOVA – Cartagena
868-97-38-38
#define SERVIDOR “192.168.3.254”
#define MSG_SIZE 256 //Tamaños maximos
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, connfd, sockfd1, nready, numero, valor;
int client [N]; // Array de descriptores de los usuario
int descriptores_aplicaciones[M]; //Se crea un array de “descriptores locales” para las aplicaciones.
// char *aplicaciones[M]; Nombres de aplicaciones. No hace falta guardarlos.
// int relaciones[N]; //Array de relaciones de clientes, para relacionar cliente->aplicacion
int clientes_destinos[M]; // Como para el apartado C hay que relacionar directamente todo, se hace un segundo
// array de relaciones aplicación->cliente
long tiempo_final[N]; //Para el cálculo del retardo de servicio
long tiempo_comienzo[N[; //Guardar tiempo de apertura del servicio
ssize_t len;
char buff[MSG_SIZE];
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensaje recibido
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
struct timeval tv;
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0){
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
clientes_destinos[i]=-1;
descriptores_servicios[i]=-1;
tiempo_final[i]=0;
buff[i][0]=’\0’;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
Centro de Estudios NOVA – Cartagena
868-97-38-38
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
tv.tv_sec=0;
tv.tv_usec=100000; //Discretización 1 décima de segundo
nready=select (maxfd+1, &rset, NULL,NULL,&tv); //Bloqueo en espera de entradas
if (nready==0){ //Salida de Timeout,
if(maxi<0){ // Si no hay clientes no hay que hacer nada, ni siquiera contar, se está en espera.
contador=0;
break;
}
contador++; //Ha pasado 1 décima
};
//Ahora la conexiones de clientes
if ( FD_ISSET(listenfd, &rset)){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
// En esta parte se leen los nombres de las aplicaciones.
for (j=0; j<N; j++){
if ((sockfd1 =client[j])<0) // Se van a comprobar todas los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0){ //Se lee. No es vacío
//Hay que extraer el valor XX
if ((msg_rec[len-2] != ‘1’) && (msg_rec[len-2] != ‘2’)) //Se ve si el penúltimo dígito es 1 ó 2:
numero =atoi (msg_rec[len-1]);
else numero = atoi (msg_rec[len-2]); //Si hay que considerarlo.
if (aplicaciones[numero]<0){ //NO ESTÁ OCUPADO (Si lo está no se pide solución)
if ((descriptores_servicios[numero]=socket(AF_INET, SOCK_STREAM, 0))<= 0) {
printf ("ERROR \n");
exit(1);
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
servaddr.sin_port=htons(APLICACION_LECTURA00+numero); //IMPORTANTE PARA CONECTARSE
if ((connect (descriptores_aplicaciones[numero], (struct sockaddr*)&servaddr, sizeof(struct sockaddr))<0){
printf ("ERROR \n");
exit(1);
} //No se dice si las aplicaciones son concurrentes o no
//La aplicación ya está conectada, actualizamos variables.
FD _SET(descriptores_aplicaciones[numero],&allset);
if (descriptores_aplicaciones[numero]>maxfd)
maxfd = descriptores_aplicaciones[numero]; //Preparado select para escuchar
clientes_destino[numero]=client[i]; //Para las operaciones relacionales.
//TIEMPOS
tiempo_comienzo[numero]=contador;
tiempo_final[numero]=0; //Inicializacion tiempo
}else{ //Cierre del cliente
}; //NO SE PIDE
}
}
}
for (j=0; j<M; j++){
if ((sockfd1 =descriptores_aplicaciones[j])<0) // Se van a comprobar todas las aplicaciones
continue;
if (FD_ISSET(sockfd1, &rset)) { //Se recibe algo -> COMPUTAR TIEMPO
if((len=read(sockfd1, buff, MSG_SIZE))> 0){ //Se lee. No es vacío
valor=strlen(buff);
strncat(buff[i],msg_rec, valor));
if ((valor+strlen(msg_rec))=>MSG_SIZE){
write(clientes_destino[j], buff, strlen(msg_rec));
if (tiempo_final[j]==0){ //Hay que hacer calculo temporal
tiempo_final[j]= contador;
if (--nready<=0) continue;
else break;
}
retardo=tiempo_final[j]-tiempo_inicial[j];
write(clientes_destino[j],&retardo,strlen(retardo));
if (--nready<=0) continue;
} //Previsión para más de una interrupción
}
}
}
} //FIN del bucle principal for
} //FIN del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se pretende realizar un programa test de un servidor concurrente TCP que es un repositorio de logins y passwords (un repositorio de identidades). El servidor consiste en que
recibido un login indica si ese usuario existe y en su caso indica si el password recibido es correcto o no (acepta un formato de mensajes: “login:password”). La respuesta del
servidor puede ser “OK”, “DESCONOCIDO” (no se conoce el login) o “FALLO” (fallo del password) y a continuación se cierra la conexión.
El test consiste en (1) comprobar el funcionamiento del servidor (responde a logins y passwords de manera adecuada), (2) se pueden realizar un número de conexiones elevado
simultáneas como todo servidor concurrente, y (3) comprobar el retardo que ofrece el servidor al cliente. Para ser más precisos se supone que la aplicación test y el servidor operan en
la misma máquina. El test necesita una identidad de prueba veraz para poder realizar el test, supóngase que se reciben login y password como argumentos 1 y 2 de la línea de
comando respectivamente. Se pide:
A) Realice el código necesario para realizar la comprobación (1). Se trata pues de acceder, y hacer dos peticiones una que se supone correcta y otra que no. La comprobación
consistirá en ver que efectivamente se recibe “OK” y otro valor en el segundo caso. Para la primera petición se utilizará el login y password leído de la línea de comando, y para la
segunda otra combinación aleatoria para login y password de 6 caracteres cada uno. Atiéndalos de forma concurrente. En caso de error se muestra por pantalla “ERROR 1” y se
continúa el test. (15%)
B) Realice el código necesario para realizar la comprobación (2). Se realiza, utilizando el número máximo de sockets creables (no suponga restricciones del sistema operativo), de
realizar conexiones con el servidor, de forma que aproximadamente el 50% pida autorización positiva y el 50% negativas. Atiéndalos de forma concurrente. Se comprobará
que efectivamente no quede ningún socket abierto pasados 10 minutos desde la última respuesta recibida. En caso de error se muestra por pantalla “ERROR 2”. (25%)
C) Por último, realice el código necesario para realizar la comprobación (3). Modifique el código del apartado B para que se pueda calcular el tiempo que tarda cada servicio (se
admite un error tolerable de 0,1 segundos). Conforme se reciban las respuestas se va calculando la media y se muestra por pantalla. (10%)
Otra información:
#define MSG_SIZE 33 //Tamaño máximo de los mensajes
#define servidor_port 5000 //Puerto de funcionamiento del servidor
NOTA IMPORTANTE PARA LOS DOS PROBLEMAS:
1.- NO es necesario incluir las macros y librerías habituales en el código de la solución.
#define MSG_SIZE 33 //Tamaño máximo de los mensajes
#define servidor_port 6200 //Puerto de funcionamiento del servidor
#define N FD_SETSIZE-3 // N=número de clientes según apartado B es el máximo menos 3.
int main (int argc, char *argv[]){
int maxfd, sockfd1, i ,j, k, contador, contadorC;
ssize_t len;
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensajes recibidos
char buff[13]; //Para la gestión de login y passwords aleatorios
char* msg_envA ;
socklen_t clilen;
struct sockaddr_in servaddr;
time *s;
int cerrrados=0;
struct timeval tv; //La variable temporal de 10 segundos
int abiertos;
int retardomedio=0;
Centro de Estudios NOVA – Cartagena
FD_ZERO(&rset);
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (“127.0.0.1”);
servaddr.sin_port=htons(servidor_port);
maxfd=-1 ;
//Se utilizan los datos de entrada para generar un mensaje de petición
char* user=strdup(arg[1]);
char* user_pass=strdup(arg[2]);
char* msg_envA=strcat(user, “:”);
msg_env=strcat(msg_envA, user_pass);
msg_rec[0]=’\0’;
msg_rec_old[0]=’\0’;
abiertos=0;
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
//Se deben generar N clients (para el apartado A dos son suficientes)
for (i=0; i=N; i++){ //NECESARIO APARTADO B
if ((sockfd[i] = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
connect(sockfd[i], (struct sockaddr*) &sevaddr, sizeof(struct sockaddr));
// Siendo una aplicación que utiliza un servicio (un cliente), no se realiza la funcion bind.
if (i%2==0){ //Los pares se toman como correctos (50%)
if(write(sockfd[i], msg_env, strlen(msg_envA))<0) {
printf("ERROR \n");
exit (1);
};
}else{ //los impares serán aleatorios
srand(time(s)/2); // Se pone la semilla
for (j=0; j=13; j++)
if(j==6) buff[j]=’:’;
else {
valor=atoi(a)+rand()%26;
buff[j]=char(valor);
}
if(write(sockfd[i], buff, strlen(buff))<0) {
printf("ERROR \n");
exit (1);
};
}
FD_SET(sockfd[i],&allset); //Socket hay que activarlo, pues espera algo.
if(listenfd >maxfd) maxfd= listenfd;
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (i>maxi) maxi=i;
};
contador=0;
for(;;){
rset=allset;
tv.tv_sec=600; //Tiempo de 600 segundos (diez minutos)
tv.tv_usec=0;
//Para el apartado (C)
tv.tv_sec=0; //Tiempo de 600 segundos son 6000 décimas.
tv.tv_usec=1000;
nready=select (maxfd+1, &rset, NULL,NULL,&tv); //Bloqueo en espera de entradas
//TIME-calculo de tiempo para retardo. Apartado C.
if (nready==0){ //Salida de Timeout,
contadorC++;
contador++;
if (contador==6000)
if(maxi<0){ // Si no hay clientes se ha hecho bien.
exit(1);
} //No se indica qué realizar, parece que se puede terminar el test
else printf(“ERROR 2\n”); //CASO DE ERROR 2
}
}else { //Se lee valor
for (j=0; j<N; j++){ //Para apartado B
if ((sockfd1 =sockfd[j])<0) // Se van a comprobar todas las aplicaciones
continue;
if (FD_ISSET(sockfd1, &rset)) {
if((len=read(sockfd1, buff, MSG_SIZE))> 0){ //Se lee.
//Se realiza la comprobación 1
if ((j%2)==0) //Debería ser OK
if (strcmp(buff, “OK”)!=0) printf (“ERROR 1\n”);
else //Debería ser algún tipo de error
if (strcmp(buff, “OK”)==0) printf (“ERROR 1\n”);
retardomedio=(retardomedio*cerrados+contadorC)/(cerrados+1)
printf(“El retardo ha sido %d segundos y %d décimas de segundos\n”, retardomedio/10, retardomedio%10);
cerrados++;
contador==0;
if (j==maxi) while(sockfd[maxi]>0) maxi--;
if (sockfd1==maxfd) maxfd--;
FD_CLR(sockfd1, &allset);
continue;
}
}
} //Fin for
Centro de Estudios NOVA – Cartagena
}//Fin main
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
30 DE JUNIO DE 2009
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP concurrente temporizado en el que el los clientes se conectan a este servicio para descargarse información de unos archivos propios del servicio, pero con un
tiempo límite de descarga, para asegurar que la copia descargada es la versión correcta, ya que pasado ese tiempo es posible que sean actualizados. El gráfico representa las
conexiones de las aplicaciones:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con los clientes. Éstos se conectan cuando desean acceder a un archivo. Habrá un total de N conexiones de este tipo en el servidor.
− Gestionar la conexión con las aplicaciones de lectura (independientes de la aplicación servidor) que leen el contenido de los archivos (o ficheros). Éstas son conectadas cuando un cliente
pide el contenido del archivo que estas leen.
− Gestionar la descarga del fichero por el cliente.
Se pide:
A) Código de aceptación de conexiones de los clientes y lectura del nombre del archivo a enviar por parte del servicio, que se transmite en el primer mensaje que llega del cliente. No olvide
realizar el proceso de forma concurrente, utilizando un único select. (20%)
B) Código para la lectura de archivos y envío concurrente de su contenido. Una vez el servicio recibe el nombre del archivo, se procede a la conexión con la aplicación de lectura y se
transmite su contenido de forma concurrente con mensajes de tamaño máximo. Para saber qué aplicación ha de vincularse, el nombre del archivo lleva un número en su nombre según
formato: “nombreXX” que indica el número de aplicación que lee ese contenido. Se mantendrá el uso de un único select. (15%)
C) Código para componer la temporización del tiempo válido de descarga de los archivos, que dependerá del tamaño que tenga cada uno. Para ello discretize el tiempo por una décima de
segundo y obtenga el instante de comienzo de lectura de cada archivo. Una vez abierto el archivo, obtenga el tamaño del archivo, que suponga se lee en la primera
lectura de la aplicación, y calcule el tiempo máximo para su descarga tomando valor de velocidad 300Kbps. Si no se ha completado la transmisión en ese tiempo se reinicia el proceso de
transmisión. Suponga que la operación de lectura y transmisión de un paquete es de una centésima de segundo y los demás retardos de código son despreciables. (15%)
#define N 20 //dato
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2399 //Dato
#define APLICACION_LECTURA00 3500
#define SERVIDOR “192.168.3.254”
#define MSG_SIZE 256 //Tamaños maximos
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, connfd, sockfd1, nready, numero, valor;
int client [N]; // Array de descriptores de los usuario
int descriptores_servicios[M]; //Se crea un array de “descriptores locales” para los servicios.
Centro de Estudios NOVA – Cartagena
868-97-38-38
// char *aplicaciones[M]; Nombres de aplicaciones. No hace falta guardarlos.
int relaciones[N]; //Array de relaciones de clientes, para relacionar cliente->aplicacion
int clientes_destinos[M]; // Como para el apartado C hay que relacionar directamente todo, se hace un segundo
// array de relaciones aplicación->cliente
long tiempo_final[N]; //Para el cálculo del tiempo_final de transmisión
long tiempo_comienzo[N[; //Guardar tiempo de apertura del servicio
ssize_t len;
buff[N][256];
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensaje recibido
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
struct timeval tv;
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
clientes_destinos[i]=-1;
descriptores_servicios[i]=-1;
tiempo_final[i]=0;
buff[i][0]=’\0’;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
tv.tv_sec=0;
tv.tv_sec=100000; //Discretización 1 décima de segundo
nready=select (maxfd+1, &rset, NULL,NULL,&tv); //Bloqueo en espera de entradas
Centro de Estudios NOVA – Cartagena
868-97-38-38
//TIMEOUT
if (nready==0){ //Salida de Timeout,
if(maxi<0){ // SI no hay clientes no hay que hacer nada, ni siquiera contar, se está en espera.
contador=0;
break;
}
contador=contador+10; //Ha pasado 1 décima que son 10 centésimas
};
//Ahora la conexiones de clientes
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO +TIEMPO DESPRECIABLE
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
// En esta parte se leen los nombres de las aplicaciones.
for (j=0; j<N; j++){
if ((sockfd1 =client[j])<0) // Se van a comprobar todas los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo +TIEMPO DESPRECIABLE
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0){ //Se lee. No es vacío
if (relaciones[N]<0) { //Es el primer mensaje… luego nos interesa el número de puerto
//Hay que extraer el valor XX
if ((msg_rec[len-2] != ‘1’) && (msg_rec[len-2] != ‘2’)) //Se ve si el penúltimo dígito es 1 ó 2:
numero =atoi (msg_rec[len-1]);
else numero = atoi (msg_rec[len-2]); //Si hay que considerarlo.
//Ahora se pasa a establecer servicio con la aplicación, el “numero” calculado es muy práctico…
if (descriptores_servicios[numero]<0){ //NO ESTÁ OCUPADO (Si lo está no se pide solución)
if ((descriptores_servicios[numero]=socket(AF_INET, SOCK_STREAM, 0))<= 0) {
printf ("ERROR \n");
exit(1);
}
servaddr.sin_port=htons(APLICACION_LECTURA00 +numero); //IMPORTANTE PARA CONECTARSE
if ((connect (descriptores_servicios[numero], (struct sockaddr*)&servaddr, sizeof(struct sockaddr))<0){
printf ("ERROR \n");
exit(1);
Centro de Estudios NOVA – Cartagena
868-97-38-38
} //No se dice si las aplicaciones son concurrentes o no
//La aplicación ya está conectada, actualizamos variables.
FD_SET(descriptores_servicios [numero],&allset);
if (descriptores_servicios[numero]>maxfd)
maxfd = descriptores_servicios[numero]; //Preparado select para escuchar
relaciones[i]=descriptores_servicios[numero]; //Luego para las respuestas
clientes_destino[numero]=client[i]; //Para las operaciones relacionales.
//TIEMPOS
tiempo_comienzo[numero]=contador;
tiempo_final[numero]=0;
}
else //CUANDO SE RECIBAN DATOS DEL CLIENTE. NO SE PIDE
{}
}else //Cierre del cliente
{ }; //NO SE PIDE
}
}
for (j=0; j<M; j++){
if ((sockfd1 =descriptores_aplicaciones[j])<0) // Se van a comprobar todas las aplicaciones
continue;
if (FD_ISSET(sockfd1, &rset)) { //Se recibe algo -> COMPUTAR TIEMPO
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0){ //Se lee. No es vacío
if (tiempo_final[j]==0){ //Es el tamaño
tiempo_final[j]= contador+(int) (8*atoi(msg_rec)/3000);
if (--nready<=0) continue;
else break;
}
if (tiempo_final[j]<contador){
valor=strlen(buff[i]);
strncat(buff[i],msg_rec,MSG_SIZE-valor));
if ((valor+strlen(msg_rec))=>MSG_SIZE){ //Si al añadir sobrepasamos el tamaño max.
write(clientes_destino[j], buff[i], strlen(msg_rec));
strcpy(buff[i], msg_rec[MSG-SIZE-valor-1]);
buff[i][MSG-SIZE-valor]=’\0’;
}
contador++;
}
}else{
FD_CLR(sockfd1, &allset);
close(sockfd1);
}
if ((descriptores_servicios[numero]=socket(AF_INET, SOCK_STREAM, 0))<= 0) {
printf ("ERROR \n");
exit(1);
Centro de Estudios NOVA – Cartagena
868-97-38-38
}
FD_SET (descriptores_servicios[numero], &allset);
servaddr.sin_port=htons(APLICACION_LECTURA00 +j); //IMPORTANTE PARA CONECTARSE
if ((connect (descriptores_servicios[numero], (struct sockaddr*)&servaddr, sizeof(struct sockaddr))<0){
printf ("ERROR \n");
exit(1);
}
//La aplicación ya está nuevamente conectada… faltan los tiempos.
tiempo_comienzo[numero]=contador;
tiempo_final[numero]=0;
if (--nready<=0) continue;
} //Previsión para más de una interrupción
}
} //FIN del bucle principal for
} //FIN del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se pretende realizar una aplicación de encaminamiento en una red datagrama (sin conexión) que se encarga de reenviar,
según las indicaciones de un algoritmo, los paquetes que le llegan por cada entrada, hacia una salida determinada (que
puede ser otra aplicación cómo esta o un destino definitivo). Como existen todo tipo de aplicaciones y por tanto muchos
servicios en la red, es necesario abrir un conjunto (se dimensiona desde el 2000 al 2099) de puertos para asegurar que un
paquete no se pierde mientras la aplicación calcula la salida de otro paquete.
Se pide:
A) Realice el código necesario para que la aplicación sea capaz de abrir los 100 puertos que se solicitan para esta
aplicación y atender simultáneamente las entradas (leer los paquetes) que se pueden producir por cada uno de ello.
Realícelo de forma concurrente y utilice un único select para esta aplicación. (20%)
B) Realice el código para que la aplicación sea capaz de enviar la información leída a un destino determinado, de
forma concurrente. Para ello utilice una función que calcula el algoritmo de encaminamiento: struct sockaddr
*routing(struct sockaddr *origen destino). Envíe la información por la salida determinada por la mencionada función. (10%)
C) Por último, suponga ahora el servicio se adapta al entorno de manera que cuando la tasa de llegada de paquetes es
elevada y, por ello, la aplicación no puede esperar a que se ejecute la función routing, que es bastante lenta. Para ello:
C.1) Detecte la saturación mediante un temporizador en select de una décima de segundo. Cuando se ejecute select
100 veces seguidas sin expirar el timer suponga saturación. (10%)
C.2) En caso de saturación se generará un servicio multiprocesado concurrente, asignando un único puerto a cada
nuevo proceso. Estos nuevos procesos realizarán el servicio tal y como estaba diseñado. (10%).
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define Direccion_local “192.168.3.1”
NOTA IMPORTANTE PARA LOS DOS PROBLEMAS:
1.- NO es necesario incluir las macros y librerías habituales en el código de la solución.
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define Direccion_local “192.168.3.1”
#define PUERTO_ORIGEN 2000
int main (int argc, char **argv) {
int maxfd, listenfd, i ,j, k;
int contador;
ssize_t len;
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensajes recibidos
socklen_t clilen;
int client[100];
struct sockaddr_in servaddr, cliaddr, *destaddr;
struct timeval tv; //La variable temporal de 1 decima segundo
int multiprocesado=0; // Para solo hacerlo una vez
Centro de Estudios NOVA – Cartagena
868-97-38-38
//Se abre el servicio
//Hay que abrir 100 puertos. Al ser UDP, no se requiere conexión, no hay bloqueo asíncrono, por lo que se puede
//proceder a hacerlo directamente en “bucle”.
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (Direccion_local);
maxfd=-1 ;
for (i=0 ; i<=99 ;i++) {
if ((client[i] = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
servaddr.sin_port=htons(PUERTO_ORIGEN+i);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
printf("ERROR \n");
exit (1);
};
FD_SET(client[i],&allset); //Socket hay que activarlo
if(client[i]>maxfd) maxfd=client[i];
};
//Aplicación UDP preparada
msg_rec[0]=’\0’;
for(;;){
rset=allset;
tv.tv_sec=0;
tv.tv_usec=100000;
nready=select (maxfd+1, &rset, NULL, NULL, &tv); //Bloqueo en espera de entradas o timeout
if (nready==0){ //Salta timeout
contador=0;
continue;
}
for (j=0; j<M; j++){
if ((sockfd1 =client[j])<0) // Se van a comprobar todas las entradas
continue;
if (FD_ISSET(sockfd1, &rset)) { //Se recibe algo -> COMPUTAR TIEMPO EN CONTADOR
contador++;
if ((contador>=100) && !multiprocesado){//Condición de salto… Se necesita generación multiproceso.
for (j=0; j<99, j++){ //Hay que generar 99 hijos, el padre se hará cargo de un puerto.
multiprocesado=1;
if (fork()==0){ //UN HIJO
Centro de Estudios NOVA – Cartagena
868-97-38-38
//Se le asigna por ejemplo, el descriptor del puerto j, por tanto el resto hay que cerrarlo.
for (k=99; k>0; k--)
if (k!=j){
FD_CLR(client[k], &allset);
if (client[k]==maxfd)
maxfd--;
client[k]=-1;
}
//El hijo se quedaría con un select y sólo un puerto el j-ésimo atento
break; //Y en el hijo no hay que hacer nada más.
}
}else { //Y el padre debe cerrar el puerto tomado por el hijo
FD_CLR(client[j], &allset);
if (client[j]==maxfd)
maxfd--;
client[j]=-1;
}; // El padre ha de seguir iterando para generar hijos
};
if((len=recvfrom(sockfd1, msg_rec, MSG_SIZE, 0, (struct sockaddr*)&cliaddr, &clilen))> 0){ //Se lee. No es vacío
//APLICACIÓN SERVICIO (APARTADO B)
msg_rec[len]=’\0’;
destaddr= (struct sockaddr_in*) routing((struct sockaddr*)&cliaddr); //Según indica el enunciado esta funcion
//devuelve el destino a donde hay que reenviar.
//Es una función interna, por lo que no se puede multiplexar.
//Y se envía por ejemplo por el mismo puerto que llegó.
sendto(sockfd1, msg_rec, strlen(msg_rec),0, (struct sockaddr *)destaddr, sizeof(struct sockaddr));
};
}//Fin del for de atención a puertos
}//FIN bucle principal
} //Fin del MAIN
Centro de Estudios NOVA – Cartagena
868-97-38-38
12 DE SEPTIEMBRE DE 2008
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP concurrente en el que el los clientes se conectan para acceder a información que están generando aplicaciones que corren de manera autónoma en la
misma máquina en el que se ejecuta el servicio. El gráfico representa las conexiones de las aplicaciones:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con los clientes. Éstos se conectan cuando desean acceder a una aplicación. Habrá un total de N conexiones de este tipo en el servidor.
− Gestionar las conexiones permanentes con las aplicaciones (actúan como servicio). Éstas son conectadas por el servidor en el momento detallado en la cuestión B. Habrá un total
de M conexiones de este tipo.
− Gestionar la comunicación cliente-aplicación.
Se pide:
A) Código para aceptar la conexión de clientes. Éstos se conectan al servidor que les debe proporcionar servicio. En el primer mensaje, el cliente invoca la aplicación deseada con
su nombre. Recuerde utilizar un solo select para todo el ejercicio. (10%)
B) Código para conectarse con las aplicaciones. Estas operan como servicios TCP en los puertos [3100..310M]. El proceso de conexión se produce cuando algún cliente solicita la
aplicación que se menciona. Para relacionar aplicación con el puerto, el nombre de cada una lleva incluido el número M en él con formato “nombreM”. El puerto de la aplicación es
por tanto 3100+M. Una vez conectada, el servidor no se desconecta de la aplicación hasta que el cliente se desconecta de él. (25%)
C) Código para preparar la comunicación cliente-aplicación. Con el nombre capturado, y el descriptor de la conexión se trata de relacionar de manera directa cliente y aplicación (p.
ej. uno/dos arrays), de forma que lo que llegue por uno se pueda propagar al otro sin más cálculos. Indique en el código dónde se debe incluir dicha propagación de mensajes.
(15%)
Otra información:
#define N 20
#define M 29
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 2399
#define MSG_SIZE 256 // Tamaño maximo para los nicks, datos del juego y nicks+struct_sockaddr.
#define N 20 //dato
#define M 29 //dato
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2399 //Dato
#define SERVICIOS 3100// A partir de este están todos los servicios
#define SERVIDOR “192.168.5.254”
Centro de Estudios NOVA – Cartagena
868-97-38-38
#define MSG_SIZE 256 //Tamaños maximos
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, resto, connfd, sockfd1, nready, numero, valor;
int client [N]; // Array de descriptores de los usuario
int descriptores_servicios[M]; //Se crea un array de “descriptores locales” para los servicios.
// char * aplicaciones[M]; Nombres de aplicaciones. No hace falta guardarlos.
int relaciones[N]; //Array de relaciones de clientes, para relacionar cliente->aplicacion
int clientes_destinos[M]; // Como para el apartado C hay que relacionar directamente todo, se hace un segundo
// array de relaciones aplicación->cliente
ssize_t len;
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensaje recibido
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
relaciones[i]=-1;
}
for (i=0; i<M;i++){ //Hay dos arrays distintos
clientes_destinos[i]=-1;
descriptores_servicios [i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset); //Ahora no hay que inicializarlo
for(;;){
Centro de Estudios NOVA – Cartagena
868-97-38-38
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
//Ahora la conexiones de clientes
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
for (j=0; j<N; j++){
if ((sockfd1 =client[j])<0) // Se van a comprobar todas los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0){ //Se lee. No es vacío
if (relaciones[N]<0) { //Es el primer mensaje… luego nos interesa el número de puerto
//Hay que extraer el valor M…
valor=atoi(msg_rec[len-2]);
if ((valor != 1) || (valor != 2)) //Se ve si el penúltimo dígito es 1 ó 2:
numero =atoi (msg_rec[len-1]);
else
numero = 10*valor+ atoi (msg_rec[len-1]); //Si hay que considerarlo.
//Ahora se pasa a establecer servicio con la aplicación, el “numero” calculado es muy práctico…
if (aplicaciones[numero]<0){ //NO ESTÁ OCUPADO (Si lo está no se pide solución)
if ((descriptores_servicios[numero]=socket(AF_INET, SOCK_STREAM, 0))<= 0) {
printf ("ERROR \n");
exit(1);
}
servaddr.sin_port=htons(SERVICIOS+numero); //IMPORTANTE PARA CONECTARSE
if ((connect (descriptores_servicios[numero], (struct sockaddr*)&servaddr, sizeof(struct sockaddr))<0){
printf ("ERROR \n");
exit(1);
Centro de Estudios NOVA – Cartagena
868-97-38-38
}
//La aplicación ya está conectada, actualizamos variables.
FD _SET(descriptores_servicios [numero],&allset);
if (descriptores_servicios[numero]>maxfd)
maxfd = descriptores_servicios[numero]; //Preparado select para escuchar
relaciones[i]=descriptores_servicios[numero]; //Luego para las relaciones apartado C
clientes_destino[numero]=client[i]; //Para las operaciones relacionales.
}else //CUANDO SE RECIBAN DATOS. NO SE PIDE
{
}
} //Fin del if entrada de datos
else //Cierre del cliente
//Es cerrar dos conexiones a la vez…
close (sockfd1); //Cliente
close(relaciones[i]); //Aplicación relacionada
//Y se actualizan variables
j=0;
while (client[i]!=cliente_destino[j]) j++;
if (j<M){ //Encontrada la aplicación
FD_CLR(client[i], &allset);
FD_CLR(aplicaciones[j],&allset); //Conjuntos de descriptores
if (aplicaciones[i]==maxfd) maxfd--; //maxfd
client[i]=-1;
cliente_destino[j]=-1;
relaciones[i]=-1;
aplicaciones[j]=-1; //arrays actualizados
if (i==maxi) maxi--; //maxi
}else{
printf(“error/n”);
}
if (--nready<=0) break;
}
}
for (j=0; j<M; j++){
if ((sockfd1 =descriptores_aplicaciones[j])<0) // Se van a comprobar todas las aplicaciones
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if (--nready<=0) continue; //Previsión para más de una interrupción
}
//CÓDIGO APARTADO B
Centro de Estudios NOVA – Cartagena
} //FIN del bucle principal for
} //FIN del main
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se pretende realizar una aplicación distribuida en una red local en la que los distintos equipos que la conforman puedan transmitirse entre ellas información del propio sistema al
resto de equipos, para que tomen decisiones sobre rendimiento, tasas de transmisión, etc. (las decisiones a considerar no se piden en este ejercicio). La aplicación es única, es
decir, la misma aplicación se encarga de leer los datos del sistema y mandarlos al resto de equipos y de recibir los datos de los otros equipos y guardarlos.
Se pide:
A) Realice el código necesario para que el equipo sea capaz de leer los datos de funcionamiento interno del equipo. Estos datos se releen regularmente cada segundo o cada ver
que se reciben datos del resto de equipos (código del apartado C). La lectura se realiza sobre una serie de 10 archivos denominados “datosX.dat”, (X es un número entre 0 y 9), que
se actualizan solidariamente. (20%)
B) Realice el código para transmitir los datos leídos al resto de equipos. La transmisión se produce cada vez que se realiza la lectura de datos, sea cuál sea el momento, y en un
solo mensaje individual para cada equipo de la red (UNICAST), excepto él mismo claro. El mensajes es composición de los datos leídos del apartado A concatenados uno
detrás de otro en orden X y separados por el símbolo “:”. (15%)
C) Por último, la misma aplicación es capaz de recibir datos de la aplicación que se ejecute en otro equipo. Prepare la aplicación para ello y con la información de llegada (mismo
formato que apartado B) guárdela sin modificar en una serie de archivos “salidaY.dat” donde Y es el número de equipo de donde viene la información, que coincide con el último
dígito de su IP. (15%)
Otra información:
#define EQUIPOS 8 //Número de equipos de la red local
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define APPLICATION_PORT 5200 // Puerto de la aplicación
#define DATO_SIZE 8 //Tamaño máximo de cada uno de los datos a leer
#define Direccion_local “192.168.3.1”
Nota: La máscara de red es 255.255.255.254. Suponga que las direcciones ocupadas son las menores
posibles: 1, 2,… EQUIPOS.
#define EQUIPOS 8 //Número de equipos de la red local
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define APPLICATION_PORT 5200 // Puerto de la aplicación
#define DATO_SIZE 8 //Tamaño máximo de cada uno de los datos a leer
#define Direccion_local “192.168.3.1”
int main (int argc, char **argv) {
int maxfd, listenfd;
int X, Y;
char *nombre, *leido, *aux, *aux2;
char *direccion, *ip_grupo;
ssize_t len;
fd_set rset, allset;
char * msg_env; //Mensaje enviado
char msg_rec[MSG_SIZE]; // Mensajes recibidos
socklen_t clilen;
Centro de Estudios NOVA – Cartagena
868-97-38-38
FILE *fl;
struct sockaddr_in servaddr, cliaddr;
struct timeval tv; //La variable temporal de 1 segundo
//Se abre el servicio
if ((listenfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (Direccion_local);
servaddr.sin_port=htons(APPLICATION_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
printf("ERROR \n");
exit (1);
}
//La operación bind hay que hacerla para que asegurar la utilización del puerto 5200, y para poder recibir datos en el
// apartado C.
//Aplicación UDP preparada
numero=0;
msg_rec1[0]=’\0’;
msg_rec2[0]=’\0’;
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset); //Socket hay que activarlo
//Se puede hacer la primera lectura y tx ahora. El código es repetido para el caso genérico.
msg_env=NULL;
for (X=0; X<=9; X++){
nombre=NULL; //Inicialiazion del nombre del archive.
nombre=strcat(“dato”, (char *) X); //Como X es menor de 256 se puede hacer casting sin perder nada
nombre=strcat(nombre, “.dat·”);
fl=fopen(nombre, “r”) ;
fgets(leido, DATO_SIZE, fl) ; // Se leen los datos (una sóla lectura de 8 bytes)
fclose(fl) ;
msg_env=strcat(msg_env, leido);
if (X!=9) msg_env=strcat(msg_env,”:”); //El mensaje está listo
};
//Mecanismo para sacar la dirección IP de los destinos
ipgrupo=strdup(Direccion_local);
aux=ipgrupo;
for (i=0; i<strlen(ip_agrupo); i++)
aux++;
Centro de Estudios NOVA – Cartagena
868-97-38-38
aux=`’\0’; //Se coge la dirección menos el último carácter [1..8].
//La dirección IP de los equipos esta guardad en ip_grupo.
for (Y=1, Y<=EQUIPOS; Y++){
aux2= strcat(ipgrupo, (char *)Y); //Se forma direccion destino
servaddr.sin_addr.s_addr=inet_addr(aux2);
if (strcmp(aux2, Direccion_local)) //Control para no enviar a sí mismo
sendto(listenfd, msg_env, strlen(msg_env), 0, (struct sockaddr*) &servaddr, sizeof(struct sockaddr));
}
for(;;){
rset=allset;
tv.tv_sec=1;
tv.tv_usec=0;
nready=select (maxfd+1, &rset, NULL, NULL, &tv); //Bloqueo en espera de entradas o timeout
if ( FD_ISSET(listenfd, &rset){ //SOCKET
len=recvfrom(listenfd, msg_env, strlen(msg_env), 0, (struct sockaddr*) &cliaddr, &clilen);
direccion=inet_ntoa(&cliddr.sin_addr.s_addr); //Se saca la dirección del remitente
aux=direccion; //Se pueden reusar variables
for (i=0; i<strlen(ip_agrupo);i++) {aux++;} // Se obtiene el último caracter para el nombre del archivo
nombre=strcat(“salida”, aux);
nombre=strcat(nombre, “.dat·”); //Se forma el nombre
fl=fopen(nombre, “w”) ; //Se abre archivo
fputs(msg_rec, DATO_SIZE, fl) ; //Se escribe lo recibido
fclose(fl) ; // Se cierra archivo
}
//SIEMPRE QUE SE SALGA POR SELECT SE TRANSMITE LUEGO SE PONE SIN NECESIDAD DE HACER
// FD_ISSET
msg_env=NULL;
for (X=0; X<=9; X++){
nombre=NULL; //Inicialiazion del nombre del archive.
nombre=strcat(“dato”, (char *) X); //Como X es menor de 256 se puede hacer casting sin perder nada
nombre=strcat(nombre, “.dat·”);
fl=fopen(nombre, “r”) ;
fgets(leido, DATO_SIZE, fl) ; // Se leen los datos (una sóla lectura de 8 bytes)
fclose(fl) ;
msg_env=strcat(msg_env, leido);
if (X!=9) msg_env=strcat(msg_env,”:”); //El mensaje está listo
};
//Mecanismo para sacer la dirección IP de los destinos
ip_grupo=strdup(Direccion_local);
aux=ipgrupo;
for (i=0; i<strlen(ip_agrupo); i++) aux++; aux=`’\0’; //Se coge la dirección menos el último carácter [1..8].
//La dirección IP de los equipos esta guardad en ip_grupo.
Centro de Estudios NOVA – Cartagena
868-97-38-38
for (Y=1, Y<=EQUIPOS; Y++){
aux2= strcat(ipgrupo, (char *)Y); //Se forma direccion destino
servaddr.sin_addr.s_addr=inet_addr(aux2);
if (strcmp(aux2, Direccion_local)) //Control para no enviar a sí mismo
sendto(listenfd, msg_env, strlen(msg_env), 0, (struct sockaddr*) &servaddr, sizeof(struct sockaddr));
}
} //Fin del for
} //Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
01 DE JULIO DE 2008
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP concurrente en el que el los clientes se conectan para acceder a información que están generando aplicaciones que corren de manera autónoma en la misma
máquina en el que se ejecuta el servidor. El gráfico representa las conexiones de las aplicaciones:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con los clientes. Éstos se conectan cuando desean acceder a una aplicación. Habrá un total de N conexiones de este tipo en el servidor.
− Gestionar las conexiones permanentes con las aplicaciones (actúan como servicio). Éstas son conectadas por el servidor cuando se arranca el mismo. Habrá un total de M conexiones de
este tipo.
− Gestionar la comunicación cliente-aplicación.
Se pide:
A) Código para conectarse con las aplicaciones. Estas operan como servicios TCP en los puertos [3101..310M]. Una vez conectadas las aplicaciones, éstas mandan el nombre de cada
aplicación, que se debe guardar en el servidor. Hágalo de manera concurrente. (25%)
B) Código para aceptar la conexión de clientes. Una vez las M aplicaciones están activas para la comunicación, se puede pasar a aceptar los N clientes. Éstos se conectan al servidor que
les debe proporcionar servicio. Recuerde utilizar un solo select para todo el ejercicio. (12,5%)
C) Código para preparar la comunicación cliente-aplicación. El cliente manda el nombre de la aplicación en la que está interesado en su primer mensaje, el servidor lo lee y en un array
específico relaciona la conexión del cliente con la de la aplicación. No realice el código de transmisión de mensajes. (12,5%)
Otra información:
#define N 20
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 2399
#define MSG_SIZE 256 // Tamaño maximo para los mensajes.
#define N 20 //Se le da un valor
#define M 30 //Se le da otro valor
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2399 //Dato
#define SERVICIOS 3101 // A partir de este están todos los servicios
#define SERVIDOR “192.168.5.254”
#define MSG_SIZE 256 //Tamaños maximos
Centro de Estudios NOVA – Cartagena
868-97-38-38
struct clientes { //aunque no se pide, parece interesante crear una estructura para guardar los datos…
int sockfd; //puede llevar el descriptor del socket.
char * aplicacion; //puede llevar el nombre de la aplicación solicitada
int descriptor_aplicacion; //lo más importante será el descriptor de la aplicación
};
int main (int argc, char **argv) {
int i, j, k, maxi, maxfd, listenfd, resto, connfd, sockfd1, nready, numero, numero_resto;
int client [N]; // Array de descriptores de los usuario
int descriptores_servicios[M]; //Se crea un array de “descriptores locales” para los servicios.
char * aplicaciones[M]; // Array de nombres de aplicaciones.
struct clientes relaciones[N]; //Array de estructuras clientes, para relacionar cliente->aplicacion
ssize_t len;
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensaje recibido
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
/* Para conectar servicios hay que generar M sockets TCP clientes, cada uno para cada servicio, y hacerlos concurrentes
para poder atender la información que mandan cuando se conecta. Se podría denominar “cliente concurrente de servicios
TCP”. Se podría resolver haciendo:
a) polling… muy apropiado para este funcionamiento.
b) Realizar las conexiones iterativas y recibir la información (nombre) de forma concurrente. Más eficiente.
c) Select, escalonado-> el fin del establecimiento de la conexión con una aplicación, lanza la de la siguiente.
Parece lo más apropiado debido a que son conexiones con la misma máquina y no saturar.
d) Simplemente iterativo, uno después de otro, sin utlilizar select-> no es concurrente, peor solución.
Como polling no está estudiado explícitamente en este curso, optamos por la forma (c) [(b) es igual de válido y más
sencillo de implementar, (d) pierde puntuación] */
//Como va a haber que abrir el puerto de aceptar conexiones de clientes, lo vamos haciendo…
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
Centro de Estudios NOVA – Cartagena
868-97-38-38
client[i]=-1;
relaciones[i].sockfd=-1;
relaciones[i].descriptor_aplicacion=-1;
relaciones[i]->aplicacion=NULL;
}
for (i=0; i<M;i++){ //Hay dos arrays distintos
aplicaciones[i]=NULL;
descriptores_servicios [i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
// FD_SET(listenfd, &allset); Ahora no hay que inicializarlo
k=0; //UN CONTADOR NECESARIO PARA LLEVAR EL NÚMERO DE APLICACIONES CONECTADAS
//Se empiezan a conectar servicios: Se utilizar servaddr ya que está medio configurada.
servaddr.sin_port=htons(SERVICIOS);
if ((descriptores_servicios[0]=socket(AF_INET, SOCK_STREAM, 0))<= 0) {
printf ("ERROR \n");
exit(1);
}
if ((connect (descriptores_servicios[0], (struct sockaddr*)&servaddr, sizeof(struct sockaddr))<0) {
printf ("ERROR \n");
exit(1);
}
//El primero ya está…. A falta de recibir el nombre. Cuando termine el primero irá al segundo… y así hasta el M.
FD_SET(descriptores_servicios [0],&allset);
if (descriptores_servicios[0]>maxfd)
maxfd = descriptores_servicios[0];
//Preparado select para escuchar
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
for (j=0; j<M; j++){
if ((sockfd1 =aplicaciones[j])<0) // Se van a comprobar todas las aplicaciones
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe el nombre de la aplicación
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0) //Se lee
if(strlen(aplicaciones[j]==0)) { // Para posibles futuros usos, se puede preparar…
aplicaciones[j]=strdup(msg_rec); // Se guarda el nombre
k++; //Uno hecho
if (k<M){ //Hay que preparar uno nuevo
servaddr.sin_port=htons(SERVICIOS+k);
if ((descriptores_servicios[j+1]=socket(AF_INET, SOCK_STREAM, 0))<= 0) {
printf ("ERROR \n");
exit(1);
Centro de Estudios NOVA – Cartagena
868-97-38-38
}
if ((connect (descriptores_servicios[j+1], (struct sockaddr*)&servaddr, sizeof(struct sockaddr))<0){
printf ("ERROR \n");
exit(1);
}
FD_SET(descriptores_servicios [j+1],&allset);
if (descriptores_servicios[j+1]>maxfd)
maxfd = descriptores_servicios[j+1]; //Preparado select para escuchar
else{ // Cuando se han conectado todos los serv y todos han respondido se puede pasar a los clientes
FD_SET(listenfd, &allset); // Y así está preparado para aceptar clientes
}
}
}
}
}
if (--nready<=0) break;
} // Fin del for de aplicaciones
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++){ //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
relaciones[i].sockfd=connfd; //Luego para las relaciones apartado C.
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
}
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todos los usuarios conectados
continue;
if (FD_ISSET(sockfd1, &rset)){ //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))>0){ //El proceso de cierre no se pide…
if (strlen(relaciones[i].aplicacion)==0){ //Es el nombre. No tiene nombre guardado.
relaciones[i].aplicacion =strdup(msr_rec);
j=0;
while ((j<=M) && strcmp(msg_rec, aplicaciones[j])) {
Centro de Estudios NOVA – Cartagena
868-97-38-38
j++;
} //Se busca el nombre de la aplicación.
if (j<M) //Se ha encontrado
relaciones[i].descriptor_aplicacion= descriptores_servicios[j]; //Se relaciona
else
write(sockfd1, “SERVICIO NO DISPONIBLE\0”, strlen(“SERVICIO NO DISPONIBLE\0”));
}else{
/* FALTA AÑADIR CÓDIGO DE ATENCIÓN A CLIENTES NO SE PIDE */
}
}
} //Fin del FD_ISSET
if (--nready<=0) continue; } //Previsión para más de una interrupción
}Fin del for de clientes
}// FIN del bucle principal for
}// Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se pretende realizar una aplicación que permita a un usuario mandar datos (texto) de un móvil a otro, tipo servicio SMS. El servicio se realiza sin servidor, directamente se realiza
la transmisión de móvil a móvil, por lo que para poder realizar la transmisión es necesario que el destino se encuentre en la misma celda de cobertura, ya que la estación base
propaga mensajes a todos lo móviles conectados a él pero no al resto de la red. Es decir, para que se pueda transmitir el mensaje los móviles deben estar cerca, y además la
comunicación se realiza sin servidor.
Se pide:
A) Realice el código necesario de para que el móvil sea capaz de leer primero el número destino y luego el mensaje texto del usuario a través del teclado. Por supuesto, no se
puede bloquear el móvil durante la recepción de ambos datos proporcionados por el usuario. Prepare la concurrencia para el resto del ejercicio. (20%)
B) Realice el código para que la aplicación, con el número de teléfono, manda un mensaje “broadcast” (IP “192.168.255.255”) conteniendo en el mensaje el número de teléfono
solicitado. A continuación, esperará la recepción de un mensaje sin datos que le informa de la dirección IP del destino. Con esa dirección se puede realizar la transmisión del
mensaje concreto de texto. Transmita el mensaje de texto con la dirección recibida. Hágalo de forma concurrente y de la forma más ágil posible. (25%)
C) Por último, la misma aplicación es capaz de recibir datos en cualquier momento de la misma aplicación de otro móvil. Prepare la aplicación para ello y con la información de
texto llegada muéstrela por pantalla. (5%)
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define APPLICATION_PORT 4200 // Puerto de la aplicación
#define PUERTO_BROADCAST 5000 //Puerto destino para mandar información broadcast
#define Direccion_local “192.168.3.1”
Nota: Los número de teléfono posee nueve cifras.
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define APPLICATION_PORT 4200 // Puerto de la aplicación
#define PUERTO_BROADCAST 5000 //Puerto para esperar recibir información broadcast
#define Direccion_local “192.168.3.1”
#define TECLADO 0 // Descriptor para el teclado
int main (int argc, char **argv) {
int maxfd, listenfd, mandarya;
char numero[9];
ssize_t len;
fd_set rset, allset;
char * msg_env; //Mensaje enviado de llamada
char msg_rec1[MSG_SIZE], msg_rec2[MSG_SIZE]; // Mensaje recibido de teclado
socklen_t clilen;
struct sockaddr_in servador, cliaddr;
mandarya=0;
if ((listenfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
Centro de Estudios NOVA – Cartagena
868-97-38-38
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (Direccion_local);
servaddr.sin_port=htons(APLICATION_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
printf("ERROR \n");
exit (1);
}
//La operación bind hay que hacerla para que asegurar la utilización del puerto 4200, y para poder recibir datos en el
// apartado C.
//Aplicación UDP preparada
numero[0]=’\0’;
msg_rec1[0]=’\0’;
msg_rec2[0]=’\0’;
if (listenfd>TECLADO)
maxfd=listenfd;
else
maxfd =TECLADO;
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset); //Socket hay que activarlo
FD_SET(TECLADO, &allset); //Teclado hay que activarlo
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL, NULL, NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //SOCKET
{
//RECEPCIÓN RESPUESTA BROADCAST. APARTADO B
if ((len=recvfrom(listenfd, msg_rec2, MSG_SIZE,0, (struct sockaddr*)&cliaddr,&clilen))== 0){ // Mensaje vacio
//respuesta al paquete broadcast
//Lo que interesa es la direccion IP del destino. El puerto habrá que actualizarlo.
cliaddr.sin_port=htons(APPLICATION_PORT);
//Con esta información se puede enviar el mensaje.
if (strlen(msg_rec)>0){ // Por si acaso no está todavía escrito
sendto (listenfd, msg_rec1, strlen(msg_rec1), 0, (struct sockaddr*)&cliaddr, sizeof(struct sockaddr));
numero [0]=’\0’;
}else mandarya=1;
else{ // Se reciben datos
printf( “%s\n”, msg_rec2);
};
}
if ( FD_ISSET(TECLADO, &rset){ //USUARIO=TECLADO
if(strlen(numero)==0){ //NO se ha recibido todavía el número.
fgets(numero, 9, stdin); //Se lee numero
msg_rec1=’\0’;
Centro de Estudios NOVA – Cartagena
868-97-38-38
// ENVÍO PAQUETE BROADCAST CÓDIGO APARTADO B
//Configurar direccion
servaddr.sin_addr.s_addr= inet_addr (“192.168.255.255”);
servaddr.sin_port=htons(PUERTO_BROADCAST);
//Y mandar
sendto (listenfd, numero, strlen(numero), 0, (struct sockaddr*)&servaddr, sizeof(struct sockaddr));
}else //SE había leído ya el número
fgets(msg_rec1, MSG_SIZE, stdin);
if (mandarya) {
sendto (listenfd, msg_rec1, strlen(msg_rec1), 0, (struct sockaddr*)&cliaddr, sizeof(struct sockaddr));
mandarya=0;numero[0]=’\0’;
} //Caso para optimizar tx (APARTADO B)
}
} Fin del for
04 DE FEBRERO DE 2007
Problema 1 (60% Ejercicios)
Se diseña un servidor concurrente TCP destinado al servicio SSH. El demonio SSHD acepta la conexión de los distintos usuarios y los mantiene conectados mientras no termina la sesión.
El gráfico representa el sistema una vez conectados los usuarios:
Entre las tareas del servidor, se encuentran:
− Gestionar las conexiones permanentes con los usuarios. Éstos se conectan cuando desean establecer una sesión. Habrá un total de N conexiones de este tipo.
− Gestionar la lectura de login y password del usuario. Los usuarios sólo pueden enviar tres tipos de paquetes: su conexión, su password y los comandos a ejecutar.
− Gestionar la creación de las consolas de ejecución de comandos una vez los usuarios se conectan y su password es correcto.
Se pide:
A) Código para aceptar la conexión de los usuarios y proporcionar la concurrencia. Gestionar la lectura de los login y password de usuario que son respectivamente el primer y segundo
mensaje que se recibe del usuario. Procure proporcionar concurrencia en este proceso. (25%)
B) Comprobar y aceptar la autorización de usuario, (con su login y password) mediante la función: int confirma (char * login, char *password). La función devuelve 0 si está autorizado y otro
número en cualquier otro caso. En caso de no estar autorizado se cierra la conexión. (15%)
C) Gestione la creación de las consolas de usuarios. Para ello debe ejecutar las funciones fork() y execl(“/bin/bash”) que crea una nueva consola. Recuerde que debe guardar el pid
asociado para poder comunicarse con ella. Finalmente, puede gestionar la lectura de comandos del usuario y su paso (p. ej.: Mediante la función write) a su correspondiente
consola. No incluya la gestión de las respuestas de la consola al usuario. (20%)
Centro de Estudios NOVA – Cartagena
Otra información:
#define N 20
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 2199
#define N 20
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2199 //Dato
#define SERVIDOR “192.168.5.254”
#define MSG_SIZE 256 //Tamaños maximos
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, resto, connfd, sockfd, sockfd1, nready, numero, numero_resto;
int client [N]; // Array de descriptores de los usuario
int descriptores_locales[N]; //Se crea un array de “descriptores locales” para las consola (APARTADO C).
char * login[N]; // Array de logins de usuarios, para ir guardándolos mientras llegan los passwwords.
char *password; // Password. No hace falta guardarlos porque sólo se utiliza una vez en “confirma”.
ssize_t len;
fd_set rset, allset;
char * msg_rec; // Mensaje recibido
char *aux; //Auxiliares para formar mensaje
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
login[i]=NULL;
consolas [i]=-1;
}
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
/* Si se quiere pasar el login del servidor en este instante se añadiría. No es solución óptima*/
// read (connfd, msg_rec, sizeof(numero)); //Se entiende que no se va a tx otra cosa
// strcpy(login[i],numero); //Copia del login
if (--nready<=0) continue; //Previsión para más de una interrupción
}
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todos los usuarios conectados
continue;
if (FD_ISSET(sockfd1, &rset)){ //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))>0){ //El proceso de cierre no se pide…
if (login[i]==NULL){ //Es el login. No password o comando.
strcpy(login[i],msg_rec);
} //Copia del login
else if (consola[i]==0){ //NO es login, no es comando (la consola debería estar abierta) es password.
strcpy(password, msg_rec); // Copia del password.
if (confirma (login[i], password)!=0){// Cuando no es correcto…
close (sockfd);
client[i]=-1;
login[i]=NULL;
FD_CLR(sockfd1,&allset);
if (maxi==i) --maxi;}
if (--nready<=0) break; else continue;
}else{ // Cuando es 0, es correcto y hay que proceder a crear las consolas… (Apartado C)
int puerto_local=4000+i; // Buscamos un puerto “aleatorio”.
if ((int sockfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
Centro de Estudios NOVA – Cartagena
868-97-38-38
printf ("ERROR \n");
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(puerto_local);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
}
// El servidor ya tiene un puerto local con el que poder comunicarse con…
pid=fork( ); // Se crea proceso hijo. Recordar guardar el pid.
if (pid==0){ //El hijo va a ser la nueva consola…
for (i = 0; i <= maxi; i++)
if (client[i]>-1) {
close(client[i]);
close(descriptor_local[i]);
}
// Cerramos todos los puertos abiertos.
close(listenfd); // Cerramos el puerto de atención a nuevas conexiones. Sólo queda abierto sockfd.
// No hace falta inicializar variables, pues éstas no se volverán a utilizar.
break; break; // Se sale de los bucles for en los que se encontraba el programa padre.
// Y ya se puede proceder a crear la consola…
execl(“/bin/sh”); // Se ejecuta la nueva consola.
}else { //y el proceso padre…
close(puerto_local); //Cierra el socket UDP…
// Y para poder utilizar write en vez de sendto, más sencillo, se pasa a hacer un UDP conectado.
if (descriptor_local[i]=socket(AF_INET, SOCK_DGRAM, 0))<= 0) {
printf ("ERROR \n";
}
if(connect(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
}
}
} //Fin del “else” principal
}else{ // Atencion a comandos
write(descriptor_local[i], msr_rec, len+1);
}
}
}
if (--nready<=0) continue; } //Previsión para más de una interrupción
}Fin del for de clientes
…
}// FIN del bucle principal for
Centro de Estudios NOVA – Cartagena
}// Fin del main
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (40% Ejercicios)
Se diseña un servicio de telefonía móvil, en el que la estación base ofrece servicios muy diversos, entre los que se encuentran los mensajes de texto. Se pretende realizar
el software de los terminales móviles, de manera que sean capaces en enviar y recibir los mensajes de texto referentes a este servicio.
De esta forma, la aplicación consiste en atender simultáneamente al usuario del teléfono móvil por si desea mandar el mensaje y atender al servidor por si es éste el que
manda el mensaje de otro usuario.
Se pide:
A) Realice el código necesario de conexión del terminal al servidor y mantener la concurrencia entre el usuario local (el teclado del terminal) y el servidor al que se ha
conectado. (10%)
B) Realice el código para mandar y recibir mensajes. En caso del envío, el teléfono incluye en el mensaje el propio número de teléfono de la forma:
“Número_propio:mensaje”. De igual forma, en la recepción se recibe un mensaje del mismo tipo y se deben separar los dos campos, mostrando sólo el mensaje por la
pantalla. (10%)
C) Añada ahora una confirmación al usuario para proceder a la presentación por pantalla (por ejemplo, pulsar la tecla #) de un mensaje recibido desde el servidor, evitando
bloquear la aplicación sólo por esta causa. Incluya un “timeout” de tiempo máximo de espera para esta confirmación (1 minuto) de forma que si no se realiza, se vuelve a la
espera original. ( 20%)
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define PUERTO_SERVIDOR 3000
#define SERVIDOR “192.168.3.254”
#define numero_propio 100
El primer paso consiste en decidir si es un proceso cliente o servidor. Es un cliente porque se refiere a los terminales móviles con interfaces para usuario (STDIN) y para el servidor.
El segundo paso es decidir el protocolo TCP ó UDP. UDP no permite asegurar que el mensaje llegue y además el servidor no podría saber el destino que corresponde a cada mensaje cuando tenga que enviarlos:
luego es TCP.
#define SERVER_PORT 3000 //Dato
#define SERVIDOR “192.168.5.254” //Dato
#define MSG_SIZE 1024 //Se sobreentiende que completo
#define TECLADO 0 // Descriptor para el teclado
#define PANTALLA 1 // Descriptor para la pantalla
int main (int argc, char **argv) {
int maxfd, listenfd, numero, sockfd1, nready;
char* numero propio;
ssize_t len;
fd_set rset, allset;
char * msg_env; //Mensaje enviado de conexión
char * msg_rec1, *msg_rec2; // Mensaje recibido de conexión
char *valor1, *valor2; //Auxiliares para formar mensaje
socklen_t clilen;
struct timeval * tv; //Variable temporal necesaria para el apartado C
struct sockaddr_in servaddr;
numero_propio= argv[1]; //Por ejemplo, se puede pasar así, no viene especificado en el enunciado.
Centro de Estudios NOVA – Cartagena
868-97-38-38
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(connect(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
printf("ERROR \n");
exit (1);
}
if (listenfd>TECLADO)
maxfd=listenfd;
else
maxfd =TECLADO;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
numeros[i]=-1;
conexiones [i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset); //Socket hay que activarlo
FD_SET(TECLADO, &allset); //Teclado hay que activarlo
tv->tv_sec=NULL;
tv->tv_usec=NULL;
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL, tv); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //SERVIDOR
if (len=read(listenfd, msg_rec1, MSG_SIZE)>0){ // Mensaje
msg_env=strchr(msg_rec1, “:”); //Buscamos los “:”
write(PANTALLA, msg_env++, strlen(msg_env));
tv->tv_sec=60;
tv->tv_usec=0;
}
}
if ( FD_ISSET(TECLADO, &rset){ //USUARIO
if (len=read(TECLADO, msg_rec2, MSG_SIZE-strlen(numero_propio)-1)> 0){ // Mensaje
if (strcmp(msg_rec1,”#”)==0){
msg_env=strchr(msg_rec1, “:”); //Buscamos los “:”
write(PANTALLA, msg_env++, strlen(msg_env));
tv->tv_sec=NULL;
Centro de Estudios NOVA – Cartagena
868-97-38-38
tv->tv_usec=NULL;
}else{
valor=numero_propio;
msg_env=strcat(valor, “:”); //Ponemos los “:”
msg_env=strcat(msg_env,msg_rec2); // Componemos el mensaje
write(listenfd, msg_env, strlen(msg_env)); // Y tx al servidor
tv->tv_sec=NULL; tv->tv_usec=NULL;
}
}
}
if (nready == 0){ //Expira el tiempo
tv->tv_sec=NULL; tv->tv_usec=NULL;
} //Inicializamos el sistema.
}
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
03 DE JULIO DE 2007
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP destinado a un videojuego distribuido. El servicio se encuentra distribuido entre todos los equipos de los jugadores que en ese momento juegan una partida del
videojuego. El gráfico representa las conexiones de las aplicaciones una vez conectados los jugadores:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con el resto de jugadores. Éstos se conectan cuando desean realizar una partida. Habrá un total de N-1 conexiones de este tipo en cada servicio.
− Gestionar la lectura de nicknames de los jugadores. Los usuarios sólo pueden enviar tres tipos de paquetes: su nickname, su fin de juego y los datos de situación referentes al juego en sí.
− Gestionar la inclusión de nuevos jugadores con la partida ya comenzada.
Se pide:
A) Código para aceptar la conexión de nuevos jugadores y proporcionar la concurrencia entre ellos. Gestionar la lectura de los nickname’s de jugadores que se transmite en el primer mensaje de
datos, que se recibe de un nuevo jugador. Procure proporcionar concurrencia en este proceso. (25%)
B) Código para conectarse a un grupo o partida ya comenzada. En este caso el jugador busca a los jugadores ya conectados. Para ello, como conoce el número de puerto en el que
funciona, va testeando las IP’s que el jugador local le va proporcionando por teclado. Cuando encuentra un jugador activo, le manda el nickname propio y recibe de este
jugador a su vez su nickname . (25%)
Otra información:
#define N 20
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 2399
#define NICKNAME nombrealumno //Cada alumno que ponga su nombre
#define MSG_SIZE 256 // Tamaño maximo para los nicks, datos del juego y nicks+struct_sockaddr.
#define N 20
#define LISTENQ N-1 // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N-1 a
// la vez.
#define NICKNAME alumnoLSC //Nick del jugador
#define SERVER_PORT 2199 //Dato
#define SERVIDOR “192.168.5.254”
#define MSG_SIZE 256 //Tamaños maximos de mensajes
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, resto, connfd, sockfd, sockfd1, nready, numero, numero_resto;
int client [N]; // Array de descriptores de los jugadores
char * nickname [N]; // Array de nicks de usuarios, para ir guardándolos (en realidad N-1).
char * IP [N]; //Array de caracteres para ir guardando las direcciones IP de los jugadores.
Centro de Estudios NOVA – Cartagena
868-97-38-38
ssize_t len;
fd_set rset, allset;
char * msg_rec; // Mensaje recibido
char *IPCON; // Char para guardar la IP del jugador al que se conecta.
socklen_t clilen;
struct sockaddr_in servaddr;
//Arranque de la parte de servidor concurrente, para aceptar nuevos jugadores.
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n";)
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
nickname[i]=NULL;
IP[i]=NULL;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
//Para el apartado B) donde hay que gestionar la conexión a nuevos juegos:
FD_SET (0, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
if (connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen)<0) continue; //Se acepta el usuario
for (i=0; i<N; i++){ //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
}
Centro de Estudios NOVA – Cartagena
868-97-38-38
IP[i]=cliaddr.sin_addr.s_addr; // Se guarda la dirección IP del nuevo jugador.
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd;
if (i>maxi) maxi=i;
/* Si se quiere leer el nick del jugador en este instante se añadiría. No es solución concurrente*/
// read (connfd, msg_rec, sizeof(msg_rec)); //Se entiende que no se va a tx otra cosa
// strcpy(nickname[i],msg_rec); //Copia del nick
// Aquí se puede hacer comprobación de NICKNAME+IP y comprobar que no es un jugador repetido.
if (--nready<=0) continue; //Previsión para más de una interrupción
}
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todos los usuarios conectados
continue;
if (FD_ISSET(sockfd1, &rset)){ //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))>0) //El proceso de cierre no se pide…
if (nickname[i]=NULL){ //Es el nick.
strcpy(nickname [i],msg_rec); //Copia del nick. La IP ya estaba copiada en la conexión.
//Según apartado B, cuando alguien se conecta se le manda el nick propio.
if (strcmp(IP[i],IPCON)!=0)
write(sockfd1, &NICKNAME, strlen(NICKNAME));
}
}else{ //Es comando o dato del juego
/* FALTA AÑADIR CÓDIGO DE FUNCIONAMIENTO DEL JUEGO. NI SE EXPLICA, NI SE PIDE.*/
}
}
if (--nready<=0) continue; } //Previsión para más de una interrupción
}
if (FD_ISSET(0, &rset)) // sale por el teclado.
if (len=read (0, msg_rec, strlen(msg_rec))==strlen (SERVIDOR)) { // caso de que “parezca” una IP.
// Aprovechamos servaddr que ya está configurado y:
servaddr.sin_addr.s_addr= inet_addr (msg_rec); //Actualizamos IP.
if ((sockfd= socket(AF_INET, SOCK_STREAM, 0))<0) continue;)
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))<0) close(sockfd); continue;}
// Se intenta conectar al jugador indicado.
//Y se gestiona la nueva conexión:
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=sockfd;
break;
}
IP[i]=strdup(servaddr.sin_addr.s_addr); // Se guarda la dirección IP del nuevo jugador.
IPCON=strdup(IP[i]); //Variable para saber a quién nos hemos conectado, para luego realizar el intercambio de nicks.
Centro de Estudios NOVA – Cartagena
868-97-38-38
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (sockfd > maxfd) //Actualización de variables
maxfd=sockfd;
if (i>maxi) maxi=i;
/* Se pasa el nick del jugador en este instante se añadiría. */
write (sockfd, &NICKNAME, strlen(NICKNAKE)); //Se entiende que no se va a tx otra cosa
/* Si se quiere esperar el nick del otro jugador en este instante se añadiría, solución no conc. */
if ((len=read(sockfd, msg_rec,MSG_SIZE))>0){
if (nickname[i]=NULL) //Es el nick.
strcpy(nickname[i],msg_rec);
} //Copia del nick
} //FIN del if (TECLADO)
…
}// FIN del bucle principal for
}// Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
15 DE SEPTIEMBRE DE 2006
Problema 1 (60% Ejercicios)
Se diseña un servidor central TCP destinado al servicio de descubrimiento de servicios. El servicio consiste en que un usuario se conecta y el servicio le informa de dónde se encuentra el
servicio que está buscando. El gráfico representa el sistema una vez conectados los usuarios:
Entre las tareas del servidor, se encuentran:
− Gestionar las conexiones permanentes con los usuarios. Éstos se conectan cuando desean descubrir (saber donde está) un servicio. Habrá un total de N conexiones de este tipo.
− Gestionar la búsqueda del servicio en la única base de datos local. Esta base de datos se abre por el servidor en el arranque y no se vuelve a cerrar. Se considera la gestión de la base de
datos idéntica a la de un fichero.
− Gestionar la búsqueda de la localización del servicio (por su nombre) en la base de datos. La base de datos es simplemente una lista de “nombres” y “localizaciones”, con tamaños de 20
bytes y “sizeof (struct sockaddr)” respectivamente. La base de datos (y el servicio) devuelve la dirección y puerto del servicio buscado en formato “struct sockaddr”.
Se pide:
A) Código para aceptar la conexión de los usuarios y proporcionar la concurrencia. Recuerde que el proceso ha de ser completamente concurrente, es decir, que el servidor no puede
quedarse atendiendo la búsqueda de un único usuario. Incluya también la apertura de la base de datos (que se realiza antes de la conexión de los clientes). (20%)
B) Código de peticiones y resolución de la localización. Una vez se recibe el nombre del servicio (20 bytes), se debe buscar en la base de datos (se supone que siempre es accesible) y
encontrar el nombre, lo que le sigue es su localización. Los registros son directamente nombre y localización (sin espacios) y se separan por un “INTRO” (dos bytes). (25%)
C) Suponga ahora que la búsqueda no encuentra resultado en la base de datos. Gestione la conexión (TCP) y petición sobre otro servicio de descubrimiento externo. A su vez se espera la
respuesta de este servicio y se responde al usuario. (15%)
Otra información:
#define N 100
#define SERVIDOR_EXTERNO “192.168.5.254”
#define SERVER_PORT 2499
Nota importante: Suponga que el equipo donde se ejecuta el servidor DISCOVERY no tiene limitaciones de memoria, ni de número de puertos abiertos simultáneamente. El sistema
operativo permite la concurrencia en la base de datos.
#define N 100
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2499 //Dato
#define SERVIDOR _EXTERNO “192.168.5.254” //Del servicio externo
#define MSG_SIZE 1024 //No se dice nada, mínimo mayor de 20 (tamaño del nombre del servicio buscado)
#define BASE “BASE DE DATOS” //No se indica nombre. Nos hace falta para proceder a su apertura.
int main (int argc, char **argv) {
int listenfd, connfd, sockfd, id, numfp;
char *nombre; // Nombre que busca el cliente
ssize_t len;
Centro de Estudios NOVA – Cartagena
868-97-38-38
char * datos; // para la base de datos
char *nombre_comprobar; // Nombre extraido de la base de datos
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr, servexternoaddr; //Hay 3, el propio, el cliente y el del servicio externo.
fp=fopen(BASE, ‘r’); //Se abre la base de datos. “fp” en el proceso padre siempre apuntará al origen.
numfp=fileno(fp); //Descriptor numérico, para facilitar la búsqueda.
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= htonl(INADDR_ANY); //Como no se conoce la direccion local, no se usa inet_addr
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
for (;;){
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
if ((id=fork())<0) {
printf (“Error\n”);
continue;
} else if (id>0){ //Proceso padre
close (connfd); // Cerramos puerto del cliente y esperamos por más.
}else{ //Proceso hijo
close (listenfd); // se cierra puerto de conexiones. Los procesos ya están funcionados y conectados.
/* ESPACIO PARA EL PROCESADO DEL HIJO APARTADOS B Y C */
if (read (connfd, nombre, 20)==0) {
close(connfd);
exit(0);
}else{ //Se lee el nombre, y se comprueba si es 0.
while (!feof(fp)){ //Con el nombre vamos a buscar por toda la base de datos.
read(datos, 20, numfp); // Leemos sólo la parte del nombre. Se podría leer todo y luego separar...
nombre_comprobar=strdup(datos); //Duplicamos la cadena, nos hará falta, (se puede usar strcpy).
fgets(datos, sizeof(struct sockaddr),fp); //Leemos lo que falta de la linea, ya que tenemos que
// hacerlo tanto si es o no correcto.
//Ahora toca comprobar:
if (strcmp(nombre_comprobar,nombre)==0){ //Si lo es
write (connfd, datos, strlen(datos)); //Rapidamente se envía la respuesta al cliente;
//Se le puede hacer un casting a datos: (struct sockaddr *) &datos
exit(0);
Centro de Estudios NOVA – Cartagena
868-97-38-38
} //El hijo no tiene nada más que hacer se termina.
} //Fin del bucle while.
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servexternoaddr, sizeof (servaddr));
servexternoaddr.sin_family= AF_INET;
servexternoaddr.sin_addr.s_addr= inet_addr(SERVIDOR_EXTERNO); //Ahora sí se usa inet_addr
servexternoaddr.sin_port=htons(SERVER_PORT); //El mismo que el propio, es otro como
//DISCOVERY.
if(connect(sockfd, (struct sockaddr *)&servexternoaddr, sizeof(servexternoaddr)<0){ //Y se conecta con el externo.
printf("ERROR \n");
exit (1);
}
// Ahora hay que hacer lo que haria un cliente con el servidor:preguntar y esperar respuesta. Como el
// proceso no tiene que realizar ninguna otra tarea se puede bloquear, aunque se deberia usar select para
// limitar el tiempo de espera.
write (sockfd, nombre_comparar, strlen(nombre_comparar)); //Mandamos el nombre.
//Se espera respuesta.
if (read ( sockfd, datos, sizeof (struct sockaddr))!=sizeof(struct sockaddr)){
write(connfd, “Dato no dispoble\n”);
exit (1);)
}else {
write(connfd, datos,strlen (datos));
exit (0);
}
}
} // Fin del hijo.
} Fin del for
}Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se diseña un servicio de subscripciones a una lista de distribución de publicidad mediante mensajes cortos de texto. Este servicio consiste en enviarles SMS’s
indiscriminadamente a números de usuario obtenidos de una base de datos y éstos, al abrir el mensaje, automáticamente responden al SMS quedando subscritos al
servicio de publicidad. Se pretende realizar el software del servidor de mensajes, de manera que sea capaz en enviar y recibir los mensajes de texto referentes a este servicio
y suscribir a los usuarios.
De esta forma, la aplicación consiste en atender simultáneamente al envío de mensajes SMS (que los va leyendo de la base de datos “DATABASE”), enviar los mensajes
a los teléfonos móviles y atender las respuestas de éstos y subscribirlos al servicio SPAM (enviando un mensaje SMS a otro servicio que se encarga de proporcionar la
publicidad).
Se pide:
A) Realice el código necesario de arranque del servidor y mantener la concurrencia entre el envío indiscriminado de SMS’s y la atención de las posibles respuestas de los
usuarios. (25%)
B) Realice el código para mandar y recibir mensajes. En caso del envío, el servidor busca el destino en la base de datos “DATABASE” y obtiene el número del usuario
junto con una estructura “struct sockaddr” que indica la dirección y puerto de funcionamiento de los terminales, bajo el formato “NÚMERO:SOCKADDR”. Con esta
información el servidor genera un mensaje mediante la función char* genera(double número, struct sockaddr* clientaddr) que ya está programada (no suponga bloqueo al
ejecutar esta función). En recepción, el servidor lee el mensaje, leyendo el origen, y enviando al servidor que funciona en el puerto 4500 de la misma máquina un mensaje
con formato “SOCKADDR+conectado” donde SOCKADDR es la struct sockaddr que contiene dirección IP y puerto del usuario conectado. (25%).
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes
#define PUERTO_SERVIDOR 3000
#define SERVIDOR “192.168.3.254”
El número de teléfono posee nueve cifras.
Considere la base de datos como un archivo compuesto por la estructura “NÚMERO:SOCKADDR”, indefinidamente repetida.
El primer paso es decidir el protocolo TCP ó UDP. TCP no permite mandar paquetes a distintos destinatarios por el mismo descriptor, además y sobre todo, se desconoce si se tiene cliente o
servidor en el otro extremo. UDP resuelve estas dificultades.
#define SERVER_PORT 3000 //Dato
#define SERVIDOR “192.168.5.254” //Dato
#define MSG_SIZE 1024 //Se sobreentiende que completo
#define SPAM 4500 //Enunciado
int main (int argc, char **argv) {
int maxfd, listenfd, numfd, sockfd1, nready;
char* numero propio;
ssize_t len;
fd_set rset, allset;
char * msg_env; //Mensaje enviado
char * msg_rec1, *msg_rec2; // Mensaje recibido de conexión
char *valor1, *valor2; //Auxiliares para formar mensajes
socklen_t clilen;
FILE *fp; //descriptor para la base de datos.
double numero; //Para el numero de la función “genera”
struct timeval * tv; //Podría ser utilizada.
Centro de Estudios NOVA – Cartagena
868-97-38-38
struct sockaddr_in servaddr, cliaddr;
if ((listenfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
fp=fopen(“DATABASE”, “r”); // Se abre base de datos.
int numfd=fileno(fp);
FD_SET(listenfd, &allset); //Socket hay que activarlo
FD_SET(numfd, &allset); //Database hay que activarlo
if (numfp>listenfd) maxfd=numfp;
else maxfd=listenfd; // Actualizacion maxfd
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL, NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //SERVIDOR
//ATENCIÓN A LA LLEGADA DE RESPUESTA DE USUARIOS se recibe mensaje
if (len=recvfrom(listenfd, msg_rec1, MSG_SIZE, 0, (struct sockaddr*)cliaddr, &len)>0){ // Mensaje
msg_env=(char *)&cliaddr; // Se va formando el mensaje a tx al otro servicio.
msg_env=strcat(msg_env,
”+conectado”); // Se termina de formar el mensaje.
servaddr.sin_port=htons(SPAM); // Destino del datagrama.
sendto(listenfd, msg_env, strlen(msg_env), 0, (struct sockaddr*)&servaddr, sizeof (servaddr));
// Y se transmite el mensaje de información al otro servidor.
}
}
if ( FD_ISSET(numfd, &rset){ //BASEDEDATOS - ATENCIÓN A LA INTERRUPCIÓN DE LA BASE DE DATOS, siempre mientras haya listado
if (len=read(numfp, msg_rec2, MSG_SIZE)> 0){ // Se lee base de datos (Se puede medir el tamaño:9+1+sizeof(struct sockaddr))
numero=0;
valor1=strdup(msg_rec2);
valor2=valor1; //Extraccion número
for (i=0; i<9; i++) {
valor1++;
valor2++;
}
++valor2=’\0’;
Centro de Estudios NOVA – Cartagena
868-97-38-38
for (i=0; i<9; i++) {
numero=(atoi(valor1)*1ei)+numero;
valor1--;
--valor2=’\0’;
}
//Se extrae el número. Hay otras alternativas… (atof, atol).
valor2=strchr (msg_rec2, ‘:’);
cliaddr=(struct sockaddr_in) ++valor2; //Direccion
msg_env=genera (numero, (struct sockaddr*) &cliaddr); //Generamos mensaje
sendto(listenfd, msg_env, strlen(msg_env), 0, (struct sockaddr*)&cliaddr, sizeof (cliaddr));
}
}
} Fin del for
} Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
14 DE SEPTIEMBRE DE 2007
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP destinado a proporcionar concurrencia para el acceso a un servidor. El servicio consiste en que los usuarios se conectan, acceden (en caso de que sea posible),
y se arranca la aplicación que los usuarios requieren del servidor.
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes de los usuarios. Éstos se conectan cuando desean acceder a algún servicio del servidor. Habrá un total de N conexiones de este tipo.
− Gestionar la lectura de los comandos de usuarios. Los usuarios mediante la transmisión de comandos estándares de Linux pueden operar en la Maquina Servidor. Por tanto el servidor
debe ser capaz de ejecutar estos comandos (incluyendo como tales los servicios de acceso). No se limita el número de comando a ejecutar en cada sesión.
− Gestionar la devolución de parámetros según se produzcan o no en los servicios.
Se pide:
A) Código para aceptar la conexión de los usuarios y proporcionar la concurrencia. No existe control de acceso en esta máquina. (15%).
B) Gestione la ejecución de los servicios de usuarios. Para ello debe ejecutar las funciones fork() y execl(“comando”) siendo “comando” lo que se recibe del usuario. La función “fork” genera
un nuevo proceso (necesario para que el proceso sea concurrente) y execl ejecuta un comando. (20%)
C) Incluya la recepción de parámetros de salida de los servicios y su transmisión al usuario que lo ha ejecutado. Para ello suponga que cada servicio antes de terminar el funcionamiento
transmite un paquete UDP al puerto 4200+i (donde i es número del servicio: i є[1,N]) en el que se contiene esa información (no se requiere programar esta parte). Por ello, sea capaz de leer
los datos de salida (en formato char*), y a continuación transmitirlos al usuario indicado. (15%).
Otra información:
#define N 20
#define SERVIDOR “192.168.3.254”
#define SERVER_PORT 2399
#define MSG_SIZE 256
#define N 20
#define LISTENQ N // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N a la vez.
#define SERVER_PORT 2399 //Dato
#define SERVIDOR “192.168.3.254”
#define MSG_SIZE 256 //Tamaños maximos
int main (int argc, char **argv) {
int i, j, maxi, maxfd, listenfd, resto, connfd, sockfd, sockfd1, nready, numero, numero_resto;
int client [N]; // Array de descriptores de los usuario
int descriptores_locales[N]; //Se crea un array de “descriptores locales” para las respuestas de los
// servicios. (APARTADO C).
Centro de Estudios NOVA – Cartagena
ssize_t len;
fd_set rset, allset;
char * msg_rec; // Mensaje recibido = comando
socklen_t clilen;
struct sockaddr_in servaddr;
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
login[i]=NULL;
descriptors_locales [i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (--nready<=0) continue; //Previsión para más de una interrupción
} // Como no hay control de acceso la atención a la conexión es genérica.
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todos los usuarios conectados
continue;
if (FD_ISSET(sockfd1, &rset)) //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec,MSG_SIZE))>0) { //El proceso de cierre no se pide…
/* FALTA AÑADIR CÓDIGO DE ARRANQUE DE SERVICIOS*/
// Se ha enviado un comando -> arranque del servicio.
// Por cada servicio aparecerá un proceso nuevo.
//Aquí se debería incluir código para el apartado C:
if (descriptores_locales[i]<0){ //Posición vacía
descriptores_locales[i]=socket(AF_INET, SOCK_DGRAM, 0);
serveraddr.sin_port=htons(4200+i); //Actualizamos puerto…
if (bind (descriptores_locales[i], (struct sockaddr*)&serveraddr, sizeof(serveraddr))<0)
printf (“Error/n”);
FD_SET(descriptores_locales[i], &allset); //Se activa para cuando deba ser atendida.
}
//Hay que crear un proceso hijo para cada nuevo servicio.
if (pid=fork()<0)
printf(“Error /n”);
else if (pid == 0){ //Proceso hijo…
//Lo fundamental del hijo es cerrar todas las conexiones abiertas que no se deben solapar con el padre.
close (listenfd);
for (i = 0; i <= maxi; i++)
if (client[i]>-1) {
close(client[i]);
}
if(descriptores_locales[i] >0)
close(descriptores_locales[i])};
//Y la última tarea del hijo es generar el servicio:
if (execl(msg_rec)<0)
printf(“Error /n”);
//Esto dedicará este proceso a la ejecución del servicio. Cuando termine no hay nada más a hacer:
exit(0);
}else{
if (--nready<=0) continue; // No hay que hacer nada especial en el padre (servidor).
}
}
if (--nready<=0) continue; } //Previsión para más de una interrupción
}//Fin del for de clientes
Centro de Estudios NOVA – Cartagena
868-97-38-38
for (i=0; i<=maxi; i++){
if ((sockfd1 =descriptores_locales[i])<0) // Se van a comprobar todos los puertos
continue;
if (FD_ISSET(sockfd1, &rset)){ //Para el caso de que uno de ellos escriba algo
if ((len=recvfrom(sockfd1, msg_rec,MSG_SIZE, 0, NULL, NULL))>0) { //Se sabe el origen…
// Se responde
write (client[i], msg_rec, strlen(msg_rec)); // Y se transmite al usuario
}
// Por último se libera el socket:
FD_CLR(sockfd1, &allset);
close (sockfd1);
}
}
…
}// FIN del bucle principal for
}// Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Problema 2 (50% Ejercicios)
Se diseña un servicio de transmisión de archivos (FTP) en una red LAN, por lo que se opta por implementar un servicio TFTP, basado en UDP.
De esta forma, la aplicación consiste en atender simultáneamente la conexión de N usuarios que requieren descargar o cargar un archivo del servidor. La
instrucción para cada caso es “get” y “put” respectivamente.
Se pide:
A) Realice el código necesario de arranque del servidor y mantener la atención de las peticiones de los usuarios. No hay control de acceso, al ser una LAN
cerrada. (10%)
B) Realice el código para que el servicio actúe correctamente como servicio TFTP. Sólo hay dos instrucciones válidas (get y put) seguidas de un espacio y el
nombre de un archivo, es decir, “get filename” o “put filename”. En el caso de que se produzca una descarga deberá de abrirse el archivo, leerlo y mandarlo, y en
caso de carga se tratará de abrir el archivo, leerlo y guardarlo. (25%).
C) Añada un timeout al funcionamiento del servidor. Para agilizar el funcionamiento y que el sistema no se sature realice las modificaciones para que el tiempo
máximo de carga o descarga de cada archivo de manera individualizada no supere los 5 minutos. (15%).
Otra información:
#define MSG_SIZE 1024 //Tamaño máximo de los mensajes tanto de comunicación como de los archivos
#define PUERTO_SERVIDOR 3000
#define SERVIDOR “192.168.3.254”
#define N 20
El enunciado indica claramente la naturaleza de la aplicación es un servidor UDP. La concurrencia para clientes UDP es natural, es decir, UDP es concurrente por sí mismo. Lo que ya no es
concurrente automáticamente es cuando se tienen N clientes con N ficheros… hace falta select. Problema: gestionar concurrencia de diversos ficheros y relacionarlos con sus usuarios. Además pueden
ser de salida (get) o entrada (put).
#define SERVER_PORT 3000 //Dato
#define SERVIDOR “192.168.3.254” //Dato
#define MSG_SIZE 1024 //Se sobreentiende que completo
#define N 20 //Dato
int main (int argc, char **argv) {
int maxfd, listenfd, numero, sockfd1, nready;
char* numero propio;
ssize_t len;
fd_set rset, allset;
char * msg; //Mensaje (simultáneamente sólo va a hacer falta uno).
char *valor1, *valor2; //Auxiliares para separar mensaje
socklen_t clilen;
char buffer[MSG_SIZE];
struct timeval * tv; //Variable temporal necesaria para el apartado C
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
struct sockaddr_in clientddr [N]; // Array para guardar las direcciones de los clientes.
int enviar[N]; // Array para separar get/put, por ejemplo get=0; put =1; inactivo=-1;
Centro de Estudios NOVA – Cartagena
868-97-38-38
int ficheros[N]; //Descriptores de ficheros en uso.
FILE *fp;
if ((listenfd = socket(AF_INET,SOCK_DGRAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
// Todo servidor, incluye operación bind.
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
printf("ERROR \n");
exit (1);
}
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
enviar[i]=-1;
bzero(clientaddr[i], sizeof(struct sockaddr_in));
ficheros[i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset); //Socket hay que activarlo
long iteracion= 300000; // 300/0,001 Este valor depende del tipo de PC, velocidad procesador… Un valor de 1 mseg, es una buena opción
long iteraciones[N];
for (int k=0; k<N; k++)
iteraciones[k]=-1;
tv->tv_sec=0;
tv->tv_usec=1000;
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL, tv); //Bloqueo en espera de entradas
for (int k=0; k<N; k++){
if (enviar[k]>-1) // Está trabajando
iteraciones[k]--;
if (iteraciones[k]<=0) // Hay que cerrar…
if (enviar[k]==0){
close(fichero[i] // Se cierra fichero;
FD_CLR(fichero[i], &allset);
} // Actualizar atención a fichero.
bzero(clientaddr[i], sizeof(cliaddr)); enviar[k]=-1;
Centro de Estudios NOVA – Cartagena
868-97-38-38
iteraciones[k]=-1;
}else{
close(fichero[i]); //Fin de archivo
fichero[i]=-1;
bzero(clientaddr[i], sizeof(cliaddr));
enviar[i]=-1;
iteraciones[k]=-1;
}
}
if ( FD_ISSET(listenfd, &rset){ //CLIENTES
//ATENCIÓN A LA LLEGADA DE NUEVOS Clientes, pero hay que separarlos de una operación
// put en curso (comando y operación “put” tendrán el mismo descriptor de entrada).
if (len=recvfrom (listenfd, msg, MSG_SIZE, 0, (struct sockaddr *)&cliaddr, &clilen)<=0){
printf(“Error \n);
}else{
for (i=0; i=N-1; i++)
if (strcmp((char*)&clientaddr[i], (char *) &cliaddr))==0) {
int encontrado =1;
break;
}
if (encontrado ==0) { // Indica que el usuario no está conectado luego es nuevo;
//Buscamos si lo que quiere es “get”o “put”)
valor2=strchr(msg, “ “)++; //Nombre del fichero;
char *filename=strdup(valor2);
strncpy(valor1, msg, strlen(“put”)); // Se captura las tres primeras letras y…
for (i=0; i=N-1;i++)
if (enviar[i]==-1){ //No hay get ni put, luego está vacío..
if (strcmp(valor1, “get”)==0){ // Es una descarga
clientaddr[i]= cliadd;
enviar[i]=0; // Actualizamos variables.
fp=fopen (filename, “r”); // Se abre fichero;
fichero[i]=fileno(fp);
FD_SET(fileno(fp), &allset); //Actualizar atención a fichero.
}else{ // Es una carga…
clientaddr[i]= cliadd;
enviar[i]=1; // Actualizamos variables.
fp=fopen (filename, “w+”); // Se abre fichero;
fichero[i]=fileno(fp);
} // NO hace falta atención a escritura. ...
}
}else{ // Está conectado-> está realizando una parte del “put”.
//En i estará el índice del cliente. Simplemente queda escribir en fichero…
write (fichero[i], msg, strlen (msg)); //Se supone paquetes máximos… y no maz. Si FIN
Centro de Estudios NOVA – Cartagena
868-97-38-38
if (len<MSG_SIZE) {
for (j=0; j=len-1; j++)
msg++;
if (msg==’\0’) {
close(fichero[i]); //Fin de archivo
fichero[i]=-1;
bzero(clientaddr[i], sizeof(cliaddr));
enviar[i]=-1; // Actualizamos variables
if (--nready<=0) continue;
}
for (i=0; i<=N-1; i++){
if ((sockfd1 =ficheros[i])<0) // Se van a comprobar todos los archivos, para ver quién es
continue; // el que tiene que transmitir.
if (FD_ISSET(sockfd1, &rset)){ //Para el caso de que sea uno En modo get:
for (j=0; j<MSG_SIZE; j++) {
read (fichero[i], buffer[j], 1); // Se lee caracter a caracter…
if (buffer[j]=’\0’) { //Fin de archivo en lectura..
enviar[i]=-1; // Actualizamos variables.
close(fichero[i] // Se cierra fichero;
FD_CLR(fichero[i], &allset);
} // Actualizar atención a fichero.
break;
}
sendto{listenfd, buffer, strlen(buffer), 0, (struct sockaddr*)& clientaddr[i], sizeof(cliaddr)); //Se tx paquete del fichero…
if (buffer[j]=’\0’) //Fin de archivo en lectura
bzero(clientaddr[i], sizeof(cliaddr));
if (--nready<=0) continue;
}
}
} //Fin del for
} //Fin del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
05 DE FEBRERO DE 2008
Problema 2 (50% Ejercicios)
Se diseña un servicio de voto por Internet mediante la activación de un código que llevan las papeletas individualizadas de los votantes. Un votante, que quiere votar con este
sistema, solicita el envío de las papeletas “codificadas” de todas las candidaturas. Las papeletas le llegan a casa del votante con el código asignado y a partir de ese momento, y,
en el plazo estipulado, puede realizar la votación. Para ello se conecta con el “servidor electoral”, se identifica con el certificado de usuario que posee y le da acceso al sistema
electoral y por último introduce el código de la candidatura a la que pretende votar. Se pide:
A) Realice el código necesario del “servidor electoral”, para que atienda la conexión de los electores. El servicio ha de ser capaz primero de aceptar la conexión y luego de leer el
certificado que el usuario le envíe (le llega en el primer mensaje). Recuerde la concurrencia del proceso. (15%)
B) Realice el código para comprobar la autenticidad del certificado. Para ello, el servidor se lo envía a otro servicio, una autoridad de certificación, que responde con “OK” ó
“ERROR”. En el primer caso, el servicio pasará a autorizar la votación con un “OK” al votante, y en el segundo cerrará la conexión. Recuerde mantener la concurrencia del proceso.
(25%)
C) Por último, en caso de que todo el proceso sea válido, se procederá a permitir la lectura del código de votación. El código se almacenará en una base de datos (archivo)
denominado “VOTACION”, incluyéndose simplemente el código seguido de un “\n”, para dejarlo listo para el siguiente votante. (10%)
Otra información:
#define N 20
#define MSG_SIZE 32 //Tamaño máximo de los códigos
#define MSG_CERT 1024 // Tamaño máximo del certificado de usuario
#define PUERTO 3000
#define SERVIDOR “192.168.3.254”
# define AUTORIDAD “192.168.5.253”
# define PTO_AUTORIDAD 2999
El primer paso consiste en decidir si es un proceso UDP ó TCP. Dado el tipo de información que se pretende enviar, la respuesta es obvia, es un servidor concurrente TCP.
#define N 20
#define MSG_SIZE 32 //Tamaño máximo de los códigos
#define MSG_CERT 1024 // Tamaño máximo del certificado de usuario
#define PUERTO 3000
#define SERVIDOR “192.168.3.254”
# define AUTORIDAD “192.168.5.253”
# define PTO_AUTORIDAD 2999
#define LISTENQ N-1
int main ( ) {
int maxfd, listenfd, numero, sockfd1, connfd, sockfd;
char* numero propio;
ssize_t len;
fd_set rset, allset;
char * msg_rec; // Mensaje recibido
socklen_t clilen;
struct sockaddr_in servaddr;
FILE *fp; // Para el archivo
Centro de Estudios NOVA – Cartagena
int i, j, maxi, maxfd, listenfd, connfd, sockfd, sockfd1, nready;
int client [N]; // Array de descriptores de cada usuario
int estado[N]; // Habrá que conocer en qué instante de la votación se encuentra, por ejemplo: 0 sin
// certificado, 1 certificado leído y enviado y 2 listo para votar. Ahorra código…
int desc_autoridad[N]; //Habrá un array de sockets para la autoridad… serán lógicamente TCP
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(PUERTO);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
estado [i]=-1;
desc_autoridad[i]=-1;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
// Para el apartado C hay que abrir un archivo, se puede hacer ya…
fp=fopen(“VOTACION”, “w+”); // Se abre el archivo
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
if ( FD_ISSET(listenfd, &rset)){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta el usuario
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
estado[i]=0; // Actualizamos arrays
break;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
maxfd=connfd; // Los operarios van después de las líneas
if (i>maxi) maxi=i;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todos los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Para el caso de que uno de ellos escriba algo
if ((len=read(sockfd1, msg_rec, MSG_CERT))==0{ //Proceso de cierre en caso de
//CLIENTE. NO SE PIDE.
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
estado[i]=-1;
des_autoridad[i]=-1;
if (maxi==i) --maxi;
if (--nready<=0) break; else continue;
else{
if (estado[i]==0){ //Se espera certificado
// Hay que crear socket…. APARTADO B
//Dos partes 1º mandar a la autoridad certificado
// 2º Recibir resultado y actuar…
// Primera parte… mandar el certificado… hay que crear un socket TCP… cliente.
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (AUTORIDAD);
servaddr.sin_port=htons(PTO_AUTORIDAD);
if (connect(sockfd,(struct sockaddr*) &seraddr, sizeof(servaddr)))<= 0) {
printf ("ERROR \n");
exit(1);
}
//Una vez creado… se le manda el certificado por él…
write (sockfd, msg_rec, strlen(msg_rec));
//Actualizamos descriptors, arrays… para estar atentos a su respuesta
FD_SET(sockfd, &allset);
desc_autoridad[i]=sockfd;
estado[i]=1; //Estado de esperando respuesta..
}
if (estado[i]==2) // Cuando se manda el código… APARTADO C.
if (strlen(msg_rec)<=MSG_SIZE){ //Es un código:
Centro de Estudios NOVA – Cartagena
868-97-38-38
strcat (msg_rec,”\n”); //Se añade lo que se indica…
fputs (msg_rec, strlen (msg_rec), fp); // Se escribe en “VOTACION”
}
// Y si se desea se puede cerrar al votante… para que no vote más…
close(sockfd1);
FD_CLR(sockfd1,&allset);
client[i]=-1;
estado[i]=-1;
des_autoridad[i]=-1;
if (maxi==i) --maxi;
}
if (--nready<=0) break; else continue;
}
if (--nready<=0) break; //Previsión para más de una interrupción
}//Fin del for de clientes.
for (i=0; i<=maxi; i++){
if ((sockfd1 =desc_autoridad[i])<0) // Se van a comprobar las conexiones con
//“AUTORIDAD”
continue;
if (FD_ISSET(sockfd1, &rset)) {
// Atención a los sockets de autoridad…
//Cuando la autoridad responde, puede decir correcto o no…
if ((len=read(sockfd1, msg_rec, MSG_SIZE))!=0) //No se plantea el fallo de la AUTORIDAD
if (strcmp(msg_rec, “OK”)==0){ //Está bien…
estado[i]=2; //Se permite votar al votante
write(client[i],”OK”, strlen(“OK”)); // Se le comunica a éste
}else if (strcmp(msg_rec, “ERROR”)==0){ // Se le cierra al votante
close(client[i]);
FD_CLR(client[i],&allset);
client[i]=-1;
estado[i]=-1;
des_autoridad[i]=-1;
if (maxi==i)
--maxi;
}
}
FD_CLR(sockfd1, &allset);
desc_autoridad[i]=-1;
close (sockfd1); //Se cierra a la AUTORIDAD
if (--nready<=0) break; else continue;
}
} //Fin del for
} // Fin del for general
}//Fin de main
Centro de Estudios NOVA – Cartagena
868-97-38-38
03 DE FEBRERO DE 2009
Problema 1 (50% Ejercicios)
Se diseña un servicio TCP concurrente en el que el los clientes se conectan para acceder a información que están
generando aplicaciones que corren de manera autónoma en la misma máquina en el que se ejecuta el servicio. El gráfico representa las conexiones de las aplicaciones:
Entre las tareas del servicio, se encuentran:
− Gestionar las conexiones permanentes con los clientes. Éstos se conectan cuando desean acceder a una aplicación. Habrá un total de N conexiones de este tipo en el servidor.
− Gestionar las conexiones permanentes con los servicios (actúan como clientes). Éstas se conectan cuando son arrancadas. Habrá un total de M conexiones de este tipo.
− Gestionar la comunicación cliente-aplicación.
Se pide:
A) Código para aceptar la conexión de clientes. Éstos se conectan al servidor que les debe proporcionar servicio. El cliente indica el servicio (sólo uno por cliente) con un mensaje
que indica su nombre mediante el código: ##nombreaplicación##. Recuerde utilizar un solo select para todo el ejercicio.(10%)
B) Código para aceptar la conexión de los servicios. Éstos se conectan al servidor cuando se arrancan (en cualquier momento indeterminado). En el primer mensaje, indican su
nombre mediante el conocido formato ##nombreaplicación##. Dada la igualdad de formatos no se admite la conexión a clientes de la misma máquina. Recuerde utilizar el mismo select
para todo el ejercicio.(15%)
C) Código para relacionar cliente y servicio. Al recibir el nombre del servicio, el servidor busca el nombre entre las aplicaciones ya conectadas. En caso de éxito, relaciona en un
array cliente y servicio y responde al cliente con un “OK”. En caso de no encontrarlo se devuelve un “ERROR”.(25%)
Otra información:
#define N 20
#define M 29
#define SERVIDOR “192.168.5.254”
#define SERVER_PORT 2399
#define MSG_SIZE 256 // Tamaño maximo para los mensajes
#define N 20 //dato
#define M 29 //dato
#define LISTENQ N+M // Tamaño de lista de entrada de conexiones. En el peor de los casos son los N+M a la vez.
#define SERVER_PORT 2399 //Dato
#define SERVIDOR “192.168.5.254”
#define MSG_SIZE 256 //Tamaños maximos
struct servicios { //aunque no se pide, parece interesante crear una estructura para guardar los datos…
int sockfd; //puede llevar el descriptor de comunicación del .
char * nombre; //puede llevar el nombre del servicio
int descriptor_cliente; //también se le puede añadir el descriptor del cliente que lo está usando (apartado C)
Centro de Estudios NOVA – Cartagena
};
int main (int argc, char **argv) {
int i, j, maxi, maxiser, maxfd, listenfd, sockfd1, nready;
int client [N]; // Array de descriptores de los usuario
int relaciones[N]; //Array de relaciones de clientes, para relacionar cliente->aplicación
struct servicios local[M]; // Array de estructuras para los servicios
ssize_t len;
fd_set rset, allset;
char msg_rec[MSG_SIZE]; // Mensaje recibido.
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
bzero(&cliaddr, sizeof(cliaddr)); // Será necesario luego.
//Arranque del servidor concurrente
if ((listenfd = socket(AF_INET,SOCK_STREAM,0))<= 0) {
printf ("ERROR \n");
exit(1);
}
bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= inet_addr (SERVIDOR);
servaddr.sin_port=htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)<0) {
printf("ERROR \n");
exit (1);
}
listen (listenfd, LISTENQ); //Preparado para aceptar la entrada de los posibles clientes.
maxfd=listenfd;
maxi=-1;
maxiser=-1;
for (i=0; i<N;i++){ //Inicialización de los arrays de clientes
client[i]=-1;
relaciones[i]=-1;
}
for (i=0; i<M;i++){ //Hay dos tamaños distintos
local[i].sockfd=-1;
local[i].descriptor_cliente=-1;
local[i]->nombre=NULL;
}
FD_ZERO(&allset); //Inicialización de los conjuntos de descriptores
FD_ZERO(&rset);
FD_SET(listenfd, &allset);
for(;;){
rset=allset;
nready=select (maxfd+1, &rset, NULL,NULL,NULL); //Bloqueo en espera de entradas
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
//Ahora la conexiones de clientes y servicios
if ( FD_ISSET(listenfd, &rset){ //USUARIO NUEVO
clilen=sizeof(cliaddr);
connfd =accept(listenfd, (struct_sockaddr *) &cliaddr, &clilen); //Se acepta la conexión.
//CÓDIGO APARTADO B
//Aquí es cuando hay que diferenciar entre clientes y servicios
if (strcmp((char *) &servaddr.sin_addr.s_addr, (char *) &cliaddr.sin_addr.s_addr))==0){ //Si es servicio.
for (i=0; i<M; i++) //Actualización del array
if (local[i].sockfd<0) {
local[i].sockfd=connfd;
break;
}
if (i>maxiser) maxiser=i;
}else { //Si es cliente
for (i=0; i<N; i++) //Actualización del array
if (client[i]<0) {
client[i]=connfd;
break;
}
if (i>maxi) maxi=i;
}
FD_SET(connfd,&allset); //Actualización del conjunto de descriptores
if (connfd > maxfd) //Actualización de variables
maxfd=connfd;
if (--nready<=0) continue; //Previsión para más de una interrupción
}
for (i=0; i<=maxi; i++){
if ((sockfd1 =client[i])<0) // Se van a comprobar todas los clientes
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0){ //Se lee. No es vacío
if (relaciones[N]<0){ //No está relacionado… luego es posible que se quiera conectar con algún servicio
//Hay que comprobar que es un mensaje de nombre de servicio
if ((len>4) && (msg_rec[0]==’#’) && (msg_rec[1]== ’#’) && (msg_rec[len-2]== ’#’) && (msg_rec[len-1]== ’#’)){
//Esta es la condicion de mensaje de nombre de servicio.
//Ahora se pasa a comprobar si ese nombre está ya adquirido por el servidor (el servicio ya se ha
//conectado). Como el mensaje es el mismo no hay ni que extraer el nombre del formato.
j=0;
while (strcmp (msg_rec, local[j]->nombre)!=0){
if (j>maxirec) break;
j++;
}
if (j<=maxirec){ // Encontrado…. OK
Centro de Estudios NOVA – Cartagena
868-97-38-38
relaciones[i]=local[j].sockfd; //se actualizan relaciones
local[j].descriptor_cliente=sockfd1;
write (sockfd1, “OK”, strlen(“OK”)); // Se responde
}
else // No encontrado
write (sockfd1, “ERROR”, strlen(“ERROR”)); //Se responde
}
}
}
}
else {
// RESTO ATENCIÓN CLIENTE NO SE PIDE
}
} //FIN ATENCIÓN CLIENTES
if (--nready<=0) continue; // Por si no ha habido entrada de los servicios
//SERVICIOS
for (i=0; i<=maxirec; i++){
if ((sockfd1 =local.sockfd[i])<0) // Se van a comprobar todos los servicios
continue;
if (FD_ISSET(sockfd1, &rset)) //Se recibe algo
if((len=read(sockfd1, msg_rec, MSG_SIZE))> 0) //Se lee. No es vacío
//Habría que comprobar que es mensaje de identificación…
if ((len>4) && (msg_rec[0]==’#’) && (msg_rec[1]== ’#’) && (msg_rec[len-2]== ’#’) && (msg_rec[len-1]== ’#’))
//Aquí es muy sencillo… solo actualizar variable.
local[i]->nombre=strdup(msrg_rec); // Se copia
else {
}
else {
}
} //FIN ATENCIÓN SERVICIOS
// AQUÍ SE INCLUIRÍA EL CÓDIGO DEL APARTADO C, Y RESTO.
} //FIN del bucle principal for
} //FIN del main
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Centro de Estudios NOVA – Cartagena
868-97-38-38
Función strtok ANSI C
char *strtok(char *s1, const char *s2);
Rompe la cadena s1 en segmentos o tókens. Esta ruptura destruye s1, en el proceso. La forma de romper la cadena
depende de la secuencia de caracteres de la cadena s2. Estos caracteres se denominan [caracteres] delimitadores. La
función recorrerá la cadena en busca de alguno de los delimitadores de la cadena s2. Cuando lo encuentre, el proceso
se detiene, ya que tiene un token. Posteriores llamadas a strtok romperán la cadena s1 en otros tókens. Estas llamadas
pueden tener otra secuencia de delimitadores.
La primera llamada a strtok determina la cadena a romper, retornando el puntero al comienzo del primer token. Si se
recorrió la cadena s1 sin haber encontrado un delimitador, y aún no se ha obtenido el primer token, entonces la
función retornará un puntero nulo.
Posteriores llamadas retornarán más tókens. Si ya no encuentra más delimitadores, entonces retornará todos los
caracteres desde el último delimitador para ser el último token. Si ya se retornó el último token, entonces retornará un
puntero nulo con demás llamadas a la función.
Ejemplo:
#include <stdio.h>
#include <string.h>
int main()
{ /* inicializa el arreglo de cadena */
char cadena[] = "cadena:puerto1:puerto2:puerto3";
char *ptrToken; /* crea un apuntador char */
printf( "La cadena a dividir en tokens es: %s. Los tokens son: ", cadena);
ptrToken = strtok( cadena, ":" ); /* comienza la division en tokens del enunciado */
/* continua la division en tokens hasta que ptrToken se hace NULL */
while ( ptrToken != NULL ) {
printf( "%s\n", ptrToken );
ptrToken = strtok( NULL, " " ); /* obtiene el siguiente token */
} /* fin de while */
return 0; /* indica terminacion exitosa */
} /* fin de main */
Centro de Estudios NOVA – Cartagena
868-97-38-38
Función FORK()
A la hora de crear procesos linux provee de la función fork(). Fork crea un nuevo proceso a partir del proceso padre pero de
una manera distinta.
Cuando utilizamos la llamada al sistema fork, el proceso hijo creado es una copia exacta del padre (salvo por el PID y la
memoria que ocupa). Al proceso hijo se le facilita una copia de las variables del proceso padre y de los descriptores de fichero.
Es importante destacar que las variables del proceso hijo son una copia de las del padre (no se refieren físicamente a la misma
variable), por lo que modificar una variable en uno de los procesos no se refleja en el otro.
En el momento de la llamada a fork el proceso hijo:
• es una copia exacta del padre excepto el PID.
• tiene las mismas variables y ficheros abiertos.
• las variables son independientes (padre e hijo tienen distintas memorias).
• los ficheros son compartidos (heredan el descriptor).
El proceso hijo creado es una copia del padre (mismas instrucciones, misma memoria). Lo normal es que a continuación el hijo
ejecute una llamada al sistema exec. En cuanto al valor devuelto por el fork, se trata de un valor numérico que depende tanto
de si el fork se ha ejecutado correctamente como de si nos encontramos en el proceso padre o en el hijo.
• Si se produce algún error en la ejecución del fork, el valor devuelto es -1.
• Si no se produce ningún error y nos encontramos en el proceso hijo, el fork devuelve un 0.
• Si no se produce ningún error y nos encontramos en el proceso padre, el fork devuelve el PID asignado al proceso hijo.
/* fork.c - Ejecución conjunta de procesos padre e hijo */
#include <stdio.h>
#include <unistd.h>
main ()
{
printf ("Ejemplo de fork.\n");
printf ("Inicio del proceso padre. PID=%d\n", getpid ());
if (fork() == 0){
/* Proceso hijo */
printf ("Inicio proceso hijo. PID=%d, PPID=%d\n", getpid (), getppid ());
sleep (1);
}
else
{
/* Proceso padre */
printf ("Continuación del padre. PID=%d\n", getpid ());
sleep (1);
}
printf ("Fin del proceso %d\n", getpid ());
exit (0);
}
Ejemplo de fork.
Inicio del proceso padre. PID=5331
Continuación del padre. PID=5331
Inicio proceso hijo. PID=5332, PPID=5331
Fin del proceso 5332
Fin del proceso 5331
Descargar