Βιβλίο "Linux API. Περιεκτικός οδηγός"


Βιβλίο "Linux API. Περιεκτικός οδηγός"

Καλό απόγευμα Σας παρουσιάζω το βιβλίο «Linux API. Ένας περιεκτικός οδηγός» (μετάφραση του βιβλίου Η διεπαφή προγραμματισμού Linux). Μπορείτε να το παραγγείλετε στον ιστότοπο του εκδότη και εάν εφαρμόσετε τον κωδικό προσφοράς LinuxAPI , θα λάβετε έκπτωση 30%.

Απόσπασμα από το βιβλίο για αναφορά:

Υποδοχές: Αρχιτεκτονική διακομιστή

Σε αυτό το κεφάλαιο, θα συζητήσουμε τα βασικά του σχεδιασμού επαναληπτικών και παράλληλων διακομιστών και επίσης θα δούμε έναν ειδικό δαίμονα που ονομάζεται inetd, ο οποίος διευκολύνει τη δημιουργία εφαρμογών διακομιστή Διαδικτύου.

Επαναληπτικοί και παράλληλοι διακομιστές

Υπάρχουν δύο κοινές αρχιτεκτονικές διακομιστή δικτύου που βασίζονται σε πρίζες:

  • επαναληπτικό: ο διακομιστής εξυπηρετεί τους πελάτες έναν κάθε φορά, πρώτα επεξεργάζεται ένα αίτημα (ή πολλά αιτήματα) από έναν πελάτη και μετά προχωρά στον επόμενο.

  • παράλληλη: ο διακομιστής έχει σχεδιαστεί για να εξυπηρετεί πολλούς πελάτες ταυτόχρονα.

Ένα παράδειγμα επαναληπτικού διακομιστή που βασίζεται σε ουρές FIFO παρουσιάστηκε ήδη στην Ενότητα 44.8.

Οι επαναληπτικοί διακομιστές είναι συνήθως κατάλληλοι μόνο σε περιπτώσεις όπου τα αιτήματα πελατών μπορούν να υποβληθούν σε επεξεργασία αρκετά γρήγορα, καθώς κάθε πελάτης αναγκάζεται να περιμένει έως ότου εξυπηρετηθούν άλλοι πελάτες που βρίσκονται μπροστά του. Μια κοινή περίπτωση χρήσης αυτής της προσέγγισης είναι η ανταλλαγή μεμονωμένων αιτημάτων και απαντήσεων μεταξύ πελάτη και διακομιστή.

Οι παράλληλοι διακομιστές είναι κατάλληλοι σε περιπτώσεις όπου κάθε αίτηση χρειάζεται σημαντικό χρόνο για την επεξεργασία ή όπου ο πελάτης και ο διακομιστής συμμετέχουν σε ανταλλαγές μεγάλων μηνυμάτων. Σε αυτό το κεφάλαιο, θα εστιάσουμε κυρίως στον παραδοσιακό (και απλούστερο) τρόπο σχεδίασης παράλληλων διακομιστών, που είναι η δημιουργία μιας ξεχωριστής θυγατρικής διαδικασίας για κάθε νέο πελάτη. Αυτή η διαδικασία εκτελεί όλη την εργασία για την εξυπηρέτηση του πελάτη και στη συνέχεια τελειώνει. Επειδή κάθε μία από αυτές τις διαδικασίες λειτουργεί ανεξάρτητα, είναι δυνατή η ταυτόχρονη εξυπηρέτηση πολλών πελατών. Το κύριο καθήκον της διαδικασίας του κύριου διακομιστή (γονέας) είναι να δημιουργήσει ένα ξεχωριστό παιδί για κάθε νέο πελάτη (εναλλακτικά, μπορούν να δημιουργηθούν νήματα εκτέλεσης αντί για διεργασίες).

Στις επόμενες ενότητες, θα εξετάσουμε παραδείγματα επαναληπτικών και παράλληλων διακομιστών υποδοχών τομέα Διαδικτύου. Αυτοί οι δύο διακομιστές εφαρμόζουν μια απλοποιημένη έκδοση της υπηρεσίας echo (RFC 862), η οποία επιστρέφει ένα αντίγραφο οποιουδήποτε μηνύματος που της αποστέλλεται από έναν πελάτη.

Επαναληπτική ηχώ διακομιστή UDP

Σε αυτήν και στην επόμενη ενότητα θα παρουσιάσουμε τους διακομιστές για την υπηρεσία echo. Είναι διαθέσιμο στη θύρα 7 και λειτουργεί τόσο σε UDP όσο και σε TCP (αυτή η θύρα είναι δεσμευμένη και επομένως ο διακομιστής echo πρέπει να εκτελείται με δικαιώματα διαχειριστή).

Ο διακομιστής echo UDP διαβάζει συνεχώς datagrams και επιστρέφει αντίγραφά τους στον αποστολέα. Δεδομένου ότι ο διακομιστής χρειάζεται να επεξεργάζεται μόνο ένα μήνυμα κάθε φορά, αρκεί μια επαναληπτική αρχιτεκτονική. Το αρχείο κεφαλίδας για τους διακομιστές εμφανίζεται στη Λίστα 56.1.

Λίστα 56.1. Αρχείο κεφαλίδας για τα προγράμματα id_echo_sv.c και id_echo_cl.c

#include "inet_sockets.h" /* Δηλώνει τις λειτουργίες της πρίζας μας */
#include "tlpi_hdr.h"

