Linux API kirja. Kattava opas »


Linux API kirja. Kattava opas »

Hyvää iltapäivää Ohjaan huomionne kirjan "Linux API. Kattava opas "(kirjan käännös Linuxin ohjelmointiliittymä). Sen voi tilata julkaisijan verkkosivustolta ja jos käytät tarjouskoodia LinuxAPI saat 30% alennuksen.

Ote kirjasta arvioitavaksi:

Sockets: palvelinarkkitehtuuri

Tässä luvussa käsittelemme iteratiivisten ja rinnakkaisten palvelimien suunnittelun perusteita sekä erityistä inetd-demonia, joka helpottaa palvelinpuolen Internet-sovellusten luomista.

Iterointi ja rinnakkaispalvelimet

On olemassa kaksi yleistä socket-pohjaista verkkopalvelinarkkitehtuuria:

  • iteratiivinen: palvelin palvelee asiakkaita yksi kerrallaan käsittelemällä ensin pyynnön (tai useita pyyntöjä) yhdeltä asiakkaalta ja siirtymällä sitten seuraavaan;

  • rinnakkain: palvelin on suunniteltu palvelemaan useita asiakkaita samanaikaisesti.

Kohdassa 44.8 on jo annettu esimerkki FIFO-jonoihin perustuvasta iteraatiopalvelimesta.

Iteraatiopalvelimet sopivat yleensä vain tilanteisiin, joissa asiakaspyynnöt voidaan käsitellä melko nopeasti, koska jokaisen asiakkaan on odotettava, kunnes muut sen edessä olevat asiakkaat on palvellut. Tämän lähestymistavan yleinen käyttötapa on yksittäisten pyyntöjen ja vastausten vaihtaminen asiakkaan ja palvelimen välillä.

Rinnakkaispalvelimet sopivat tilanteisiin, joissa jokaisen pyynnön käsittely vie huomattavan paljon aikaa tai asiakkaan ja palvelimen välillä on pitkä viestien vaihto. Tässä luvussa keskitymme pääasiassa perinteiseen (ja helpoimpaan) tapaan suunnitella rinnakkaisia ​​palvelimia, eli luoda jokaiselle uudelle asiakkaalle erillinen aliprosessi. Tällainen prosessi tekee kaiken asiakkaan palvelemisen, minkä jälkeen se päättyy. Koska jokainen näistä prosesseista toimii itsenäisesti, on mahdollista palvella useita asiakkaita samanaikaisesti. Pääpalvelinprosessin (emo) päätehtävä on luoda erillinen lapsi jokaiselle uudelle asiakkaalle (vaihtoehtoisesti voit luoda prosessien sijaan suoritussäikeitä).

Seuraavissa osioissa tarkastellaan esimerkkejä iteratiivisista ja rinnakkaisista palvelimista, jotka perustuvat Internet-verkkoalueen pistokkeisiin. Nämä kaksi palvelinta toteuttavat kaikupalvelun yksinkertaistetun version (RFC 862), joka palauttaa kopion kaikista asiakkaan sille lähettämistä viesteistä.

echo iteration udp-palvelin

Tässä ja seuraavassa osiossa esittelemme kaikupalvelun palvelimia. Se on saatavilla portissa 7 ja toimii sekä UDP:n että TCP:n kautta (tämä portti on varattu, ja siksi kaikupalvelinta on käytettävä järjestelmänvalvojan oikeuksin).

Echo UDP -palvelin lukee jatkuvasti datagrammeja ja palauttaa niistä kopion lähettäjälle. Koska palvelimen tarvitsee käsitellä vain yksi viesti kerrallaan, iteratiivinen arkkitehtuuri riittää tässä. Palvelimien otsikkotiedosto näkyy listassa 56.1-XNUMX.

Listaus 56.1. Otsikkotiedosto ohjelmille id_echo_sv.c ja id_echo_cl.c

#include "inet_sockets.h" /* Ilmoittaa socket-funktiomme */
#include "tlpi_hdr.h"

#define SERVICE "echo" /* UDP-palvelun nimi */

#define BUF_SIZE 500 /* Datagrammien enimmäiskoko
asiakas ja palvelin voivat lukea */
______________________________________________________________________________ pistorasiat/id_echo.h

