Boek “Linux API. Uitgebreide gids"


Boek “Linux API. Uitgebreide gids"

Goedemiddag Ik presenteer onder uw aandacht het boek “Linux API. Een uitgebreide gids" (vertaling van het boek De Linux-programmeerinterface). Het kan worden besteld op de website van de uitgever en als u de promotiecode toepast LinuxAPI , ontvangt u 30% korting.

Ter referentie een fragment uit het boek:

Sockets: serverarchitectuur

In dit hoofdstuk bespreken we de basisprincipes van het ontwerpen van iteratieve en parallelle servers, en kijken we ook naar een speciale daemon genaamd inetd, die het gemakkelijker maakt om internetserverapplicaties te maken.

Iteratieve en parallelle servers

Er zijn twee veelgebruikte socketgebaseerde netwerkserverarchitecturen:

  • iteratief: de server bedient klanten één voor één, waarbij eerst een verzoek (of meerdere verzoeken) van de ene klant wordt verwerkt en vervolgens doorgaat naar de volgende;

  • parallel: de server is ontworpen om meerdere clients tegelijkertijd te bedienen.

Een voorbeeld van een iteratieve server op basis van FIFO-wachtrijen werd al gegeven in Paragraaf 44.8.

Iteratieve servers zijn doorgaans alleen geschikt in situaties waarin clientverzoeken redelijk snel kunnen worden verwerkt, omdat elke client moet wachten totdat alle andere clients die voor hem staan, zijn bediend. Een veelvoorkomend gebruiksscenario voor deze aanpak is de uitwisseling van afzonderlijke verzoeken en antwoorden tussen een client en een server.

Parallelle servers zijn geschikt in gevallen waarin elk verzoek een aanzienlijke hoeveelheid tijd kost om te verwerken, of waar de client en server lange berichtenuitwisselingen voeren. In dit hoofdstuk zullen we ons vooral concentreren op de traditionele (en eenvoudigste) manier om parallelle servers te ontwerpen, namelijk het creëren van een afzonderlijk onderliggend proces voor elke nieuwe client. Dit proces voert al het werk uit om de klant te bedienen en eindigt dan. Doordat elk van deze processen onafhankelijk functioneert, is het mogelijk om meerdere klanten tegelijk te bedienen. De hoofdtaak van het hoofdserverproces (ouder) is het creëren van een afzonderlijk kind voor elke nieuwe client (als alternatief kunnen er uitvoeringsthreads worden gemaakt in plaats van processen).

In de volgende paragrafen zullen we voorbeelden bekijken van iteratieve en parallelle socketservers voor internetdomeinen. Deze twee servers implementeren een vereenvoudigde versie van de echoservice (RFC 862), die een kopie retourneert van elk bericht dat door een client naar de server wordt verzonden.

Iteratieve UDP-serverecho

In deze en de volgende sectie introduceren we de servers voor de echoservice. Het is beschikbaar op poortnummer 7 en werkt via zowel UDP als TCP (deze poort is gereserveerd en daarom moet de echoserver worden uitgevoerd met beheerdersrechten).

De echo-UDP-server leest voortdurend datagrammen en stuurt kopieën ervan terug naar de afzender. Omdat de server slechts één bericht tegelijk hoeft te verwerken, volstaat een iteratieve architectuur. Het headerbestand voor de servers wordt weergegeven in Listing 56.1.

Lijst 56.1. Headerbestand voor programma's id_echo_sv.c en id_echo_cl.c

#include "inet_sockets.h" /* Verklaart de functies van onze socket */
#include "tlpi_hdr.h"

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

#define BUF_SIZE 500 /* Maximale grootte van datagrammen die
kan worden gelezen door client en server */
___________________________________________________________________________________sockets/id_echo.h

Listing 56.2 toont de serverimplementatie. De volgende punten zijn het vermelden waard:

  • om de server in de daemon-modus te zetten, gebruiken we de functie BecomeDaemon() uit paragraaf 37.2;

  • om het programma compacter te maken, gebruiken we de bibliotheek voor het werken met internetdomeinsockets, ontwikkeld in sectie 55.12;

  • als de server geen antwoord kan retourneren naar de client, schrijft hij een bericht naar het log met behulp van de syslog()-aanroep.

In een echte toepassing zouden we waarschijnlijk een limiet stellen aan de frequentie van het loggen van berichten met behulp van syslog(). Dit zou de mogelijkheid elimineren dat een aanvaller het systeemlogboek overloopt. Vergeet bovendien niet dat elke aanroep van syslog() behoorlijk duur is, omdat het standaard fsync() gebruikt.

Lijst 56.2. Iteratieserver die de UDP-echoservice implementeert

_________________________________________________________________sockets/id_echo_sv.c
#erbij betrekken
#include "id_echo.h"
#include "word_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 (wordDaemon(0) == -1)
errExit("Daemon geworden");

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
als (sfd == -1) {
syslog(LOG_ERR, "Kon server socket (%s) niet aanmaken",
strerror(errno));
afsluiten(EXIT_FAILURE);

/* Ontvang datagrammen en stuur kopieën ervan terug naar de afzenders */
}
voor (;;) {
len = groottevan(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

als (numRead == -1)
errExit("recvvan");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= aantalLezen)
syslog(LOG_WARNING, "Fout bij het echoën van de reactie op %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________sockets/id_echo_sv.c

Om de werking van de server te testen, gebruiken we het programma uit Listing 56.3. Het gebruikt ook de bibliotheek voor het werken met internetdomeinsockets, ontwikkeld in sectie 55.12. Als eerste opdrachtregelargument neemt het clientprogramma de naam aan van het netwerkknooppunt waarop de server zich bevindt. De client komt in een lus waarin hij elk van de resterende argumenten als afzonderlijke datagrammen naar de server stuurt, en vervolgens de datagrammen leest en afdrukt die hij als reactie van de server ontvangt.

Lijst 56.3. Client voor UDP-echoservice

#include "id_echo.h"

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

als (argc < 2 || strcmp(argv[1], "--help") == 0)
gebruikErr("%s hostbericht...n", argv[0]);

/* Vorm het serveradres op basis van het eerste opdrachtregelargument */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
als (sfd == -1)
fatal("Kan geen verbinding maken met de server socket");

/* Stuur de overige argumenten naar de server in de vorm van afzonderlijke datagrammen */
voor (j = 2; j <argc; j++) {
len = strlen(argv[j]);
als (schrijf(sfd, argv[j], len) != len)
fatal("gedeeltelijk/mislukt schrijven");

numRead = lezen(sfd, buf, BUF_SIZE);
als (numRead == -1)
errExit("lezen");
printf("[%ld bytes] %.*sn", (lang) numRead, (int) numRead, buf);
}
afsluiten (EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

Hieronder ziet u een voorbeeld van wat we zullen zien bij het uitvoeren van de server en twee clientinstanties:

$su // Er zijn rechten vereist om te binden aan een gereserveerde poort
Wachtwoord:
# ./id_echo_sv // Server gaat naar de achtergrondmodus
# exit // Geef beheerdersrechten op
$ ./id_echo_cl localhost hallo wereld // Deze client verzendt twee datagrammen
[5 bytes] hallo // De client geeft het antwoord weer dat is ontvangen van de server
[5 bytes] wereld
$ ./id_echo_cl localhost afscheid // Deze client verzendt één datagram
[7 bytes] tot ziens

Ik wens u veel leesplezier)

Bron: linux.org.ru