Libri “Linux API. Udhëzues gjithëpërfshirës"


Libri “Linux API. Udhëzues gjithëpërfshirës"

Mirembrema Ju paraqes në vëmendje librin “Linux API. Një udhëzues gjithëpërfshirës" (përkthimi i librit Ndërfaqja e programimit Linux). Mund të porositet në faqen e internetit të botuesit dhe nëse aplikoni kodin promocional LinuxAPI , do të përfitoni 30% zbritje.

Fragment nga libri për referencë:

Prizat: Arkitektura e Serverit

Në këtë kapitull, ne do të diskutojmë bazat e dizajnimit të serverëve iterativë dhe paralelë, dhe gjithashtu do të shohim një daemon të veçantë të quajtur inetd, i cili e bën më të lehtë krijimin e aplikacioneve të serverëve në Internet.

Serverë iterativë dhe paralelë

Ekzistojnë dy arkitektura të zakonshme të serverëve të rrjetit të bazuara në fole:

  • përsëritëse: serveri u shërben klientëve një nga një, fillimisht përpunon një kërkesë (ose disa kërkesa) nga një klient dhe më pas kalon te tjetri;

  • paralel: serveri është krijuar për t'u shërbyer klientëve të shumtë në të njëjtën kohë.

Një shembull i një serveri përsëritës të bazuar në radhët FIFO është paraqitur tashmë në Seksionin 44.8.

Serverët përsëritës janë zakonisht të përshtatshëm vetëm në situatat kur kërkesat e klientit mund të përpunohen mjaft shpejt, pasi çdo klient është i detyruar të presë derisa të shërbehet ndonjë klient tjetër përballë tij. Një rast i zakonshëm i përdorimit për këtë qasje është shkëmbimi i kërkesave dhe përgjigjeve të vetme midis një klienti dhe serveri.

Serverët paralelë janë të përshtatshëm në rastet kur çdo kërkesë kërkon një kohë të konsiderueshme për t'u përpunuar, ose kur klienti dhe serveri përfshihen në shkëmbime të gjata mesazhesh. Në këtë kapitull, ne do të fokusohemi kryesisht në mënyrën tradicionale (dhe më të thjeshtë) të dizajnimit të serverëve paralelë, që është krijimi i një procesi të veçantë fëmijësh për çdo klient të ri. Ky proces kryen të gjithë punën për t'i shërbyer klientit dhe më pas përfundon. Për shkak se secili prej këtyre proceseve funksionon në mënyrë të pavarur, është e mundur që t'u shërbejmë klientëve të shumtë në të njëjtën kohë. Detyra kryesore e procesit të serverit kryesor (prindi) është të krijojë një fëmijë të veçantë për çdo klient të ri (përndryshe, temat e ekzekutimit mund të krijohen në vend të proceseve).

Në seksionet në vijim, do të shikojmë shembuj të serverëve të foleve të domenit të internetit iterativë dhe paralelë. Këta dy serverë zbatojnë një version të thjeshtuar të shërbimit echo (RFC 862), i cili kthen një kopje të çdo mesazhi të dërguar nga një klient.

Jehonë përsëritëse e serverit UDP

Në këtë dhe në pjesën tjetër do të prezantojmë serverët për shërbimin echo. Është i disponueshëm në portin numër 7 dhe funksionon si mbi UDP ashtu edhe mbi TCP (kjo portë është e rezervuar, prandaj serveri echo duhet të ekzekutohet me privilegje administratori).

Serveri echo UDP lexon vazhdimisht datagramet dhe i kthen kopjet e tyre dërguesit. Meqenëse serveri duhet të përpunojë vetëm një mesazh në një kohë, një arkitekturë përsëritëse do të mjaftojë. Skedari i kokës për serverët është paraqitur në Listimin 56.1.

Listimi 56.1. Skedari i kokës për programet id_echo_sv.c dhe id_echo_cl.c

#include "inet_sockets.h" /* Deklaron funksionet e prizës sonë */
#include "tlpi_hdr.h"

#define SERVICE "echo" /* Emri i shërbimit UDP */

#define BUF_SIZE 500 /* Madhësia maksimale e datagrameve që
mund të lexohet nga klienti dhe serveri */
___________________________________________________________________ priza/id_echo.h

