Libro “Linukso API. Ampleksa Gvidilo"


Libro “Linukso API. Ampleksa Gvidilo"

Bonan posttagmezon Mi prezentas al via atento la libron “Linux API. Ampleksa gvidilo" (traduko de la libro La Linuksa Programado-Interfaco). Ĝi povas esti mendita en la retejo de la eldonejo, kaj se vi aplikas la reklaman kodon LinuxAPI , vi ricevos rabaton de 30%.

Eltiraĵo de la libro por referenco:

Ingoj: Servila Arkitekturo

En ĉi tiu ĉapitro, ni diskutos la bazaĵojn de desegnado de ripetaj kaj paralelaj serviloj, kaj ankaŭ rigardos specialan demonon nomitan inetd, kiu faciligas krei interretan servilojn.

Ripetemaj kaj paralelaj serviloj

Ekzistas du oftaj ingo-bazitaj retservilaj arkitekturoj:

  • ripeta: la servilo servas klientojn unuope, unue prilaborante peton (aŭ plurajn petojn) de unu kliento kaj poste pluirante al la sekva;

  • paralela: la servilo estas dizajnita por servi plurajn klientojn samtempe.

Ekzemplo de ripeta servilo bazita sur FIFO-vostoj jam estis prezentita en Sekcio 44.8.

Ripetemaj serviloj estas kutime nur taŭgaj en situacioj kie klientpetoj povas esti procesitaj sufiĉe rapide, ĉar ĉiu kliento estas devigita atendi ĝis iuj aliaj klientoj antaŭ ĝi estas servitaj. Ofta uzokazo por tiu aliro estas la interŝanĝo de ununuraj petoj kaj respondoj inter kliento kaj servilo.

Paralelaj serviloj taŭgas en kazoj kie ĉiu peto prenas signifan tempon por prilabori, aŭ kie la kliento kaj servilo okupiĝas pri longaj mesaĝaj interŝanĝoj. En ĉi tiu ĉapitro, ni ĉefe koncentriĝos pri la tradicia (kaj plej simpla) maniero desegni paralelajn servilojn, kiu estas krei apartan infanprocezon por ĉiu nova kliento. Ĉi tiu procezo plenumas la tutan laboron por servi la klienton kaj poste finiĝas. Ĉar ĉiu el ĉi tiuj procezoj funkcias sendepende, eblas servi plurajn klientojn samtempe. La ĉefa tasko de la ĉefa servila procezo (gepatro) estas krei apartan infanon por ĉiu nova kliento (alternative, fadenoj de ekzekuto povas esti kreitaj anstataŭ procezoj).

En la sekvaj sekcioj, ni rigardos ekzemplojn de ripetaj kaj paralelaj interretaj domajnaj serviloj. Ĉi tiuj du serviloj efektivigas simpligitan version de la eĥservo (RFC 862), kiu resendas kopion de iu ajn mesaĝo sendita al ĝi de kliento.

Ripetema UDP-servila eĥo

En ĉi tiu kaj la sekva sekcio ni enkondukos la servilojn por la eĥa servo. Ĝi estas havebla sur la haveno numero 7 kaj funkcias super kaj UDP kaj TCP (ĉi tiu haveno estas rezervita, kaj tial la eĥservilo devas esti funkciigata kun administranto-privilegioj).

La eĥa UDP-servilo senĉese legas datagramojn kaj resendas kopiojn de ili al la sendinto. Ĉar la servilo bezonas nur prilabori unu mesaĝon samtempe, ripeta arkitekturo sufiĉos. La kapdosiero por la serviloj estas montrita en Listo 56.1.

Listo 56.1. Ĉefdosiero por programoj id_echo_sv.c kaj id_echo_cl.c

#include "inet_sockets.h" /* Deklaras la funkciojn de nia ingo */
#include "tlpi_hdr.h"

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

#define BUF_SIZE 500 /* Maksimuma grandeco de datagramoj tio
legeblas de kliento kaj servilo */
___________________________________________________________________________sockets/id_echo.h

