Дейтаграмма может иметь нулевую длину. В случае UDP при этом возвращается дейтаграмма IP, содержащая заголовок IP (обычно 20 байт для IPv4 или 40 байт для IPv6), 8-байтовый заголовок UDP и никаких данных. Это также означает, что возвращаемое из функции recvfrom
нулевое значение вполне приемлемо для протокола дейтаграмм: оно не является признаком того, что собеседник закрыл соединение, как это происходит при возвращении нулевого значения из функции read
на сокете TCP. Поскольку протокол UDP не ориентирован на установление соединения, то в нем и не существует такого события, как закрытие соединения.
Если аргумент from функции recvfrom
является пустым указателем, то соответствующий аргумент длины ( addrlen
) также должен быть пустым указателем, и это означает, что нас не интересует адрес отправителя данных.
И функция recvfrom
, и функция sendto
могут использоваться с TCP, хотя обычно в этом нет необходимости.
8.3. Эхо-сервер UDP: функция main
Теперь мы переделаем нашу простую модель клиент-сервер из главы 5, используя UDP. Диаграмма вызовов функций в программах наших клиента и сервера UDP показана на рис. 8.1. На рис. 8.2 представлены используемые функции. В листинге 8.1 [1] Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.
показана функция сервера main
.
Рис. 8.2. Простая модель клиент-сервер, использующая UDP
Листинг 8.1. Эхо-сервер UDP
//udpcliserv/udpserv01.с
1 #include "unp.h"
2
3 intmain(int argc, char **argv)
4 {
5 int sockfd;
6 struct sockaddr_in servaddr, cliaddr;
7 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
8 bzero(&servaddr, sizeof(servaddr));
9 servaddr.sin_family = AF_INET;
10 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
11 servaddr.sin_port = htons(SERV_PORT);
12 Bind(sockfd, (SA*)&servaddr, sizeof(servaddr));
13 dg_echo(sodkfd, (SA*)&cliaddr, sizeof(cliaddr));
14 }
Создание сокета UDP, связывание с заранее известным портом при помощи функции bind
7-12
Мы создаем сокет UDP, задавая в качестве второго аргумента функции socket
значение SOCK_DGRAM
(сокет дейтаграмм в протоколе IPv4). Как и в примере сервера TCP, адрес IPv4 для функции bind задается как INADDR_ANY
, а заранее известный номер порта сервера — это константа SERV_PORT
из заголовка unp.h
.
13
Затем вызывается функция dg_echo
для обработки клиентского запроса сервером.
8.4. Эхо-сервер UDP: функция dg_echo
В листинге 8.2 показана функция dg_echo
.
Листинг 8.2. Функция dg_echo: отражение строк на сокете дейтаграмм
//lib/dg_echo.c
1 #include "unp.h"
2 void
3 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
4 {
5 int n;
6 socklen_t len;
7 char mesg[MAXLINE];
8 for (;;) {
9 len = clilen;
10 n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
11 Sendto(sockfd, mesg, n, 0, pcliaddr, len);
12 }
13 }
Чтение дейтаграммы, отражение отправителю
8-12
Эта функция является простым циклом, в котором очередная дейтаграмма, приходящая на порт сервера, читается функцией recvfrom
и с помощью функции sendto
отправляется обратно.
Несмотря на простоту этой функции, нужно учесть ряд важных деталей. Во- первых, эта функция никогда не завершается. Поскольку UDP — это протокол, не ориентированный на установление соединения, в нем не существует никаких аналогов признака конца файла, используемого в TCP.
Во-вторых, эта функция позволяет создать последовательный сервер , а не параллельный, который мы получали в случае TCP. Поскольку нет вызова функции fork
, один процесс сервера выполняет обработку всех клиентов. В общем случае большинство серверов TCP являются параллельными, а большинство серверов UDP — последовательными.
Для сокета на уровне UDP происходит неявная буферизация дейтаграмм в виде очереди. Действительно, у каждого сокета UDP имеется буфер приема, и каждая дейтаграмма, приходящая на этот сокет, помещается в его буфер приема. Когда процесс вызывает функцию recvfrom
, очередная дейтаграмма из буфера возвращается процессу в порядке FIFO (First In, First Out — первым пришел, первым обслужен). Таким образом, если множество дейтаграмм приходит на сокет до того, как процесс может прочитать данные, уже установленные в очередь для сокета, то приходящие дейтаграммы просто добавляются в буфер приема сокета. Но этот буфер имеет ограниченный размер. Мы обсуждали этот размер и способы его увеличения с помощью параметра сокета SO_RCVBUF
в разделе 7.5.
Читать дальше
Конец ознакомительного отрывка
Купить книгу