Bog "Linux API. Omfattende guide"


Bog "Linux API. Omfattende guide"

God eftermiddag Jeg præsenterer for din opmærksomhed bogen "Linux API. En omfattende guide" (oversættelse af bogen Linux-programmeringsgrænsefladen). Den kan bestilles på udgiverens hjemmeside, og hvis du anvender kampagnekoden LinuxAPI , får du 30 % rabat.

Uddrag fra bogen til reference:

Sockets: Serverarkitektur

I dette kapitel vil vi diskutere det grundlæggende i at designe iterative og parallelle servere, og vi vil også se på en speciel dæmon kaldet inetd, som gør det nemmere at oprette internetserverapplikationer.

Iterative og parallelle servere

Der er to almindelige socket-baserede netværksserverarkitekturer:

  • iterativ: serveren betjener klienter én ad gangen, behandler først en anmodning (eller flere anmodninger) fra én klient og går derefter videre til den næste;

  • parallel: Serveren er designet til at betjene flere klienter samtidigt.

Et eksempel på en iterativ server baseret på FIFO-køer blev allerede præsenteret i afsnit 44.8.

Iterative servere er normalt kun egnede i situationer, hvor klientanmodninger kan behandles ret hurtigt, da hver klient er tvunget til at vente, indtil andre klienter foran den er blevet serveret. En almindelig anvendelse af denne tilgang er udvekslingen af ​​enkelte anmodninger og svar mellem en klient og server.

Parallelle servere er velegnede i tilfælde, hvor hver anmodning tager en betydelig mængde tid at behandle, eller hvor klienten og serveren deltager i lange beskedudvekslinger. I dette kapitel vil vi hovedsageligt fokusere på den traditionelle (og enkleste) måde at designe parallelle servere på, som er at skabe en separat underordnet proces for hver ny klient. Denne proces udfører alt arbejdet for at betjene kunden og slutter derefter. Fordi hver af disse processer fungerer uafhængigt, er det muligt at betjene flere klienter samtidigt. Hovedopgaven for hovedserverprocessen (forælder) er at oprette et separat underordnet for hver ny klient (alternativt kan der oprettes udførelsestråde i stedet for processer).

I de følgende afsnit vil vi se på eksempler på iterative og parallelle internet domæne socket-servere. Disse to servere implementerer en forenklet version af ekkotjenesten (RFC 862), som returnerer en kopi af enhver besked sendt til den af ​​en klient.

Iterativt UDP-serverekko

I dette og næste afsnit vil vi introducere serverne til ekkotjenesten. Den er tilgængelig på port nummer 7 og fungerer over både UDP og TCP (denne port er reserveret, og derfor skal ekkoserveren køres med administratorrettigheder).

Echo UDP-serveren læser kontinuerligt datagrammer og returnerer kopier af dem til afsenderen. Da serveren kun skal behandle én besked ad gangen, vil en iterativ arkitektur være tilstrækkelig. Overskriftsfilen for serverne er vist i liste 56.1.

Fortegnelse 56.1. Header-fil for programmerne id_echo_sv.c og id_echo_cl.c

#include "inet_sockets.h" /* Erklærer funktionerne i vores socket */
#include "tlpi_hdr.h"

#define SERVICE "echo" /* UDP-tjenestenavn */

#define BUF_SIZE 500 /* Maksimal størrelse af datagrammer, der
kan læses af klient og server */
__________________________________________________________________sockets/id_echo.h

Liste 56.2 viser serverimplementeringen. Følgende punkter er værd at bemærke:

  • for at sætte serveren i dæmontilstand, bruger vi funktionen becomeDaemon() fra afsnit 37.2;

  • for at gøre programmet mere kompakt bruger vi biblioteket til at arbejde med internet domæne sockets, udviklet i afsnit 55.12;

  • hvis serveren ikke kan returnere et svar til klienten, skriver den en besked til loggen ved hjælp af syslog()-kaldet.

I en rigtig applikation vil vi sandsynligvis pålægge en vis grænse for hyppigheden af ​​logning af meddelelser ved hjælp af syslog(). Dette ville eliminere muligheden for, at en hacker overfyldte systemloggen. Derudover skal du ikke glemme, at hvert kald til syslog() er ret dyrt, da det bruger fsync() som standard.

Fortegnelse 56.2. Iterationsserver, der implementerer UDP-ekkotjenesten

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

int
main(int argc, char *argv[])
{
int sfd;
ssize_t numRead;
socklen_t len;
struct sockaddr_storage claddr;
char buf[BUF_SIZE];
char addrStr[IS_ADDR_STR_LEN];

if (becomeDaemon(0) == -1)
errExit("becomeDaemon");

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
if (sfd == -1) {
syslog(LOG_ERR, "Kunne ikke oprette server socket (%s)",
strerror(fejl));
exit(EXIT_FAILURE);

/* Modtag datagrammer og returner kopier af dem til afsenderne */
}
til (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

hvis (antalLæs == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= antalLæst)
syslog(LOG_WARNING, "Fejl ved ekko af svar til %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(fejl));
}
}
_________________________________________________________________sockets/id_echo_sv.c

For at teste serverens funktion bruger vi programmet fra Listing 56.3. Det bruger også biblioteket til at arbejde med internetdomæne-sockets, udviklet i afsnit 55.12. Som det første kommandolinjeargument tager klientprogrammet navnet på den netværksknude, hvor serveren er placeret. Klienten går ind i en løkke, hvor den sender hvert af de resterende argumenter til serveren som separate datagrammer, og læser og udskriver derefter de datagrammer, den modtager fra serveren som svar.

Fortegnelse 56.3. Klient til UDP ekkotjeneste

#include "id_echo.h"

int
main(int argc, char *argv[])
{
int sfd, j;
size_t len;
ssize_t numRead;
char buf[BUF_SIZE];

if (argc < 2 || strcmp(argv[1], "--hjælp") == 0)
usageErr("%s værtsmeddelelse...n", argv[0]);

/* Form serveradressen baseret på det første kommandolinjeargument */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
hvis (sfd == -1)
fatal("Kunne ikke oprette forbindelse til serversocket");

/* Send de resterende argumenter til serveren i form af separate datagrammer */
for (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (skriv(sfd, argv[j], len) != len)
fatal("delvis/mislykket skrivning");

numRead = read(sfd, buf, BUF_SIZE);
hvis (antalLæs == -1)
errExit("læs");
printf("[%ld bytes] %.*sn", (lang) numRead, (int) numRead, buf);
}
exit(EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

Nedenfor er et eksempel på, hvad vi vil se, når du kører serveren og to klientforekomster:

$su // Privilegier er påkrævet for at binde sig til en reserveret port
Adgangskode:
# ./id_echo_sv // Server går i baggrundstilstand
# exit // Opgiv administratorrettigheder
$ ./id_echo_cl localhost hello world // Denne klient sender to datagrammer
[5 bytes] hej // Klienten viser svaret modtaget fra serveren
[5 bytes] verden
$ ./id_echo_cl localhost farvel // Denne klient sender et datagram
[7 bytes] farvel

Jeg ønsker dig god læsning)

Kilde: linux.org.ru