Llibre "Linux API. Guia completa»


Llibre "Linux API. Guia completa»

Bona tarda Apunto a la vostra atenció el llibre “Linux API. Guia completa "(traducció del llibre La interfície de programació de Linux). Es pot demanar al lloc web de l'editor, i si apliqueu el codi promocional LinuxAPI tindreu un 30% de descompte.

Un fragment del llibre per a la seva revisió:

Sockets: arquitectura del servidor

En aquest capítol, parlarem dels conceptes bàsics del disseny de servidors iteratius i paral·lels, així com d'un dimoni especial inetd que facilita la creació d'aplicacions d'Internet al costat del servidor.

Iteració i servidors paral·lels

Hi ha dues arquitectures comunes de servidor de xarxa basades en sòcols:

  • iteratiu: el servidor serveix els clients d'un en un, primer processant la sol·licitud (o diverses) d'un client i després passant al següent;

  • paral·lel: el servidor està dissenyat per donar servei a diversos clients alhora.

La secció 44.8 ja ha proporcionat un exemple de servidor d'iteració basat en cues FIFO.

Els servidors d'iteració només són adequats en situacions en què les sol·licituds dels clients es poden processar amb força rapidesa, ja que cada client ha d'esperar fins que s'hagi atès qualsevol altre client que té davant. Un cas d'ús comú d'aquest enfocament és intercanviar sol·licituds i respostes individuals entre un client i un servidor.

Els servidors paral·lels són adequats en els casos en què cada sol·licitud triga un temps important a processar-se, o el client i el servidor tenen un llarg intercanvi de missatges. En aquest capítol, ens centrarem principalment en la manera tradicional (i més senzilla) de dissenyar servidors paral·lels, que és crear un procés fill separat per a cada client nou. Aquest procés fa tot el treball de servei al client, després del qual finalitza. Com que cadascun d'aquests processos funciona de manera independent, és possible atendre diversos clients alhora. La tasca principal del procés del servidor principal (parent) és crear un fill independent per a cada client nou (com alternativa, en lloc de processos, podeu crear fils d'execució).

A les seccions següents, veurem exemples de servidors iteratius i paral·lels basats en sockets de domini d'Internet. Aquests dos servidors implementen una versió simplificada del servei d'eco (RFC 862) que retorna una còpia de qualsevol missatge que li enviï el client.

iteració d'eco servidor udp

En aquesta i la següent secció, presentarem servidors per al servei echo. Està disponible al port número 7 i funciona tant a través d'UDP com de TCP (aquest port està reservat i, per tant, el servidor d'eco s'ha d'executar amb privilegis d'administrador).

El servidor Echo UDP llegeix constantment datagrames i en retorna una còpia al remitent. Com que el servidor només necessita processar un missatge a la vegada, aquí n'hi haurà prou amb una arquitectura iterativa. El fitxer de capçalera dels servidors es mostra al Llistat 56.1-XNUMX.

Llistat 56.1. Fitxer de capçalera per als programes id_echo_sv.c i id_echo_cl.c

#include "inet_sockets.h" /* Declara les nostres funcions de socket */
#include "tlpi_hdr.h"

#define SERVICE "eco" /* Nom del servei UDP */

#define BUF_SIZE 500 /* Mida màxima dels datagrames que
es pot llegir pel client i el servidor */
________________________________________________________________________ sockets/id_echo.h

El llistat 56.2-XNUMX mostra la implementació del servidor. Val la pena destacar els següents punts:

  • per posar el servidor en mode dimoni, utilitzem la funció becomeDaemon() de la Secció 37.2;

  • per fer el programa més compacte, fem servir la biblioteca de socket de domini d'Internet desenvolupada a la secció 55.12;

  • si el servidor no pot tornar una resposta al client, escriu un missatge al registre mitjançant la crida syslog().

En una aplicació real, molt probablement imposaríem un cert límit a la freqüència de registre de missatges mitjançant syslog(). Això eliminaria la possibilitat que un atacant desbordés el registre del sistema. A més, tingueu en compte que cada trucada a syslog() és bastant cara, ja que utilitza fsync() per defecte.

Llistat 56.2. Un servidor d'iteració que implementa el servei Echo UDP

_________________________________________________________________sockets/id_echo_sv.c
#incloure
#include "id_echo.h"
#include "become_daemon.h"

int
main(int argc, char *argv[])
{
int sfd;
ssiize_t numRead;
socklen_tlen;
struct sockaddr_storage clddr;
charbuf[BUF_SIZE];
char addrStr[IS_ADDR_STR_LEN];

si (esdevé Daemon(0) == -1)
errExit("convertir-se en dimoni");

sfd = inetBind (SERVEI, SOCK_DGRAM, NULL);
si (sfd == -1) {
syslog(LOG_ERR, "No s'ha pogut crear el sòcol del servidor (%s)",
strerror(errno));
sortida (EXIT_FAILURE);

/* Rebre datagrames i retornar còpies als remitents */
}
per(;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom (sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

si (númeroLlegit == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!=numRead)
syslog(LOG_WARNING, "Error fent ressò de la resposta a %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________sockets/id_echo_sv.c

Utilitzem el programa al llistat 56.3 per provar el servidor. També utilitza la biblioteca de socket de domini d'Internet desenvolupada a la Secció 55.12. El programa client pren el nom de l'amfitrió de la xarxa on es troba el servidor com a primer argument a la línia d'ordres. El client entra en un bucle on envia cadascun dels arguments restants al servidor com a datagrames separats, i després llegeix i envia els datagrames rebuts del servidor com a resposta.

Llistat 56.3. Client per al servei Echo UDP

#include "id_echo.h"

int
main(int argc, char *argv[])
{
int sfd,j;
mida_tlen;
ssiize_t numRead;
charbuf[BUF_SIZE];

if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s msj d'amfitrió...n", argv[0]);

/* Formeu l'adreça del servidor en funció del primer argument de la línia d'ordres */
sfd = inetConnect(argv[1], SERVEI, SOCK_DGRAM);
si (sfd == -1)
fatal("No s'ha pogut connectar al sòcol del servidor");

/* Envia la resta d'arguments al servidor com a datagrames separats */
per (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (escriviu (sfd, argv[j], len) != len)
fatal("escriptura parcial/fallida");

numRead = llegir (sfd, buf, BUF_SIZE);
si (númeroLlegit == -1)
errExit("llegir");
printf("[%ld bytes] %.*sn", (long) numRead, (int) numRead, buf);
}
sortida (EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

El següent és un exemple del que veurem quan iniciem el servidor i dues instàncies de client:

$ su // Requereix privilegis per vincular-se a un port reservat
clau:
# ./id_echo_sv // El servidor passa a un segon pla
# exit // Renuncia als drets d'administrador
$ ./id_echo_cl localhost hello world // Aquest client envia dos datagrames
[5 bytes] hola // El client envia la resposta rebuda del servidor
[5 bytes] món
$ ./id_echo_cl localhost adéu // Aquest client envia un datagrama
[7 bytes] adéu

Et desitjo una lectura agradable)

Font: linux.org.ru