continue; /* В случае ошибки пробуем следующий адрес */
sizeof(optval)) == -1)
errExit("setsockopt");
break; /* Успех */
/* Вызов bind() завершился неудачно; закрываем этот сокет и пробуем следующий адрес */
close(lfd);
}
if (rp == NULL)
fatal("Could not bind socket to any address");
errExit("listen");
freeaddrinfo(result);
/* Принимаем соединение со стороны клиента и получаем его адрес */
addrlen = sizeof(struct sockaddr_storage);
if (cfd == -1) {
errMsg("accept");
continue;
}
host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
snprintf(addrStr, ADDRSTRLEN, "(%s, %s)", host, service);
else
snprintf(addrStr, ADDRSTRLEN, "(?UNKNOWN?)");
printf("Connection from %s\n", addrStr);
/* Считываем запрос клиента, возвращаем в ответ элемент последовательности */
close(cfd);
continue; /* Ошибка при чтении; пропускаем запрос */
}
if (reqLen <= 0) { /* Отслеживаем клиентов, которые ведут себя некорректно */
close(cfd);
continue; /* Некорректный запрос; пропускаем его */
}
if (write(cfd, seqNumStr, strlen(seqNumStr))!= strlen(seqNumStr))
fprintf(stderr, "Error on write");
if (close(cfd) == -1) /* Закрываем соединение */
errMsg("close");
}
}
sockets/is_seqnum_sv.c
Клиентская программа, показанная в листинге 55.7, принимает два аргумента. Первый, обозначающий имя узла, на котором работает сервер, является обязательным. Второй аргумент, определяющий длину последовательности, запрашиваемой клиентом, можно пропустить. По умолчанию длина равна 1. Клиент выполняет следующие шаги.
• Вызывает функцию getaddrinfo(), чтобы получить список структур с адресами, подходящими для подключения к TCP-серверу, привязанному к заданному узлу
• Входит в цикл
• Отправляет целое число, обозначающее длину нужной клиенту последовательности
• Считывает номер последовательности, переданный сервером (который тоже имеет строковый формат и символ новой строки в конце)
Запустив сервер и клиент на одном и том же компьютере, мы увидим следующее:
$ ./is_seqnum_sv &
[1] 4075
$ ./is_seqnum_cl localhost
Connection from (localhost, 33273)
Sequence number: 0
$ ./is_seqnum_cl localhost 10
Connection from (localhost, 33274)
Sequence number: 1
$ ./is_seqnum_cl localhost
Connection from (localhost, 33275)
Sequence number: 11
Теперь покажем, как с помощью утилиты telnet можно выполнить отладку этого приложения:
$ telnet localhost 50000
Trying 127.0..0.1…
Connection from (localhost, 33276)
Connected to localhost.
Escape character is '^]'.
1
12
Connection closed by foreign host.
В сессии командной строки, приведенной выше, можно видеть, что ядро циклически перебирает номера динамических портов (другие реализации демонстрируют похожее поведение). В Linux такое поведение является результатом оптимизации, призванной сократить количество запросов к таблице ядра, хранящей локальные привязки сокетов. При достижении максимального значения ядро опять начинает выделять доступные номера, начиная с наименьшего элемента диапазона (который, в свою очередь, определяется в файле /proc/sys/net/ipv4/ip_local_port_range, доступном только в Linux).