Создается прослушиваемый сокет 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 */
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
.
8.16. Резюме
Преобразовать наши эхо-клиент и эхо-сервер так, чтобы использовать UDP вместо TCP, оказалось несложно. Но при этом мы лишились множества возможностей, предоставляемых протоколом TCP: определение потерянных пакетов и повторная передача, проверка, приходят ли пакеты от корректного собеседника, и т.д. Мы возвратимся к этой теме в разделе 22.5 и увидим, как можно улучшить надежность приложения UDP.
Сокеты UDP могут генерировать асинхронные ошибки, то есть ошибки, о которых сообщается спустя некоторое время после того, как пакет был отправлен. Сокеты TCP всегда сообщают приложению о них, но в случае UDP для получения этих ошибок сокет должен быть присоединенным.
В UDP отсутствует возможность управления потоком, что очень легко продемонстрировать. Обычно это не создает проблем, поскольку многие приложения UDP построены с использованием модели «запрос-ответ» и не предназначены для передачи большого количества данных.