4. Будем считать, что два клиента TCP запускаются одновременно. Оба устанавливают параметр сокета
SO_REUSEADDR
и затем с помощью функции
bind
связываются с одним и тем же локальным IP-адресом и одним и тем же локальным портом (допустим, 1500). Но один из клиентов соединяется с помощью функции connect с адресом 198.69.10.2, порт 7000, а второй — с адресом 198.69.10.2 (тот же IP-адрес собеседника), порт 8000. Опишите возникающую ситуацию гонок.
5. Получите исходный код для примеров в этой книге (см. предисловие) и откомпилируйте программу sock (см. раздел В.3). Сначала классифицируйте свой узел как узел, не поддерживающий
многоадресную передачу, затем — как поддерживающий многоадресную передачу, но не поддерживающий параметр
SO_REUSEPORT
, и наконец, как узел, поддерживающий многоадресную передачу с предоставлением параметра
SO_REUSEPORT
. Попытайтесь запустить несколько экземпляров программы sock в качестве сервера TCP (параметр
– s
командной строки) на одном и том же порте, связывая универсальный адрес, один из адресов интерфейсов вашего узла и адрес закольцовки (loopback address). Нужно ли вам задавать параметр
SO_REUSEADDR
(параметр
– А
командной строки)? Используйте программу
netstat
для просмотра прослушиваемых сокетов.
6. Продолжайте предыдущий пример, но запустите сервер UDP (параметр
– u
командной строки) и попытайтесь запустить два экземпляра, связанные с одними и теми же локальным IP-адресом и портом. Если ваша реализация поддерживает параметр
SO_REUSEPORT
, попытайтесь использовать ее (параметр
– T
командной строки).
7. Многие версии утилиты
ping
имеют флаг
– d
, задающий параметр сокета
SO_DEBUG
. В чем его назначение?
8. Продолжая пример в конце нашего обсуждения параметра сокета
TCP_NODELAY
, предположим, что клиент выполняет две операции записи с помощью функции
write
: первую для 4 байт данных и вторую для 396 байт. Также будем считать, что время задержки ACK — 100 мс, период RTT между клиентом и сервером равен 100 мс, а время обработки сервером каждого клиентского запроса — 50 мс. Нарисуйте временную диаграмму, показывающую взаимодействие алгоритма Нагла с задержанными сегментами ACK.
9. Снова выполните предыдущее упражнение, считая, что установлен параметр сокета
TCP_NODELAY
.
10. Снова выполните упражнение 8, считая, что процесс вызывает функцию
writev
один раз для обоих буферов (4-байтового и 396-байтового).
11. Прочтите RFC 1122 [10], чтобы определить рекомендуемый интервал для задержанных сегментов ACK.
12. В какой из версий наш сервер тратит больше времени — в листинге 5.1 или 5.2? Что происходит, если сервер устанавливает параметр сокета
SO_KEEPALIVE
, через соединение не происходит обмена данными, узел клиента выходит из строя и не перезагружается?
13. В какой из версий наш клиент тратит больше времени — в листинге 5.3 или 5.4? Что происходит, если клиент устанавливает параметр сокета
SO_KEEPALIVE
, через соединение не происходит обмена данными и узел сервера выходит из строя и не перезагружается?
14. В какой из версий наш клиент тратит больше времени — в листинге 5.3 или 6.2? Что происходит, если клиент устанавливает параметр сокета
SO_KEEPALIVE
, через соединение не происходит обмена данными и узел сервера выходит из строя и не перезагружается?
15. Будем считать, что и клиент, и сервер устанавливают параметр сокета
SO_KEEPALIVE
.
Между собеседниками поддерживается соединение, но через это соединение не происходит обмена данными между приложениями. Когда проходят условленные 2 ч и требуется проверить наличие связи, сколькими сегментами TCP обмениваются собеседники?
16. Почти все реализации определяют константу
SO_ACCEPTCONN
в заголовочном файле
<sys/socket.h>
, но мы не описывали этот параметр. Прочтите [69], чтобы понять, зачем этот параметр существует.
Глава 8
Основные сведения о сокетах UDP
8.1. Введение
Приложения, использующие TCP и UDP, фундаментально отличаются друг от друга, потому что UDP является ненадежным протоколом дейтаграмм, не ориентированным на установление соединения, и этим принципиально непохож на ориентированный на установление соединения и надежную передачу потока байтов TCP. Тем не менее есть случаи, когда имеет смысл использовать UDP вместо TCP. Подобные случаи мы рассматриваем в разделе 22.4. Некоторые популярные приложения построены с использованием UDP, например DNS (Domain Name System — система доменных имен), NFS (сетевая файловая система — Network File System) и SNMP (Simple Network Management Protocol — простой протокол управления сетью).
На рис. 8.1 показаны вызовы функций для типичной схемы клиент-сервер UDP. Клиент не устанавливает соединения с сервером. Вместо этого клиент лишь отправляет серверу дейтаграмму, используя функцию
sendto
(она описывается в следующем разделе), которой нужно задать адрес получателя (сервера) в качестве аргумента. Аналогично, сервер не устанавливает соединения с клиентом. Вместо этого сервер лишь вызывает функцию
recvfrom
, которая ждет, когда придут данные от какого-либо клиента. Функция
recvfrom
возвращает адрес клиента (для данного протокола) вместе с дейтаграммой, и таким образом сервер может отправить ответ именно тому клиенту, который прислал дейтаграмму.
Рис. 8.1. Функции сокета для модели клиент-сервер UDP
Рисунок 8.1 иллюстрирует временную диаграмму типичного сценария обмена UDP-дейтаграммами между клиентом и сервером. Мы можем сравнить этот пример с типичным обменом по протоколу TCP, изображенным на рис. 4.1.
В этой главе мы опишем новые функции, применяемые с сокетами UDP, —
recvfrom
и
sendto
, и переделаем нашу модель клиент-сервер для применения UDP. Кроме того, мы рассмотрим использование функции connect с сокетом UDP и концепцию асинхронных ошибок.
8.2. Функции recvfrom и sendto
Эти две функции аналогичны стандартным функциям
read
и
write
, но требуют трех дополнительных аргументов.
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void * buff, size_t nbytes, int flags,