Knjiga “Linux API. Sveobuhvatan vodič"


Knjiga “Linux API. Sveobuhvatan vodič"

Dobar dan Predstavljam vašoj pažnji knjigu „Linux API. Sveobuhvatan vodič" (prijevod knjige Linux programski interfejs). Može se naručiti na web stranici izdavača i ako primijenite promotivni kod LinuxAPI , dobićete 30% popusta.

Izvod iz knjige za referencu:

Utičnice: Arhitektura servera

U ovom poglavlju ćemo razgovarati o osnovama dizajniranja iterativnih i paralelnih servera, a takođe ćemo pogledati poseban demon koji se zove inetd, koji olakšava kreiranje aplikacija za Internet server.

Iterativni i paralelni serveri

Postoje dvije uobičajene arhitekture mrežnog servera zasnovane na utičnicama:

  • iterativno: server opslužuje klijente jednog po jednog, prvo obrađuje zahtjev (ili nekoliko zahtjeva) od jednog klijenta, a zatim prelazi na sljedećeg;

  • paralelno: server je dizajniran da opslužuje više klijenata istovremeno.

Primjer iterativnog servera zasnovanog na FIFO redovima već je predstavljen u Odjeljku 44.8.

Iterativni serveri su obično prikladni samo u situacijama kada se zahtjevi klijenta mogu obraditi prilično brzo, budući da je svaki klijent primoran da čeka dok drugi klijenti ispred njega ne budu opsluženi. Uobičajeni slučaj upotrebe ovog pristupa je razmjena pojedinačnih zahtjeva i odgovora između klijenta i servera.

Paralelni serveri su prikladni u slučajevima kada je svakom zahtjevu potrebno dosta vremena za obradu, ili kada klijent i server učestvuju u dugoj razmjeni poruka. U ovom poglavlju ćemo se uglavnom fokusirati na tradicionalni (i najjednostavniji) način dizajniranja paralelnih servera, a to je stvaranje posebnog podređenog procesa za svakog novog klijenta. Ovaj proces obavlja sav posao da bi služio klijentu i zatim se završava. Budući da svaki od ovih procesa funkcioniše nezavisno, moguće je istovremeno opsluživati ​​više klijenata. Glavni zadatak glavnog serverskog procesa (roditelja) je kreiranje posebnog djeteta za svakog novog klijenta (alternativno, umjesto procesa mogu se kreirati niti izvršavanja).

U sljedećim odjeljcima ćemo pogledati primjere iterativnih i paralelnih socket servera internet domena. Ova dva servera implementiraju pojednostavljenu verziju usluge eho (RFC 862), koja vraća kopiju bilo koje poruke koju mu pošalje klijent.

Iterativni eho UDP servera

U ovom i sljedećem dijelu ćemo predstaviti servere za echo servis. Dostupan je na portu broj 7 i radi i preko UDP-a i TCP-a (ovaj port je rezervisan, i stoga echo server mora biti pokrenut sa administratorskim privilegijama).

Echo UDP server neprekidno čita datagrame i vraća njihove kopije pošiljaocu. Pošto server treba da obrađuje samo jednu po jednu poruku, iterativna arhitektura će biti dovoljna. Datoteka zaglavlja za servere je prikazana u Listingu 56.1.

Listing 56.1. Datoteka zaglavlja za programe id_echo_sv.c i id_echo_cl.c

#include "inet_sockets.h" /* Deklariše naše funkcije socketa */
#include "tlpi_hdr.h"

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

#define BUF_SIZE 500 /* Maksimalna veličina datagrama koji
može se čitati od strane klijenta i servera */
_____________________________________________________________________sockets/id_echo.h

Listing 56.2 prikazuje implementaciju servera. Sljedeće tačke su vrijedne pažnje:

  • da stavimo server u demonski mod, koristimo funkciju becomeDaemon() iz odjeljka 37.2;

  • kako bismo program učinili kompaktnijim, koristimo biblioteku za rad sa utičnicama internet domena, razvijenu u odjeljku 55.12;

  • ako server ne može vratiti odgovor klijentu, piše poruku u dnevnik koristeći syslog() poziv.

U stvarnoj aplikaciji, vjerovatno bismo nametnuli određeno ograničenje učestalosti evidentiranja poruka pomoću syslog(). Ovo bi eliminisalo mogućnost da napadač prepuni sistemski dnevnik. Osim toga, ne zaboravite da je svaki poziv syslog() prilično skup, budući da koristi fsync() po defaultu.

Listing 56.2. Iteracijski server koji implementira UDP echo uslugu

___________________________________________________________________sockets/id_echo_sv.c
#include
#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, "Ne mogu kreirati utičnicu servera (%s)",
strerror(errno));
izlaz(EXIT_FAILURE);

/* Primanje datagrama i vraćanje njihovih kopija pošiljaocima */
}
za (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

if (numRead == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= numRead)
syslog(LOG_WARNING, "Greška pri eho odgovoru na %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
___________________________________________________________________sockets/id_echo_sv.c

Za testiranje rada servera koristimo program iz Listinga 56.3. Takođe koristi biblioteku za rad sa utičnicama internet domena, razvijenu u odjeljku 55.12. Kao prvi argument komandne linije, program klijenta uzima ime mrežnog čvora na kojem se server nalazi. Klijent ulazi u petlju u kojoj svaki od preostalih argumenata šalje serveru kao zasebne datagrame, a zatim čita i ispisuje datagrame koje prima od servera kao odgovor.

Listing 56.3. Klijent za UDP echo uslugu

#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], "--pomoć") == 0)
usageErr("%s host msg…n", argv[0]);

/* Formirajte adresu servera na osnovu prvog argumenta komandne linije */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
ako (sfd == -1)
fatal("Ne mogu se povezati na utičnicu servera");

/* Pošaljite preostale argumente serveru u obliku zasebnih datagrama */
za (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (write(sfd, argv[j], len) != len)
fatal("djelimično/neuspješno pisanje");

numRead = read(sfd, buf, BUF_SIZE);
if (numRead == -1)
errExit("čitaj");
printf("[%ld bajtova] %.*sn", (dugo) numRead, (int) numRead, buf);
}
izlaz(EXIT_SUCCESS);
}
___________________________________________________________________sockets/id_echo_cl.c

Ispod je primjer onoga što ćemo vidjeti kada pokrenemo server i dvije klijentske instance:

$su // Potrebne su privilegije za vezanje na rezervirani port
Password:
# ./id_echo_sv // Server ide u pozadinski način rada
# izlaz // Odustati od administratorskih prava
$ ./id_echo_cl localhost hello world // Ovaj klijent šalje dva datagrama
[5 bajtova] zdravo // Klijent prikazuje odgovor primljen od servera
[5 bajtova] svijet
$ ./id_echo_cl localhost zbogom // Ovaj klijent šalje jedan datagram
[7 bajtova] zbogom

Želim vam prijatno čitanje)

izvor: linux.org.ru