Книга: UNIX: разработка сетевых приложений

8.15. Эхо-сервер TCP и UDP, использующий функцию select

8.15. Эхо-сервер TCP и UDP, использующий функцию select

Теперь мы объединим наш параллельный эхо-сервер TCP из главы 5 и наш последовательный эхо-сервер UDP из данной главы в один сервер, использующий функцию select для мультиплексирования сокетов TCP и UDP. В листинге 8.14 представлена первая часть этого сервера.

Листинг 8.14. Первая часть эхо-сервера, обрабатывающего сокеты TCP и UDP при помощи функции select

//udpcliserv/udpservselect01.c
 1 #include "unp.h"
 2 int
 3 main(int argc, char **argv)
 4 {
 5  int listenfd, connfd, udpfd, nready, maxfdp1;
 6  char mesg[MAXLINE];
 7  pid_t childpid;
 8  fd_set rset;
 9  ssize_t n;
10  socklen_t len;
11  const int on = 1;
12  struct sockaddr_in cliaddr, servaddr;
13  void sig_chld(int);
14  /* создание прослушиваемого сокета TCP */
15  listenfd = Socket(AF_INET, SOCK_STREAM, 0);
16  bzero(&servaddr, sizeof(servaddr));
17  servaddr.sin_family = AF_INET;
18  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
19  servaddr.sin_port = htons(SERV_PORT);
20  Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
21  Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
22  Listen(listenfd, LISTENQ);
23  /* создание сокета UDP */
24  udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
25  bzero(&servaddr, sizeof(servaddr));
26  servaddr.sin_family = AF_INET;
27  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
28  servaddr.sin_port = htons(SERV_PORT);
29  Bind(udpfd, (SA*)&servaddr, sizeof(servaddr));

Создание прослушиваемого сокета TCP

14-22 Создается прослушиваемый сокет TCP, который связывается с заранее известным портом сервера. Мы устанавливаем параметр сокета SO_REUSEADDR в случае, если на этом порте существуют соединения.

Создание сокета UDP

23-29 Также создается сокет UDP и связывается с тем же портом. Даже если один и тот же порт используется для сокетов TCP и UDP, нет необходимости устанавливать параметр сокета SO_REUSEADDR перед этим вызовом функции bind, поскольку порты TCP не зависят от портов UDP.

В листинге 8.15 показана вторая часть нашего сервера.

Листинг 8.15. Вторая половина эхо-сервера, обрабатывающего TCP и UDP при помощи функции select

udpcliserv/udpservselect01.c
30  Signal(SIGCHLD, sig_chld); /* требуется вызвать waitpid() */
31  FD_ZERO(&rset);
32  maxfdp1 = max(listenfd, udpfd) + 1;
33  for (;;) {
34   FD_SET(listenfd, &rset);
35   FD_SET(udpfd, &rset);
36   if ((nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {
37    if (errno == EINTR)
38     continue; /* назад в for() */
39    else
40     err_sys("select error");
41   }
42   if (FD_ISSET(listenfd, &rset)) {
43    len = sizeof(cliaddr);
44    connfd = Accept(listenfd, (SA*)&cliaddr, &len);
45    if ((childpid = Fork()) == 0) { /* дочерний процесс */
46     Close(listenfd); /* закрывается прослушиваемый сокет */
47     str_echo(connfd); /* обработка запроса */
48     exit(0);
49    }
50    Close(connfd); /* родитель закрывает присоединенный сокет */
51   }
52   if (FD_ISSET(udpfd, &rset)) {
53    len = sizeof(cliaddr);
54    n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA*)&cliaddr, &len);
55    Sendto(udpfd, mesg, n, 0, (SA*)&cliaddr, len);
56   }
57  }
58 }

Установка обработчика сигнала SIGCHLD

30 Для сигнала SIGCHLD устанавливается обработчик, поскольку соединения TCP будут обрабатываться дочерним процессом. Этот обработчик сигнала мы показали в листинге 5.8.

Подготовка к вызову функции select

31-32 Мы инициализируем набор дескрипторов для функции select и вычисляем максимальный из двух дескрипторов, готовности которого будем ожидать.

Вызов функции select

34-41 Мы вызываем функцию select, ожидая только готовности к чтению прослушиваемого сокета TCP или сокета UDP. Поскольку наш обработчик сигнала sig_chld может прервать вызов функции select, обрабатываем ошибку EINTR.

Обработка нового клиентского соединения

42-51 С помощью функции accept мы принимаем новое клиентское соединение, а когда прослушиваемый сокет TCP готов для чтения, с помощью функции fork порождаем дочерний процесс и вызываем нашу функцию str_echo в дочернем процессе. Это та же последовательность действий, которую мы выполняли в главе 5.

Обработка приходящей дейтаграммы

52-57 Если сокет UDP готов для чтения, дейтаграмма пришла. Мы читаем ее с помощью функции recvfrom и отправляем обратно клиенту с помощью функции sendto.

Оглавление книги


Генерация: 2.791. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз