Литмир - Электронная Библиотека
ЛитМир: бестселлеры месяца
Содержание  
A
A

И функция

recvfrom
, и функция
sendto
могут использоваться с TCP, хотя обычно в этом нет необходимости.

8.3. Эхо-сервер UDP: функция main

Теперь мы переделаем нашу простую модель клиент-сервер из главы 5, используя UDP. Диаграмма вызовов функций в программах наших клиента и сервера UDP показана на рис. 8.1. На рис. 8.2 представлены используемые функции. В листинге 8.1[1] показана функция сервера

main
.

UNIX: разработка сетевых приложений - img_71.png

Рис. 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.

На рис. 8.3 показано обобщение нашей модели TCP клиент-сервер из главы 5, когда два клиента устанавливают соединения с сервером.

UNIX: разработка сетевых приложений - img_72.png

Рис. 8.3. Обобщение модели TCP клиент-сервер с двумя клиентами

Здесь имеется два присоединенных сокета, и каждый из присоединенных сокетов на узле сервера имеет свой собственный буфер приема. На рис. 8.4 показан случай, когда два клиента отправляют дейтаграммы серверу UDP.

UNIX: разработка сетевых приложений - img_73.png

Рис. 8.4. Обобщение модели UDP клиент-сервер с двумя клиентами

Существует только один процесс сервера, и у него имеется один сокет, на который сервер получает все приходящие дейтаграммы и с которого отправляет все ответы. У этого сокета имеется буфер приема, в который помещаются все приходящие дейтаграммы.

Функция

main
в листинге 8.1 является зависящей от протокола (она создает сокет семейства
AF_INET
, а затем выделяет и инициализирует структуру адреса сокета IPv4), но функция
dg_echo
от протокола не зависит. Причина, по которой функция
dg_echo
не зависит от протокола, заключается в том, что вызывающий процесс (в нашем случае функция
main
) должен разместить в памяти структуру адреса сокета корректного размера, и указатель на эту структуру вместе с ее размером передаются в качестве аргументов функции
dg_echo
. Функция
dg_echo
никогда не углубляется в эту структуру: она просто передает указатель на нее функциям
recvfrom
и
sendto
. Функция
recvfrom
заполняет эту структуру, вписывая в нее IP-адрес и номер порта клиента, и поскольку тот же указатель (
pcliaddr
) затем передается функции
sendto
в качестве адреса получателя, таким образом дейтаграмма отражается обратно клиенту, отправившему дейтаграмму.

вернуться

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.

97
{"b":"225366","o":1}
ЛитМир: бестселлеры месяца