Boek "Linux API. Omvattende gids"


Boek "Linux API. Omvattende gids"

Goeie middag Ek bied aan u aandag die boek "Linux API. 'n Omvattende gids" (vertaling van die boek Die Linux-programmeringskoppelvlak). Dit kan op die uitgewer se webwerf bestel word, en as jy die promosiekode toepas LinuxAPI , sal jy 30% afslag ontvang.

Uittreksel uit die boek vir verwysing:

Sokke: Bedienerargitektuur

In hierdie hoofstuk sal ons die basiese beginsels van die ontwerp van iteratiewe en parallelle bedieners bespreek, en ook kyk na 'n spesiale daemon genaamd inetd, wat dit makliker maak om internetbedienertoepassings te skep.

Iteratiewe en parallelle bedieners

Daar is twee algemene sok-gebaseerde netwerkbedienerargitekture:

  • iteratief: die bediener bedien kliënte een op 'n slag, verwerk eers 'n versoek (of verskeie versoeke) van een kliënt en beweeg dan aan na die volgende;

  • parallel: die bediener is ontwerp om verskeie kliënte gelyktydig te bedien.

'n Voorbeeld van 'n iteratiewe bediener gebaseer op EIEU-rye is reeds in Afdeling 44.8 aangebied.

Iteratiewe bedieners is gewoonlik slegs geskik in situasies waar kliëntversoeke redelik vinnig verwerk kan word, aangesien elke kliënt gedwing word om te wag totdat enige ander kliënte voor hom bedien is. 'n Algemene gebruiksgeval vir hierdie benadering is die uitruil van enkele versoeke en antwoorde tussen 'n kliënt en bediener.

Parallelle bedieners is geskik in gevalle waar elke versoek 'n aansienlike hoeveelheid tyd neem om te verwerk, of waar die kliënt en bediener betrokke is by lang boodskapuitruilings. In hierdie hoofstuk sal ons hoofsaaklik fokus op die tradisionele (en eenvoudigste) manier om parallelle bedieners te ontwerp, wat is om 'n aparte kinderproses vir elke nuwe kliënt te skep. Hierdie proses voer al die werk uit om die kliënt te bedien en eindig dan. Omdat elkeen van hierdie prosesse onafhanklik funksioneer, is dit moontlik om verskeie kliënte gelyktydig te bedien. Die hooftaak van die hoofbedienerproses (ouer) is om 'n aparte kind vir elke nuwe kliënt te skep (alternatiewelik kan drade van uitvoering geskep word in plaas van prosesse).

In die volgende afdelings sal ons kyk na voorbeelde van iteratiewe en parallelle internetdomein-sokbedieners. Hierdie twee bedieners implementeer 'n vereenvoudigde weergawe van die eggo-diens (RFC 862), wat 'n afskrif van enige boodskap wat deur 'n kliënt aan hom gestuur is, terugstuur.

Iteratiewe UDP-bediener eggo

In hierdie en die volgende afdeling sal ons die bedieners vir die eggo-diens bekendstel. Dit is beskikbaar op poort nommer 7 en werk oor beide UDP en TCP (hierdie poort is gereserveer, en daarom moet die eggo-bediener met administrateur-regte bestuur word).

Die eggo UDP-bediener lees voortdurend datagramme en stuur kopieë daarvan aan die sender terug. Aangesien die bediener net een boodskap op 'n slag hoef te verwerk, sal 'n iteratiewe argitektuur voldoende wees. Die koplêer vir die bedieners word in Listing 56.1 gewys.

Inskrywing 56.1. Koplêer vir programme id_echo_sv.c en id_echo_cl.c

#include "inet_sockets.h" /* Verklaar die funksies van ons sok */
#sluit "tlpi_hdr.h" in

#define SERVICE "echo" /* UDP diens naam */

#define BUF_SIZE 500 /* Maksimum grootte van datagramme wat
kan deur kliënt en bediener gelees word */
_________________________________________________________________sockets/id_echo.h

