Книга „Linux API. Изчерпателно ръководство"


Книга „Linux API. Изчерпателно ръководство"

Добър ден Представям на вашето внимание книгата „Linux API. Изчерпателно ръководство“ (превод на книгата Интерфейсът за програмиране на Linux). Може да се поръча на уебсайта на издателя и ако приложите промоционалния код LinuxAPI , ще получите 30% отстъпка.

Откъс от книгата за справка:

Сокети: сървърна архитектура

В тази глава ще обсъдим основите на проектирането на итеративни и паралелни сървъри, а също така ще разгледаме специален демон, наречен inetd, който улеснява създаването на приложения за интернет сървъри.

Итеративни и паралелни сървъри

Има две общи архитектури на мрежови сървъри, базирани на сокет:

  • итеративен: сървърът обслужва клиенти един по един, като първо обработва заявка (или няколко заявки) от един клиент и след това преминава към следващия;

  • паралелен: сървърът е проектиран да обслужва множество клиенти едновременно.

Пример за итеративен сървър, базиран на FIFO опашки, вече беше представен в раздел 44.8.

Итеративните сървъри обикновено са подходящи само в ситуации, в които клиентските заявки могат да бъдат обработени доста бързо, тъй като всеки клиент е принуден да чака, докато всички други клиенти пред него бъдат обслужени. Често срещан случай на използване на този подход е обменът на единични заявки и отговори между клиент и сървър.

Паралелните сървъри са подходящи в случаите, когато обработката на всяка заявка отнема значително време или когато клиентът и сървърът обменят дълги съобщения. В тази глава ще се съсредоточим главно върху традиционния (и най-прост) начин за проектиране на паралелни сървъри, който е да създадем отделен дъщерен процес за всеки нов клиент. Този процес изпълнява цялата работа за обслужване на клиента и след това приключва. Тъй като всеки от тези процеси работи независимо, множество клиенти могат да бъдат обслужвани едновременно. Основната задача на основния сървърен процес (родител) е да създаде отделен дъщерен процес за всеки нов клиент (алтернативно могат да се създават нишки за изпълнение вместо процеси).

В следващите раздели ще разгледаме примери за итеративни и паралелни сървъри за сокет на интернет домейн. Тези два сървъра прилагат опростена версия на услугата ехо (RFC 862), която връща копие на всяко съобщение, изпратено до нея от клиент.

Итеративно UDP сървърно ехо

В този и следващия раздел ще представим сървърите за услугата ехо. Той е достъпен на порт номер 7 и работи както през UDP, така и през TCP (този порт е запазен и следователно ехо сървърът трябва да се изпълнява с администраторски права).

Ехо UDP сървърът непрекъснато чете дейтаграми и връща копия от тях на подателя. Тъй като сървърът трябва да обработва само едно съобщение в даден момент, итеративната архитектура ще бъде достатъчна. Заглавният файл за сървърите е показан в листинг 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 ехо

_________________________________________________________________sockets/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 addrStr[IS_ADDR_STR_LEN];

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

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
if (sfd == -1) {
syslog(LOG_ERR, "Не може да се създаде сървърен сокет (%s)",
strerror(errno));
изход (EXIT_FAILURE);

/* Получаване на дейтаграми и връщане на копия от тях на подателите */
}
за (;;) {
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,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________sockets/id_echo_sv.c

За да тестваме работата на сървъра, използваме програмата от листинг 56.3. Той също така използва библиотеката за работа с интернет домейни сокети, разработена в раздел 55.12. Като първи аргумент на командния ред клиентската програма приема името на мрежовия възел, на който се намира сървърът. Клиентът влиза в цикъл, в който изпраща всеки от останалите аргументи към сървъра като отделни дейтаграми и след това чете и отпечатва дейтаграмите, които получава от сървъра в отговор.

Списък 56.3. Клиент за UDP ехо услуга

#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 хост съобщение…n", argv[0]);

/* Формиране на адреса на сървъра въз основа на първия аргумент на командния ред */
sfd = inetConnect(argv[1], SERVICE, SOCK_DGRAM);
ако (sfd == -1)
фатален ("Не може да се свърже със сървърния сокет");

/* Изпращане на останалите аргументи към сървъра под формата на отделни дейтаграми */
за (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (write(sfd, argv[j], len) != len)
фатален ("частично/неуспешно записване");

numRead = четене (sfd, buf, BUF_SIZE);
ако (numRead == -1)
errExit("четене");
printf("[%ld байта] %.*sn", (дълъг) numRead, (int) numRead, buf);
}
изход (EXIT_SUCCESS);
}
_________________________________________________________________sockets/id_echo_cl.c

По-долу е даден пример за това, което ще видим, когато стартираме сървъра и два клиентски екземпляра:

$su // Необходими са привилегии за свързване към запазен порт
парола:
# ./id_echo_sv // Сървърът преминава във фонов режим
# изход // Откажете се от администраторските права
$ ./id_echo_cl localhost hello world // Този клиент изпраща две дейтаграми
[5 байта] здравей // Клиентът показва отговора, получен от сървъра
[5 байта] свят
$ ./id_echo_cl localhost довиждане // Този клиент изпраща една дейтаграма
[7 байта] довиждане

Желая ви приятно четене)

Източник: linux.org.ru