Кніга Linux API. Вычарпальнае кіраўніцтва»


Кніга Linux API. Вычарпальнае кіраўніцтва»

Добры дзень! Прапаную вашай увазе кнігу «Linux API. Вычарпальнае кіраўніцтва»(пераклад кнігі The Linux Programming Interface). Яе можна замовіць на сайце выдавецтва, і калі прымяніць промакод LinuxAPI , то атрымаеце скідку 30%.

Урывак з кнігі для азнаямлення:

Сокеты: архітэктура сервера

У гэтым раздзеле мы абмяркуем асновы праектавання ітэрацыйных і раўналежных сервераў, а таксама разгледзім адмысловы дэман inetd, які палягчае стварэнне серверных інтэрнэт-прыкладанняў.

Ітэрацыйныя і паралельныя серверы

Існуюць дзве распаўсюджаныя архітэктуры сеткавых сервераў на аснове сокетаў:

  • ітэрацыйная: сервер абслугоўвае кліентаў па адным, спачатку апрацоўваючы запыт (ці некалькі запытаў) аднаго кліента і затым пераходзячы да наступнага;

  • паралельная: сервер спраектаваны для абслугоўвання некалькіх кліентаў адначасова.

У раздзеле 44.8 ужо быў прадстаўлены прыклад ітэрацыйнага сервера на аснове чэргаў FIFO.

Ітэрацыйныя серверы звычайна падыходзяць толькі ў сітуацыях, калі кліенцкія запыты можна апрацаваць досыць хутка, бо кожны кліент змушаны чакаць, пакуль не абслужаць любых іншых кліентаў, змешчаных перад ім. Звычайным сцэнарам выкарыстання гэтага падыходу з'яўляецца абмен адзінкавымі запытамі і адказамі паміж кліентам і сэрверам.

Раўналежныя серверы падыходзяць у выпадках, калі на апрацоўку кожнага запыту сыходзіць значная колькасць часу або кліент і сервер выконваюць працяглы абмен паведамленнямі. У дадзеным раздзеле мы ў асноўным засяродзімся на традыцыйным (і найболей простым) спосабе праектавання раўналежных сервераў, які складаецца ў стварэнні асобнага даччынага працэсу для кожнага новага кліента. Такі працэс выконвае ўсю працу па абслугоўванні кліента, пасля чаго завяршаецца. Паколькі кожны з гэтых працэсаў функцыянуе незалежна, можна абслугоўваць некалькі кліентаў адначасова. Асноўная задача галоўнага сервернага працэсу (аднаго з бацькоў) заключаецца ў стварэнні асобнага нашчадка для кожнага новага кліента (як варыянт, замест працэсаў можна ствараць патокі выканання).

У наступных раздзелах мы разгледзім прыклады ітэрацыйнага і паралельнага сервераў на аснове сокетаў інтэрнэт-дамена. Гэтыя два сервера рэалізуюць спрошчаны варыянт службы echo (RFC 862), якая вяртае копію любога паведамлення, дасланага ёй кліентам.

Ітэрацыйны UDP-сервер echo

У гэтай і наступнай частцы мы прадставім серверы для службы echo. Яна даступная на порце з нумарам 7 і працуе як па UDP, так і па TCP (дадзены порт зарэзерваваны, у сувязі з чым сервер echo неабходна запускаць з прывілеямі адміністратара).

UDP-сервер echo стала счытвае датаграмы і вяртае адпраўніку іх копіі. Паколькі серверу трэба апрацоўваць толькі адно паведамленне за раз, тут будзе дастаткова ітэрацыйнай архітэктуры. Загалоўкавыя файл для сервераў паказаны ў лістынгу 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 /* Максімальны памер датаграм, якія
могуць быць прачытаныя кліентам і серверам */
____________________________________________________________________sockets/id_echo.h

У лістынгу 56.2 прадстаўлена рэалізацыя сервера. Варта адзначыць наступныя моманты:

  • для пераводу сервера ў рэжым дэмана мы задзейнічаем функцыю becomeDaemon() з раздзела 37.2;

  • каб зрабіць праграму больш кампактнай, мы выкарыстоўваем бібліятэку для працы з сокетамі інтэрнэт-дамена, распрацаваную ў раздзеле 55.12;

  • калі сервер не можа вярнуць адказ кліенту, тое запісвае паведамленне ў часопіс, ужываючы выклік syslog().

У рэальным дадатку мы б, хутчэй за ўсё, увялі вызначанае абмежаванне на частату запісу паведамленняў з дапамогай syslog(). Гэта выключыла б магчымасць перапаўнення сістэмнага часопіса зламыснікам. Да таго ж не варта забываць, што кожны выклік syslog() даволі затратны, бо па змаўчанні выкарыстоўвае fsync().

Лістынг 56.2. Ітэрацыйны сервер, які рэалізуе UDP-службу echo

_________________________________________________________________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, "Не можа стварыць сэрвэт socket (%s)",
strerror(errno));
exit(EXIT_FAILURE);

/* Атрымліваем датаграмы і вяртаем адпраўшчыкам іх копіі */
}
for (;;) {
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, "Error echoing response to %s (%s)",
inetAddressStr((struct sockaddr *) &claddr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________sockets/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("host host msg…n", argv[0]);

/* Фарміруем адрас сервера на аснове першага аргументу каманднага радка */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
if (sfd == -1)
fatal("Could no connect to server socket");

/* Пасылаем серверу астатнія аргументы ў выглядзе асобных датаграм */
for (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (write(sfd, argv[j], len) != len)
fatal("partial/failed write");

numRead = read(sfd, buf, BUF_SIZE);
if (numRead == -1)
errExit("read");
printf("[%ld bytes] %.*sn", (long) numRead, (int) numRead, buf);
}
exit(EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

Ніжэй паказаны прыклад таго, што мы ўбачым пры запуску сервера і двух асобнікаў кліента:

$ su // Для прывязкі да зарэзерваванага порта патрэбныя прывілеі
пароль:
# ./id_echo_sv // Сервер пераходзіць у фонавы рэжым
# exit // Адмаўляемся ад правоў адміністратара
$ ./id_echo_cl localhost hello world // Гэты кліент адпраўляе дзве датаграмы
[5 bytes] hello // Кліент выводзіць адказ, атрыманы ад сервера
[5 bytes] world
$ ./id_echo_cl localhost goodbye // Гэты кліент шле адну датаграму
[7 bytes] goodbye

Жадаю прыемнага чытання)

Крыніца: linux.org.ru