Lista 56.2 tregon zbatimin e serverit. Vlen të përmenden pikat e mëposhtme:

  • për të vendosur serverin në modalitetin daemon, ne përdorim funksionin beneDaemon() nga seksioni 37.2;

  • për ta bërë programin më kompakt, ne përdorim bibliotekën për të punuar me bazat e domenit të Internetit, të zhvilluara në seksionin 55.12;

  • nëse serveri nuk mund t'i kthejë një përgjigje klientit, ai shkruan një mesazh në log duke përdorur thirrjen syslog().

Në një aplikacion real, ne ka të ngjarë të vendosim njëfarë kufiri në frekuencën e regjistrimit të mesazheve duke përdorur syslog(). Kjo do të eliminonte mundësinë që një sulmues të tejmbushte regjistrin e sistemit. Përveç kësaj, mos harroni se çdo thirrje në syslog() është mjaft e shtrenjtë, pasi përdor fsync() si parazgjedhje.

Listimi 56.2. Serveri i përsëritjes që zbaton shërbimin UDP echo

_________________________________________________________________ fole/id_echo_sv.c
#përfshi
#include "id_echo.h"
#include "become_daemon.h"

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

nëse (bëhet Daemon (0) == -1)
errExit ("bemeDaemon");

sfd = inetBind(SHËRBIMI, SOCK_DGRAM, NULL);
nëse (sfd == -1) {
syslog(LOG_ERR, "Nuk mund të krijohej foleja e serverit (%s)",
strerror(errno));
dalje (EXIT_FAILURE);

/* Merrni datagrame dhe ktheni kopjet e tyre dërguesit */
}
per (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

nëse (numLexuar == -1)
errExit ("recvfrom");
nëse (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= numLexo)
syslog(LOG_WARNING, "Gabim në përgjigjen e përgjigjes në %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________ fole/id_echo_sv.c

Për të testuar funksionimin e serverit, ne përdorim programin nga Lista 56.3. Ai përdor gjithashtu bibliotekën për të punuar me bazat e domenit të Internetit, të zhvilluara në seksionin 55.12. Si argumenti i parë i linjës së komandës, programi i klientit merr emrin e nyjes së rrjetit në të cilën ndodhet serveri. Klienti hyn në një lak ku dërgon secilin nga argumentet e mbetura në server si datagrame të veçanta, dhe më pas lexon dhe printon datagramet që merr nga serveri si përgjigje.

Listimi 56.3. Klient për shërbimin UDP echo

#include "id_echo.h"

int
kryesore (int argc, char *argv[])
{
int sfd, j;
madhësia_t len;
ssize_t numRead;
char buf[BUF_SIZE];

nëse (argc < 2 || strcmp(argv[1], "--ndihmë") == 0)
usageErr("%s host host msg…n", argv[0]);

/* Formoni adresën e serverit bazuar në argumentin e parë të linjës së komandës */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
nëse (sfd == -1)
fatal ("Nuk mund të lidhej me prizën e serverit");

/* Dërgoni argumentet e mbetura në server në formën e të dhënave të veçanta */
për (j = 2; j < argc; j++) {
len = strlen(argv[j]);
nëse (shkruaj(sfd, argv[j], len) != len)
fatal ("shkrim i pjesshëm / i dështuar");

numRead = lexo (sfd, buf, BUF_SIZE);
nëse (numLexuar == -1)
errExit ("lexo");
printf("[%ld bytes] %.*sn", (e gjate) numRead, (int) numRead, buf);
}
dalje (EXIT_SUCCESS);
}
_________________________________________________________________ fole/id_echo_cl.c

Më poshtë është një shembull i asaj që do të shohim kur ekzekutojmë serverin dhe dy shembuj të klientit:

$su // Kërkohen privilegje për t'u lidhur me një port të rezervuar
fjalëkalimi:
# ./id_echo_sv // Serveri kalon në modalitetin e sfondit
# dalje // Hiq dorë nga të drejtat e administratorit
$ ./id_echo_cl localhost hello world // Ky klient dërgon dy datagrame
[5 bytes] përshëndetje // Klienti shfaq përgjigjen e marrë nga serveri
[5 bytes] botë
$ ./id_echo_cl localhost mirupafshim // Ky klient dërgon një datagram
[7 bytes] mirupafshim

Ju uroj lexim të këndshëm)

Burimi: linux.org.ru