Modelo Cliente-Servidor y Multiplexión de I/O (select)
Clasificación de Servidores
    Servidor Iterativo
  1. Espera por la llegada de un cliente
  2. Procesa el requerimiento de un cliente
  3. Envía la respuesta al cliente que envió el requerimiento
  4. Regresa al primer paso
Como consecuencia de esta interacción sólo un cliente puede ser atendido a la vez. Si la generación de la respuesta a un cleite demora,  otros requerimeintos no pueden ser atendidos concurrentemente.

Éste es el caso típico de servidores UDP (UDPServer.c   UDPClient.c). Como no hay una conexión establecida, los requerimientos de varios clientes se pueden traslapar conservando "atómicamente" las secuencias requeriemitno y respuesta para cada cliente.

Un servidor Iterativo basado en TCP es menos conveniete. En este caso hay una conexión con el cliente y sólo podemos atender a otro cliente cuando el previo ha concluido todas sus interacciones requerimiento-respuesta. La postergación de los otros clientes es mayor comparada con servidor UDP.

    Servidor Concurrente

  1. Espera por la llegada de un cliente
  2. Inicia un servidor para manejar los requerimientos del cliente. Esto involucra la creación de un nuevo proceso o hilo. Cuando el cliente se va (termina) el proceso o hilo también termina.
  3. Regresa al primer paso.
Éste es el caso típico de servidores TCP (TCPServer.c   TCPClient.c). El paso tres es alcanzado tan pronto como se inicia la atención del Cliente, por lo tanto múltiples cleintes pueden ser atendidos en forma concurrente. 

Destacar:

  1. Valores de puertas asignadas cuando se establece una conexión. El par (LocalPort, LocalIP, remotePort, RemoteIP) define cada conexión.
  2. Llamados para obtener información sobre la conexión: gethostname(), gethostbyname(), inet_ntoa(), inet_addr(), getsockname(), getpeername(), setsockopt().
  3. Configuración de puerta como reusable.
Una situación intermedia es creada cuando un servidor TCP atiende múltiples clientes en forma secuencial por requerimiento. Es un caso parecido al servidor Iterativo UDP, pero implementado con TCP. En este caso tenemos:
  1. Espera por la llegada de un cliente o nuevo requerimiento de un cliente ya conectado.
  2. Acepta la conexión de un nuevo cliente o procesa requerimiento de uno ya conectado
  3. Si se atendió un requerimiento, envía la respuesta al cliente que envió el requerimiento
  4. Regresa al primer paso
Para poder esperar requerimientos que pueden llegar por múltiples descriptores, se dispone de la función select().
Algunas otras técnicas que se parecen a select pero no siempre son las más adecuadas son:
  1. Creación de múltiples procesos y cada uno atiende cada descriptor (Se deben eliminar carreras críticas cuando se requiere comunicación entre los servidores de cada cliente),
  2. El uso de hilos es similar al caso anterior a cambio de procesos,
  3. Uso de I/O sin bloqueo (nonbloking I/O). En este caso el servidor hace una encuenta descriptor por descriptor identificando si hay algún requerimeinto, si no lo hay se retorna inmediatamente y se consulta el otro descriptor. Conduce a sistemas con "Busy-wait".
  4. Uso de I/O asíncrono. Se anuncia deseo de hacer I/O y el proceso es interrunpido cuando tiene algo. Esta opción no es soportada por todos los sitemas operativos.


Las funciones select y poll proveen un mecanismo para que los programas chequeen un grupo de descriptores y sepan cuando existe entradas, salidas, o alguna condición de excepción en alguno de ellos.

#include <sys/types.h>
#include <sys/time.h>

int select ( int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval * timeout_ptr );

Macros para manipular el conjunto de descriptores:
void FD_SET ( int fd, fd_set * fdset);

void FD_CLR ( int fd, fd_set * fdset);
void FD_ISSET ( int fd, fd_set * fdset);
void FD_ZERO ( fd_set * fdset);

struct timeval {
    long tv_sec;    /* segundos*/
    long tv_usec;    /* micro segundos */
}
 

Valores para timeout_ptr
  1. NULL : Espera por siempre. Espera infinita por actividad en alguno de los descriptores. Esta espera puede ser interrumpida por la llegada de una señal.
  2. tv_sec = tv_usec = 0:    No espera. Todos los descriptores son chequeados y se retorna inmediatamente.
  3. tv_sec != 0 ó tv_usec != 0 : Se espera hasta el tiempo indicado. Si hay actividad antes, se retorna inmediatamente, sino se retorna como el caso anterior al término del timeout.
Valores retornado por select:


Ejemplos

  1. Leyendo entradas de múltiples terminales (o ventanas xterm o telnet) select
  2. Como el caso de la tarea Servidor Iterativo Cliente