Вход/Регистрация
UNIX: разработка сетевых приложений
вернуться

Стивенс Уильям Ричард

Шрифт:

5 {

6 int listenfd, connfd;

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 connfd = Accept(listenfd, (SA*)&cliaddr, &len);

17 printf("connection from %s\n", Sock_ntop((SA*)&cliaddr, len));

18 ticks = time(NULL);

19 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));

20 Write(connfd, buff, strlen(buff));

21 Close(connfd);

22 }

23 }

Ввод имени службы или номера порта в качестве аргумента командной строки

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

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

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

Цикл сервера

14-22
Функция
accept
ждет соединения с клиентом. Мы выводим адрес клиента, вызывая функцию
sock_ntop
. В случае IPv4 или IPv6 эта функция выводит IP-адрес и номер порта. Мы могли бы использовать функцию
getnameinfo
(описанную в разделе 11.17), чтобы попытаться получить имя узла клиента, но это подразумевает запрос PTR в DNS, что может занять некоторое время, особенно если запрос PTR окажется неудачным. В разделе 14.8 [112] упоминается, что на занятом веб-сервере почти у 25% всех клиентов, соединяющихся с этим сервером, в DNS нет записей типа PTR. Поскольку мы не хотим, чтобы наш сервер (особенно последовательный сервер) в течение нескольких секунд ждал запрос PTR, мы просто выводим IP-адрес и порт.

Пример: сервер времени и даты с указанием протокола

В листинге 11.7 есть небольшая проблема: первый аргумент функции

tcp_listen
— пустой указатель, объединенный с семейством адресов
AF_UNSPEC
, который задает функция
tcp_listen
, — может заставить функцию
getaddrinfo
возвратить структуру адреса сокета с семейством адресов, отличным от желаемого. Например, первой на узле с двойным стеком будет возвращена структура адреса сокета для IPv6 (см. табл. 11.3), но, возможно, нам требуется, чтобы наш сервер обрабатывал только IPv4.

У клиентов такой проблемы нет, поскольку клиент должен всегда задавать либо IP-адрес, либо имя узла. Клиентские приложения обычно позволяют пользователю вводить этот параметр как аргумент командной строки. Это дает нам возможность задавать имя узла, связанное с определенным типом IP-адреса (вспомните наши имена узлов -4 и -6 в разделе 11.2), или же задавать либо строку в точечно-десятичной записи (для IPv4), либо шестнадцатеричную строку (для IPv6).

И для серверов существует простая методика, позволяющая нам указать, какой именно протокол следует использовать — IPv4 или IPv6. Для этого нужно позволить пользователю ввести либо IP-адрес, либо имя узла в качестве аргумента командной строки и передать его функции

getaddrinfo
. В случае IP-адреса строка точечно-десятичной записи IPv4 отличается от шестнадцатеричной строки IPv6. Следующие вызовы функции
inet_pton
оказываются либо успешными либо нет, как это показано в данном случае:

inet_pton(AF_INET, "0.0.0.0", &foo); /* успешно */

inet_pton(AF_INET, "0::0", &foo); /* неудачно*/

inet_pton(AF_INET6, "0.0.0.0", &foo); /* неудачно */

inet_pton(AF_INET6, "0::0", &foo); /* успешно */

Следовательно, если мы изменим наши серверы таким образом, чтобы они получали дополнительный аргумент, то при вводе

% server

по умолчанию мы получим IPv6 на узле с двойным стеком, но при вводе

% server 0.0.0.0

явно задается IPv4, а при вводе

% server 0::0

явно задается IPv6.

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

Листинг 11.8. Не зависящий от протокола сервер времени и даты, использующий функцию tcp_listen

names/daytimetcpsrv2.c

1 #include "unp.h"

  • Читать дальше
  • 1
  • ...
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: