Добры дзень! Прапаную вашай увазе кнігу «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