UNIX: разработка сетевых приложений
15. Будем считать, что и клиент, и сервер устанавливают параметр сокета
. Между собеседниками поддерживается соединение, но через это соединение не происходит обмена данными между приложениями. Когда проходят условленные 2 ч и требуется проверить наличие связи, сколькими сегментами TCP обмениваются собеседники?SO_KEEPALIVE16. Почти все реализации определяют константу
в заголовочном файлеSO_ACCEPTCONN, но мы не описывали этот параметр. Прочтите [69], чтобы понять, зачем этот параметр существует.<sys/socket.h>Глава 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, и переделаем нашу модель клиент-сервер для применения UDP. Кроме того, мы рассмотрим использование функции connect с сокетом UDP и концепцию асинхронных ошибок.sendto8.2. Функции recvfrom и sendto
Эти две функции аналогичны стандартным функциям
иread, но требуют трех дополнительных аргументов.write#include <sys/socket.h>ssize_t recvfrom(int <i>sockfd</i>, void *<i>buff</i>, size_t <i>nbytes</i>, int <i>flags</i>,struct sockaddr *<i>from</i>, socklen_t *<i>addrlen</i>);ssize_t sendto(int <i>sockfd</i>, const void *<i>buff</i>, size_t <i>nbytes</i>, int <i>flags</i>,const struct sockaddr *<i>to</i>, socklen_t <i>addrlen</i>);<i>Обе функции возвращают количество записанных или прочитанных байтов в случае успешного выполнения, -1 в случае ошибки</i>Первые три аргумента,
,sockfdиbuff, идентичны первым трем аргументам функцийnbytesиread: дескриптор, указатель на буфер, из которого производится чтение или в который происходит запись, и число байтов для чтения или записи.writeМы расскажем об аргументе
в главе 14, где мы рассматриваем функцииflags,recv,sendиrecvmsg, поскольку сейчас в нашем простом примере они не нужны. Пока мы всегда будем устанавливать аргументsendmsgв нуль.flagsАргумент to для функции
— это структура адреса сокета, содержащая адрес протокола (например, IP-адрес и номер порта) адресата. Размер этой структуры адреса сокета задается аргументомsendto. Функцияaddrlenзаполняет структуру адреса сокета, на которую указывает аргумент from, записывая в нее протокольный адрес отправителя дейтаграммы. Число байтов, хранящихся в структуре адреса сокета, также возвращается вызывающему процессу в целом числе, на которое указывает аргументrecvform. Обратите внимание, что последний аргумент функцииaddrlenявляется целочисленным значением, в то время как последний аргумент функцииsendto— это указатель на целое значение (аргумент типа «значение-результат»).recvfromПоследние два аргумента функции recvfrom аналогичны двум последним аргументам функции
: содержимое структуры адреса сокета по завершении сообщает нам, кто отправил дейтаграмму (в случае UDP) или кто инициировал соединение (в случае TCP). Последние два аргумента функцииacceptаналогичны двум последним аргументам функцииsendto: мы заполняем структуру адреса сокета протокольным адресом получателя дейтаграммы (в случае UDP) или адресом узла, с которым будет устанавливаться соединение (в случае TCP).connectОбе функции возвращают в качестве значения функции длину данных, которые были прочитаны или записаны. При типичном использовании функции
с протоколом дейтаграмм возвращаемое значение — это объем пользовательских данных в полученной дейтаграмме.recvfromДейтаграмма может иметь нулевую длину. В случае UDP при этом возвращается дейтаграмма IP, содержащая заголовок IP (обычно 20 байт для IPv4 или 40 байт для IPv6), 8-байтовый заголовок UDP и никаких данных. Это также означает, что возвращаемое из функции
нулевое значение вполне приемлемо для протокола дейтаграмм: оно не является признаком того, что собеседник закрыл соединение, как это происходит при возвращении нулевого значения из функцииrecvfromна сокете TCP. Поскольку протокол UDP не ориентирован на установление соединения, то в нем и не существует такого события, как закрытие соединения.readЕсли аргумент from функции
является пустым указателем, то соответствующий аргумент длины (recvfrom) также должен быть пустым указателем, и это означает, что нас не интересует адрес отправителя данных.addrlen