Lys 56.2 toon die bedienerimplementering. Die volgende punte is opmerklik:

  • om die bediener in daemon-modus te plaas, gebruik ons ​​die wordDaemon()-funksie vanaf afdeling 37.2;

  • om die program meer kompak te maak, gebruik ons ​​die biblioteek om met Internet-domeinsokke te werk, ontwikkel in afdeling 55.12;

  • as die bediener nie 'n antwoord aan die kliënt kan terugstuur nie, skryf dit 'n boodskap aan die log met behulp van die syslog()-oproep.

In 'n regte toepassing sal ons waarskynlik 'n beperking oplê op die frekwensie van aanteken van boodskappe met behulp van syslog(). Dit sal die moontlikheid uitskakel dat 'n aanvaller die stelsellogboek oorloop. Daarbenewens, moenie vergeet dat elke oproep na syslog() redelik duur is nie, aangesien dit fsync() by verstek gebruik.

Inskrywing 56.2. Iterasie bediener wat die UDP eggo diens implementeer

_________________________________________________________________sockets/id_echo_sv.c
#insluit
#sluit "id_echo.h" in
#sluit "word_daemon.h" in

int
hoof(int argc, char *argv[])
{
int sfd;
ssize_t numLees;
socklen_t len;
struct sockaddr_berging claddr;
char buff[BUF_SIZE];
char addrStr[IS_ADDR_STR_LEN];

if (word Daemon(0) == -1)
errExit("word Daemon");

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
if (sfd == -1) {
syslog(LOG_ERR, "Kon nie bedienersok (%s) skep nie",
strerror (fout));
uitgang (EXIT_FAILURE);

/* Ontvang datagramme en stuur afskrifte daarvan aan die senders terug */
}
vir (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

if (getalLees == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= aantalLees)
syslog(LOG_WARNING, "Fout tydens eggo reaksie op %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror (fout));
}
}
_________________________________________________________________sockets/id_echo_sv.c

Om die bediener se werking te toets, gebruik ons ​​die program van Listing 56.3. Dit gebruik ook die biblioteek om met internet-domein-sokke te werk, ontwikkel in afdeling 55.12. As die eerste opdragreëlargument neem die kliëntprogram die naam van die netwerknodus waarop die bediener geleë is. Die kliënt gaan 'n lus in waar dit elk van die oorblywende argumente na die bediener stuur as aparte datagramme, en dan lees en druk die datagramme wat dit van die bediener ontvang in reaksie.

Inskrywing 56.3. Kliënt vir UDP eggo diens

#sluit "id_echo.h" in

int
hoof(int argc, char *argv[])
{
int sfd, j;
size_t len;
ssize_t numLees;
char buff[BUF_SIZE];

if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s host msg...n", argv[0]);

/* Vorm die bedieneradres gebaseer op die eerste opdragreëlargument */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
if (sfd == -1)
fatal("Kon nie aan bedienersok koppel nie");

/* Stuur die oorblywende argumente na die bediener in die vorm van aparte datagramme */
vir (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (skryf(sfd, argv[j], len) != len)
fatal("gedeeltelike/mislukte skryf");

numRead = lees(sfd, buf, BUF_SIZE);
if (getalLees == -1)
errExit("lees");
printf("[%ld grepe] %.*sn", (lang) numRead, (int) numRead, buf);
}
uitgang (EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

Hieronder is 'n voorbeeld van wat ons sal sien wanneer die bediener en twee kliëntgevalle uitgevoer word:

$su // Voorregte word vereis om aan 'n gereserveerde poort te bind
wagwoord:
# ./id_echo_sv // Bediener gaan in agtergrondmodus
# exit // Gee administrateurregte prys
$ ./id_echo_cl localhost hallo wêreld // Hierdie kliënt stuur twee datagramme
[5 grepe] hallo // Die kliënt vertoon die antwoord wat vanaf die bediener ontvang is
[5 grepe] wêreld
$ ./id_echo_cl localhost totsiens // Hierdie kliënt stuur een datagram
[7 grepe] totsiens

Ek wens jou 'n aangename leeswerk)

Bron: linux.org.ru