#define SERVICE "echo" /* Όνομα υπηρεσίας UDP */

#define BUF_SIZE 500 /* Μέγιστο μέγεθος datagrams που
μπορεί να διαβαστεί από πελάτη και διακομιστή */
____________________________________________________________________ πρίζες/id_echo.χ

Η λίστα 56.2 δείχνει την υλοποίηση του διακομιστή. Αξίζει να σημειωθούν τα ακόλουθα σημεία:

  • Για να θέσουμε τον διακομιστή σε λειτουργία δαίμονα, χρησιμοποιούμε τη συνάρτηση γίνειDaemon() από την ενότητα 37.2.

  • Για να κάνουμε το πρόγραμμα πιο συμπαγές, χρησιμοποιούμε τη βιβλιοθήκη για εργασία με υποδοχές τομέα Διαδικτύου, που αναπτύχθηκε στην ενότητα 55.12.

  • Εάν ο διακομιστής δεν μπορεί να επιστρέψει μια απάντηση στον πελάτη, γράφει ένα μήνυμα στο αρχείο καταγραφής χρησιμοποιώντας την κλήση syslog().

Σε μια πραγματική εφαρμογή, πιθανότατα θα επιβάλλαμε κάποιο όριο στη συχνότητα καταγραφής μηνυμάτων χρησιμοποιώντας το syslog(). Αυτό θα εξαλείφει την πιθανότητα υπερχείλισης ενός εισβολέα από το αρχείο καταγραφής συστήματος. Επιπλέον, μην ξεχνάτε ότι κάθε κλήση στο syslog() είναι αρκετά ακριβή, αφού χρησιμοποιεί την fsync() από προεπιλογή.

Λίστα 56.2. Διακομιστής επανάληψης που υλοποιεί την υπηρεσία UDP echo

_________________________________________________________________ υποδοχές/id_echo_sv.c
#περιλαμβάνω
#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 adrStr[IS_ADDR_STR_LEN];

αν (γίνετε Daemon(0) == -1)
errExit("becomeDaemon");

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
αν (sfd == -1) {
syslog(LOG_ERR, "Δεν ήταν δυνατή η δημιουργία υποδοχής διακομιστή (%s)",
strerror(errno));
έξοδος (EXIT_FAILURE);

/* Λάβετε datagrams και επιστρέψτε αντίγραφά τους στους αποστολείς */
}
Για (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

εάν (numRead == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= numRead)
syslog(LOG_WARNING, "Σφάλμα ηχώ απόκρισης στο %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
adrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________ υποδοχές/id_echo_sv.c

Για να ελέγξουμε τη λειτουργία του διακομιστή, χρησιμοποιούμε το πρόγραμμα από τη Λίστα 56.3. Χρησιμοποιεί επίσης τη βιβλιοθήκη για εργασία με υποδοχές τομέα Διαδικτύου, που αναπτύχθηκε στην ενότητα 55.12. Ως πρώτο όρισμα γραμμής εντολών, το πρόγραμμα-πελάτη παίρνει το όνομα του κόμβου δικτύου στον οποίο βρίσκεται ο διακομιστής. Ο πελάτης εισάγει έναν βρόχο όπου στέλνει καθένα από τα υπόλοιπα ορίσματα στον διακομιστή ως ξεχωριστά γραφήματα δεδομένων και, στη συνέχεια, διαβάζει και εκτυπώνει τα γραφήματα δεδομένων που λαμβάνει από τον διακομιστή ως απόκριση.

Λίστα 56.3. Πελάτης για υπηρεσία UDP echo

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

/* Σχηματίστε τη διεύθυνση διακομιστή με βάση το όρισμα της πρώτης γραμμής εντολών */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
αν (sfd == -1)
fatal("Δεν ήταν δυνατή η σύνδεση στην υποδοχή διακομιστή");

/* Στείλτε τα υπόλοιπα ορίσματα στον διακομιστή με τη μορφή ξεχωριστών γραμμάτων δεδομένων */
για (j = 2; j < argc; j++) {
len = strlen(argv[j]);
αν (γράψτε(sfd, argv[j], len) != len)
fatal("μερική/αποτυχημένη εγγραφή");

numRead = ανάγνωση (sfd, buf, BUF_SIZE);
εάν (numRead == -1)
errExit("read");
printf("[%ld bytes] %.*sn", (long) numRead, (int) numRead, buf);
}
έξοδος(EXIT_SUCCESS);
}
_________________________________________________________________ υποδοχές/id_echo_cl.c

Παρακάτω είναι ένα παράδειγμα του τι θα δούμε κατά την εκτέλεση του διακομιστή και δύο παρουσίες πελάτη:

$su // Απαιτούνται δικαιώματα για τη σύνδεση σε μια δεσμευμένη θύρα
Σύνθημα:
# ./id_echo_sv // Ο διακομιστής μεταβαίνει σε λειτουργία παρασκηνίου
# έξοδος // Παραίτηση από δικαιώματα διαχειριστή
$ ./id_echo_cl localhost hello world // Αυτός ο πελάτης στέλνει δύο datagrams
[5 bytes] hello // Ο πελάτης εμφανίζει την απάντηση που έλαβε από τον διακομιστή
[5 bytes] κόσμο
$ ./id_echo_cl localhost αντίο // Αυτός ο πελάτης στέλνει ένα datagram
[7 bytes] αντίο

Σας εύχομαι μια ευχάριστη ανάγνωση)

Πηγή: linux.org.ru