Libro “API de Linux. Guía completa"


Libro “API de Linux. Guía completa"

Buenas tardes Les presento el libro “Linux API. Una guía completa" (traducción del libro La interfaz de programación de Linux). Se puede pedir en la web de la editorial, y si aplicas el código promocional API de Linux , recibirás un 30% de descuento.

Extracto del libro como referencia:

Zócalos: Arquitectura del servidor

En este capítulo, discutiremos los conceptos básicos del diseño de servidores iterativos y paralelos, y también veremos un demonio especial llamado inetd, que facilita la creación de aplicaciones de servidor de Internet.

Servidores iterativos y paralelos

Hay dos arquitecturas de servidor de red basadas en sockets comunes:

  • iterativo: el servidor atiende a los clientes uno por uno, primero procesa una solicitud (o varias solicitudes) de un cliente y luego pasa al siguiente;

  • paralelo: el servidor está diseñado para atender a varios clientes simultáneamente.

En la Sección 44.8 ya se presentó un ejemplo de un servidor iterativo basado en colas FIFO.

Los servidores iterativos generalmente solo son adecuados en situaciones en las que las solicitudes de los clientes se pueden procesar con bastante rapidez, ya que cada cliente se ve obligado a esperar hasta que se haya atendido a los demás clientes que tiene delante. Un caso de uso común para este enfoque es el intercambio de solicitudes y respuestas únicas entre un cliente y un servidor.

Los servidores paralelos son adecuados en los casos en los que cada solicitud tarda una cantidad significativa de tiempo en procesarse, o en los que el cliente y el servidor participan en largos intercambios de mensajes. En este capítulo, nos centraremos principalmente en la forma tradicional (y más sencilla) de diseñar servidores paralelos, que consiste en crear un proceso hijo independiente para cada nuevo cliente. Este proceso realiza todo el trabajo para atender al cliente y luego finaliza. Debido a que cada uno de estos procesos opera de forma independiente, es posible atender a varios clientes simultáneamente. La tarea principal del proceso del servidor principal (padre) es crear un hijo separado para cada nuevo cliente (alternativamente, se pueden crear subprocesos de ejecución en lugar de procesos).

En las siguientes secciones, veremos ejemplos de servidores de socket de dominio de Internet iterativos y paralelos. Estos dos servidores implementan una versión simplificada del servicio de eco (RFC 862), que devuelve una copia de cualquier mensaje que le envíe un cliente.

Eco iterativo del servidor UDP

En esta y la siguiente sección presentaremos los servidores para el servicio de eco. Está disponible en el puerto número 7 y funciona tanto en UDP como en TCP (este puerto está reservado y, por lo tanto, el servidor de eco debe ejecutarse con privilegios de administrador).

El servidor echo UDP lee continuamente datagramas y devuelve copias de ellos al remitente. Dado que el servidor sólo necesita procesar un mensaje a la vez, una arquitectura iterativa será suficiente. El archivo de encabezado para los servidores se muestra en el Listado 56.1.

Listado 56.1. Archivo de encabezado para los programas id_echo_sv.c e id_echo_cl.c

#include "inet_sockets.h" /* Declara las funciones de nuestro socket */
#incluir "tlpi_hdr.h"

#define SERVICIO "echo" /* nombre del servicio UDP */

#define BUF_SIZE 500 /* Tamaño máximo de datagramas que
puede ser leído por el cliente y el servidor */
_____________________________________________________________________sockets/id_echo.h

El listado 56.2 muestra la implementación del servidor. Vale la pena señalar los siguientes puntos:

  • para poner el servidor en modo demonio, usamos la función convertDaemon() de la sección 37.2;

  • para hacer el programa más compacto, utilizamos la biblioteca para trabajar con sockets de dominio de Internet, desarrollada en la sección 55.12;

  • Si el servidor no puede devolver una respuesta al cliente, escribe un mensaje en el registro utilizando la llamada syslog().

En una aplicación real, probablemente impondríamos algún límite en la frecuencia de registro de mensajes usando syslog(). Esto eliminaría la posibilidad de que un atacante desborde el registro del sistema. Además, no olvide que cada llamada a syslog() es bastante costosa, ya que utiliza fsync() por defecto.

Listado 56.2. Servidor de iteración que implementa el servicio de eco UDP.

_________________________________________________________________sockets/id_echo_sv.c
#incluir
#incluir "id_echo.h"
#incluir "convertirse en_daemon.h"

int
principal (int argc, char * argv [])
{
int sfd;
ssize_t numRead;
calcetín_t len;
estructura sockaddr_storage claddr;
char buf[BUF_SIZE];
char addrStr[IS_ADDR_STR_LEN];

si (conviértete en Daemon(0) == -1)
errExit("convertirse en Daemon");

sfd = inetBind(SERVICIO, SOCK_DGRAM, NULL);
si (sfd == -1) {
syslog(LOG_ERR, "No se pudo crear el socket del servidor (%s)",
strerror (errno));
salir(EXIT_FAILURE);

/* Recibir datagramas y devolver copias de ellos a los remitentes */
}
por (;;) {
len = tamaño de (struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

si (numRead == -1)
errSalir("recvfrom");
if (enviar a (sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= númLectura)
syslog(LOG_WARNING, "Error al hacer eco de la respuesta a %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror (errno));
}
}
_________________________________________________________________sockets/id_echo_sv.c

Para probar el funcionamiento del servidor, utilizamos el programa del Listado 56.3. También utiliza la biblioteca para trabajar con sockets de dominio de Internet, desarrollada en la sección 55.12. Como primer argumento de la línea de comando, el programa cliente toma el nombre del nodo de red en el que se encuentra el servidor. El cliente ingresa a un bucle donde envía cada uno de los argumentos restantes al servidor como datagramas separados y luego lee e imprime los datagramas que recibe del servidor en respuesta.

Listado 56.3. Cliente para el servicio de eco UDP

#incluir "id_echo.h"

int
principal (int argc, char * argv [])
{
int sfd, j;
tamaño_t len;
ssize_t numRead;
char buf[BUF_SIZE];

si (argc < 2 || strcmp(argv[1], "--help") == 0)
useErr("%s mensaje de host…n", argv[0]);

/* Forma la dirección del servidor según el primer argumento de la línea de comando */
sfd = inetConnect(argv[1], SERVICIO, SOCK_DGRAM);
si (sfd == -1)
fatal("No se pudo conectar al socket del servidor");

/* Envía los argumentos restantes al servidor en forma de datagramas separados */
para (j = 2; j < argc; j++) {
len = strlen(argv[j]);
si (escribir(sfd, argv[j], len) != len)
fatal("escritura parcial/fallida");

numRead = leer(sfd, buf, BUF_SIZE);
si (numRead == -1)
errSalir("leer");
printf("[%ld bytes] %.*sn", (long) numRead, (int) numRead, buf);
}
salir (EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

A continuación se muestra un ejemplo de lo que veremos al ejecutar el servidor y dos instancias de cliente:

$su // Se requieren privilegios para vincularse a un puerto reservado
Contraseña:
# ./id_echo_sv // El servidor pasa al modo de fondo
# salir // Renunciar a los derechos de administrador
$ ./id_echo_cl localhost hola mundo // Este cliente envía dos datagramas
[5 bytes] hola // El cliente muestra la respuesta recibida del servidor
[5 bytes] mundo
$ ./id_echo_cl localhost adiós // Este cliente envía un datagrama
[7 bytes] adios

Te deseo una agradable lectura)

Fuente: linux.org.ru