Lista 56.2-XNUMX näyttää palvelimen toteutuksen. Kannattaa huomioida seuraavat seikat:

  • asettaaksemme palvelimen daemon-tilaan, käytämme kohdasta 37.2 olevaa couldDaemon()-funktiota;

  • ohjelman tiivistämiseksi käytämme osiossa 55.12 kehitettyä Internet domain socket -kirjastoa;

  • jos palvelin ei voi palauttaa vastausta asiakkaalle, se kirjoittaa viestin lokiin syslog()-kutsulla.

Todellisessa sovelluksessa asettaisimme todennäköisimmin tietyn rajan viestien kirjaamistiheydelle syslog(:lla). Tämä eliminoisi mahdollisuuden, että hyökkääjä vuotaa järjestelmälokin yli. Muista myös, että jokainen syslog()-kutsu on melko kallis, koska se käyttää oletuksena fsync()-funktiota.

Listaus 56.2. Iterointipalvelin, joka toteuttaa echo UDP -palvelun

____________________________________________________________________________sockets/id_echo_sv.c
#sisältää
#include "id_echo.h"
#include "become_daemon.h"

int
main(int argc, char *argv[])
{
int sfd;
ssize_t numRead;
socklen_tlen;
struct sockaddr_storage claddr;
charbuf[BUF_KOKO];
char addrStr[IS_ADDR_STR_LEN];

if (becomeDaemon(0) == -1)
errExit("becomeDaemon");

sfd = inetBind(PALVELU, SOCK_DGRAM, NULL);
if (sfd == -1) {
syslog(LOG_ERR, "Ei voitu luoda palvelinliitäntää (%s)",
strerror(errno));
exit(EXIT_FAILURE);

/* Vastaanota datagrammit ja palauta kopiot lähettäjille */
}
varten(;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

jos (lukumäärä == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!=numRead)
syslog(LOG_WARNING, "Virhe toistettaessa vastausta kohteeseen %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
____________________________________________________________________________sockets/id_echo_sv.c

Käytämme listassa 56.3 olevaa ohjelmaa palvelimen testaamiseen. Se käyttää myös osiossa 55.12 kehitettyä Internet-toimialueen socket-kirjastoa. Asiakasohjelma ottaa komentorivin ensimmäisenä argumenttina sen verkkopalvelimen nimen, jossa palvelin sijaitsee. Asiakas siirtyy silmukkaan, jossa se lähettää jokaisen jäljellä olevan argumentin palvelimelle erillisinä datagrammeina ja sitten lukee ja tulostaa palvelimelta vastauksena saadut datagrammit.

Listaus 56.3. Asiakas echo UDP -palvelulle

#include "id_echo.h"

int
main(int argc, char *argv[])
{
int sfd,j;
koko_tlen;
ssize_t numRead;
charbuf[BUF_KOKO];

if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s isäntäviesti…n", argv[0]);

/* Muodosta palvelimen osoite ensimmäisen komentorivin argumentin perusteella */
sfd = inetConnect(argv[1], PALVELU, SOCK_DGRAM);
jos (sfd == -1)
fatal("Ei voitu muodostaa yhteyttä palvelinpistorasiaan");

/* Lähetä loput argumentit palvelimelle erillisinä datagrammeina */
for (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (kirjoita(sfd, argv[j], len) != len)
fatal("osittainen/epäonnistunut kirjoitus");

numRead = read(sfd, buf, BUF_KOKO);
jos (lukumäärä == -1)
errExit("lue");
printf("[%ld tavua] %.*sn", (pitkä) numRead, (int) numRead, buf);
}
exit(EXIT_SUCCESS);
}
_______________________________________________________________________sockets/id_echo_cl.c

Seuraavassa on esimerkki siitä, mitä näemme, kun käynnistämme palvelimen ja kaksi asiakasesiintymää:

$ su // Vaatii oikeudet sitoutuaksesi varattuun porttiin
Salasana:
# ./id_echo_sv // Palvelin menee taustalle
# exit // Luovuta järjestelmänvalvojan oikeudet
$ ./id_echo_cl localhost hello world // Tämä asiakas lähettää kaksi datagrammia
[5 tavua] hello // Asiakas tulostaa palvelimelta saamansa vastauksen
[5 tavua] maailma
$ ./id_echo_cl localhost goodbye // Tämä asiakas lähettää yhden datagrammin
[7 tavua] näkemiin

Toivotan sinulle mukavia lukuhetkiä)

Lähde: linux.org.ru