Добавить в цитаты Настройки чтения

Страница 133 из 399

15  ressave = res;

16  do {

17   listenfd =

18    socket(res->ai_family, res->ai_socktype, res->ai_protocol);

19   if (listenfd < 0)

20    continue; /* ошибка, пробуем следующий адрес */

21   Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

22   if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)

23    break; /* успех */

24   Close(listenfd); /* ошибка при вызове функции bind, закрываем

                         сокет и пробуем следующий адрес*/

25  } while ((res = res->ai_next) != NULL);

26  if (res == NULL) /* значение errno устанавливается при последнем

                        вызове функции socket() или bind() */

27   err_sys("tcp_listen error for %s, %s", host, serv);

28  Listen(listenfd, LISTENQ);

29  if (addrlenp)

30   *addrlenp = res->ai_addrlen; /* возвращает размер адреса протокола */

31  freeaddrinfo(ressave);

32  return (listenfd);

33 }

8-15 Мы инициализируем структуру addrinfo с учетом следующих рекомендаций (элементов структуры hints): AI_PASSIVE, поскольку это функция для сервера, AF_UNSPEC для семейства адресов и SOCK_STREAM. Вспомните табл. 11.3: если имя узла не задано (что вполне нормально для сервера, который хочет связать с дескриптором универсальный адрес), то наличие значений AI_PASSIVE и AF_UNSPEC вызовет возвращение двух структур адреса сокета: первой для IPv6 и второй для IPv4 (в предположении, что это узел с двойным стеком).

16-24 Вызываются функции socket и bind. Если любой из вызовов окажется неудачным, мы просто игнорируем данную структуру addrinfo и переходим к следующей. Как было сказано в разделе 7.5, для сервера TCP мы всегда устанавливаем параметр сокета SO_REUSEADDR.

25-26 Если все вызовы функций socket и bind окажутся неудачными, мы сообщаем об ошибке и завершаем выполнение. Как и в случае с нашей функцией tcp_co

27 Сокет превращается в прослушиваемый сокет с помощью функции listen.

28-31 Если аргумент addrlenp является непустым указателем, мы возвращаем размер адресов протокола через этот указатель. Это позволяет вызывающему процессу выделять память для структуры адреса сокета, чтобы получить адрес протокола клиента из функции accept (см. также упражнение 11.7).

Пример: сервер времени и даты

В листинге 11.7 показан наш сервер времени и даты из листинга 4.2, переписанный с использованием функции tcp_listen.

Листинг 11.7. Сервер времени и даты, переписанный с использованием функции tcp_listen

//names/daytimetcpsrv1.c

 1 #include "unp.h"

 2 #include <time.h>

 3 int

 4 main(int argc, char **argv)

 5 {

 6  int listenfd, co

 7  socklen_t addrlen, len;

 8  char = buff[MAXLINE];

 9  time_t ticks;

10  struct sockaddr_storage cliaddr;

11  if (argc != 2)

12   err_quit("usage: daytimetcpsrv1 <service or port#>");

13  listenfd = Tcp_listen(NULL, argv[1], &addrlen);

14  for (;;) {

15   len = sizeof(cliaddr);

16   co

17   printf("co

18   ticks = time(NULL);

19   snprintf(buff, sizeof(buff), "%.24srn", ctime(&ticks));

20   Write(co

21   Close(co

22  }

23 }

11-12 Нам нужно использовать аргумент командной строки, чтобы задать либо имя службы, либо номер порта. Это упрощает проверку нашего сервера, поскольку связывание с портом 13 для сервера времени и даты требует прав привилегированного пользователя.

13 Функция tcp_listen создает прослушиваемый сокет. В качестве третьего аргумента мы передаем нулевой указатель, потому что нам безразличен размер структуры адреса, используемого данным семейством: мы будем работать со структурой sockaddr_storage.