Listo 56.2 montras la servilan efektivigon. La sekvaj punktoj estas rimarkindaj:

  • por meti la servilon en demonreĝimon, ni uzas la funkcion becomeDaemon() el sekcio 37.2;

  • por fari la programon pli kompakta, ni uzas la bibliotekon por labori kun Interretaj domajnaj ingoj, evoluigita en sekcio 55.12;

  • se la servilo ne povas resendi respondon al la kliento, ĝi skribas mesaĝon al la protokolo uzante la alvokon syslog().

En reala aplikaĵo, ni verŝajne trudus iun limon al la ofteco de registri mesaĝojn uzante syslog(). Ĉi tio forigus la eblecon de atakanto superfluanta la sisteman protokolon. Krome, ne forgesu, ke ĉiu voko al syslog() estas sufiĉe multekosta, ĉar ĝi uzas fsync() defaŭlte.

Listo 56.2. Itera servilo kiu efektivigas la UDP-eĥservon

_________________________________________________________________sockets/id_echo_sv.c
#inkluzivi
#include "id_echo.h"
#include "become_daemon.h"

int
ĉefa (int argc, char *argv[])
{
int sfd;
grandeco_t numRead;
socklen_t len;
struct sockaddr_storage claddr;
char buf[BUF_SIZE];
char addrStr[IS_ADDR_STR_LEN];

se (iĝu Daemon(0) == -1)
eraroEliri ("fariĝi Daemon");

sfd = inetBind (SERVO, SOCK_DGRAM, NULL);
if (sfd == -1) {
syslog(LOG_ERR, "Ne eblis krei servila ingo (%s)",
strerror(errno));
eliro (ELIR_FAILURE);

/* Ricevu datagramojn kaj resendu kopiojn de ili al la sendintoj */
}
por (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom (sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

se (numRead == -1)
eraroEliro ("rekvde");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= numRead)
syslog(LOG_WARNING, "Eraro dum respondo al %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________sockets/id_echo_sv.c

Por testi la funkciadon de la servilo, ni uzas la programon el Listo 56.3. Ĝi ankaŭ uzas la bibliotekon por labori kun Interretaj domajnaj ingoj, evoluigitaj en sekcio 55.12. Kiel la unua komandlinia argumento, la klientprogramo prenas la nomon de la retnodo sur kiu la servilo situas. La kliento eniras buklon kie ĝi sendas ĉiun el la ceteraj argumentoj al la servilo kiel apartaj datumgramoj, kaj tiam legas kaj presas la datagramojn kiujn ĝi ricevas de la servilo en respondo.

Listo 56.3. Kliento por UDP-eĥservo

#include "id_echo.h"

int
ĉefa (int argc, char *argv[])
{
int sfd, j;
size_t len;
grandeco_t numRead;
char buf[BUF_SIZE];

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

/* Formu la servilan adreson surbaze de la unua komandlinia argumento */
sfd = inetConnect(argv[1], SERVO, SOCK_DGRAM);
se (sfd == -1)
fatal("Ne eblis konekti al servila ingo");

/* Sendu la ceterajn argumentojn al la servilo en la formo de apartaj datagramoj */
por (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (skribi(sfd, argv[j], len) != len)
fatala ("parta/malsukcesa skribo");

numRead = legi (sfd, buf, BUF_SIZE);
se (numRead == -1)
eraroEliro ("legu");
printf("[%ld bajtoj] %.*sn", (longa) numRead, (int) numRead, buf);
}
eliro (ELIR_SUCCESO);
}
_________________________________________________________________sockets/id_echo_cl.c

Malsupre estas ekzemplo de tio, kion ni vidos dum funkciado de la servilo kaj du klientaj okazoj:

$su // Privilegioj estas bezonataj por ligi al rezervita haveno
Pasvorto:
# ./id_echo_sv // Servilo iras en fonan reĝimon
# eliro // Rezignu administrantajn rajtojn
$ ./id_echo_cl localhost saluton mondo // Ĉi tiu kliento sendas du datagramojn
[5 bajtoj] saluton // La kliento montras la respondon ricevitan de la servilo
[5 bajtoj] mondo
$ ./id_echo_cl localhost adiaŭ // Ĉi tiu kliento sendas unu datagramon
[7 bajtoj] adiaŭ

Mi deziras al vi agrablan legadon)

fonto: linux.org.ru