Knjiga “Linux API. Obsežen vodnik"


Knjiga “Linux API. Obsežen vodnik"

Dober večer Predstavljam vam knjigo »Linux API. Obsežen vodnik« (prevod knjige Programski vmesnik Linux). Naročite ga lahko na spletni strani založbe in če uporabite promocijsko kodo LinuxAPI , boste prejeli 30% popust.

Odlomek iz knjige za referenco:

Vtičnice: Arhitektura strežnika

V tem poglavju bomo razpravljali o osnovah načrtovanja iterativnih in vzporednih strežnikov ter si ogledali poseben demon, imenovan inetd, ki olajša ustvarjanje aplikacij internetnega strežnika.

Iterativni in vzporedni strežniki

Obstajata dve skupni arhitekturi omrežnega strežnika, ki temelji na vtičnicah:

  • iterativno: strežnik služi odjemalcem enega za drugim, pri čemer najprej obdela zahtevo (ali več zahtev) enega odjemalca in nato nadaljuje z naslednjim;

  • vzporedno: strežnik je zasnovan tako, da služi več strankam hkrati.

Primer iterativnega strežnika, ki temelji na čakalnih vrstah FIFO, je bil že predstavljen v razdelku 44.8.

Iterativni strežniki so običajno primerni le v situacijah, ko je zahteve odjemalcev mogoče obdelati dokaj hitro, saj je vsak odjemalec prisiljen čakati, dokler niso postreženi kateri koli drugi odjemalci pred njim. Pogost primer uporabe tega pristopa je izmenjava posameznih zahtev in odgovorov med odjemalcem in strežnikom.

Vzporedni strežniki so primerni v primerih, ko obdelava vsake zahteve traja precej časa ali kjer si odjemalec in strežnik izmenjujeta dolga sporočila. V tem poglavju se bomo osredotočili predvsem na tradicionalni (in najenostavnejši) način oblikovanja vzporednih strežnikov, ki je ustvarjanje ločenega podrejenega procesa za vsakega novega odjemalca. Ta proces opravi vse delo za storitev stranki in se nato konča. Ker vsak od teh procesov deluje neodvisno, je mogoče služiti več strankam hkrati. Glavna naloga glavnega strežniškega procesa (nadrejenega) je ustvariti ločenega otroka za vsakega novega odjemalca (alternativno je mogoče ustvariti niti izvajanja namesto procesov).

V naslednjih razdelkih si bomo ogledali primere ponavljajočih se in vzporednih strežnikov vtičnic internetne domene. Ta dva strežnika izvajata poenostavljeno različico storitve echo (RFC 862), ki vrne kopijo katerega koli sporočila, ki mu ga pošlje odjemalec.

Iterativni odmev strežnika UDP

V tem in naslednjem razdelku bomo predstavili strežnike za storitev echo. Na voljo je na vratih številka 7 in deluje prek UDP in TCP (ta vrata so rezervirana, zato je treba strežnik echo izvajati s skrbniškimi pravicami).

Strežnik echo UDP neprestano bere datagrame in pošiljatelju vrača njihove kopije. Ker mora strežnik obdelati samo eno sporočilo naenkrat, bo zadostovala iterativna arhitektura. Datoteka glave za strežnike je prikazana v seznamu 56.1.

Seznam 56.1. Datoteka glave za programa id_echo_sv.c in id_echo_cl.c

#include "inet_sockets.h" /* Označi funkcije naše vtičnice */
#include "tlpi_hdr.h"

#define SERVICE "echo" /* ime storitve UDP */

#define BUF_SIZE 500 /* Največja velikost datagramov, ki
lahko bereta odjemalec in strežnik */
_________________________________________________________________sockets/id_echo.h

Listing 56.2 prikazuje implementacijo strežnika. Omeniti velja naslednje točke:

  • da strežnik postavimo v demonski način, uporabimo funkcijo becomeDaemon() iz razdelka 37.2;

  • da bi bil program bolj kompakten, uporabljamo knjižnico za delo z vtičnicami internetne domene, razvito v razdelku 55.12;

  • če strežnik odjemalcu ne more vrniti odgovora, zapiše sporočilo v dnevnik s klicem syslog().

V resnični aplikaciji bi verjetno določili določeno omejitev pogostosti beleženja sporočil z uporabo syslog(). To bi odpravilo možnost, da bi napadalec presegel sistemski dnevnik. Poleg tega ne pozabite, da je vsak klic syslog() precej drag, saj privzeto uporablja fsync().

Seznam 56.2. Iteracijski strežnik, ki izvaja storitev odmeva UDP

_________________________________________________________________sockets/id_echo_sv.c
#vključi
#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];

če (postaneDaemon(0) == -1)
errExit("postaniDaemon");

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
če (sfd == -1) {
syslog(LOG_ERR, "Ni bilo mogoče ustvariti strežniške vtičnice (%s)",
strerror(errno));
izhod(EXIT_FAILURE);

/* Sprejmi datagrame in vrni njihove kopije pošiljateljem */
}
za (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

če (numRead == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= numRead)
syslog(LOG_WARNING, "Napaka pri odzivu na %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________sockets/id_echo_sv.c

Za preizkus delovanja strežnika uporabimo program iz Listinga 56.3. Uporablja tudi knjižnico za delo z vtičnicami internetne domene, razvito v razdelku 55.12. Kot prvi argument ukazne vrstice odjemalski program vzame ime omrežnega vozlišča, na katerem se nahaja strežnik. Odjemalec vstopi v zanko, kjer pošlje vsakega od preostalih argumentov strežniku kot ločene datagrame, nato pa prebere in natisne datagrame, ki jih prejme od strežnika kot odgovor.

Seznam 56.3. Odjemalec za storitev UDP echo

#include "id_echo.h"

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

if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s gostiteljsko sporočilo…n", argv[0]);

/* Oblikuj naslov strežnika na podlagi prvega argumenta ukazne vrstice */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
če (sfd == -1)
fatal("Ni bilo mogoče vzpostaviti povezave s strežniško vtičnico");

/* Pošlji preostale argumente strežniku v obliki ločenih datagramov */
za (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (write(sfd, argv[j], len) != len)
fatal("delno/neuspešno pisanje");

numRead = read(sfd, buf, BUF_SIZE);
če (numRead == -1)
errExit("branje");
printf("[%ld bajtov] %.*sn", (dolgo) numRead, (int) numRead, buf);
}
izhod(EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

Spodaj je primer tega, kar bomo videli, ko zaženemo strežnik in dva primerka odjemalca:

$su // Privilegiji so potrebni za povezovanje z rezerviranimi vrati
geslo:
# ./id_echo_sv // Strežnik gre v način ozadja
# izhod // Opustite skrbniške pravice
$ ./id_echo_cl localhost hello world // Ta odjemalec pošlje dva datagrama
[5 bajtov] zdravo // Odjemalec prikaže odgovor, prejet s strežnika
[5 bajtov] svet
$ ./id_echo_cl localhost adijo // Ta odjemalec pošlje en datagram
[7 bajtov] nasvidenje

Želim vam prijetno branje)

Vir: linux.org.ru