Обе функции возвращают в качестве значения функции длину данных, которые были прочитаны или записаны. При типичном использовании функции
recvfrom
с протоколом дейтаграмм возвращаемое значение — это объем пользовательских данных в полученной дейтаграмме.Дейтаграмма может иметь нулевую длину. В случае 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]показана функция сервера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 }
7-12
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.
Во-вторых, эта функция позволяет
fork
, один процесс сервера выполняет обработку всех клиентов. В общем случае большинство серверов TCP являются параллельными, а большинство серверов UDP — последовательными.