7-12
fgets
, отправка строки серверу с помощью функции sendto
, чтение отраженного ответа сервера с помощью функции recvfrom
и помещение отраженной строки в стандартный поток вывода с помощью функции fputs
.Наш клиент не запрашивал у ядра присваивания динамически назначаемого порта своему сокету (тогда как для клиента TCP это имело место при вызове функции connect
sendto
ядро выбирает динамически назначаемый порт, если с этим сокетом еще не был связан никакой локальный порт. Как и в случае TCP, клиент может вызвать функцию bind явно, но это делается редко.Обратите внимание, что при вызове функции recvfrom
Как и в случае функции сервера dg_echo
dg_cli
является не зависящей от протокола, но функция main клиента зависит от протокола. Функция main размещает в памяти и инициализирует структуру адреса сокета, относящегося к определенному типу протокола, а затем передает функции dg_cli
указатель на структуру вместе с ее размером.8.7. Потерянные дейтаграммы
Клиент и сервер UDP в нашем примере являются ненадежными. Если дейтаграмма клиента потеряна (допустим, она проигнорирована неким маршрутизатором между клиентом и сервером), клиент навсегда заблокируется в своем вызове функции recvfrom
dg_cli
, ожидая от сервера ответа, который никогда не придет. Аналогично, если дейтаграмма клиента приходит к серверу, но ответ сервера потерян, клиент навсегда заблокируется в своем вызове функции recvfrom
. Единственный способ предотвратить эту ситуацию — поместить тайм-аут в клиентский вызов функции recvfrom
. Мы рассмотрим это в разделе 14.2.Простое помещение тайм-аута в вызов функции recvfrom
8.8. Проверка полученного ответа
В конце раздела 8.6 мы упомянули, что любой процесс, который знает номер динамически назначаемого порта клиента, может отправлять дейтаграммы нашему клиенту, и они будут перемешаны с нормальными ответами сервера. Все, что мы можем сделать, — это изменить вызов функции recvfrom
Сначала мы изменяем функцию клиента main
servaddr.sin_port = htons(SERV_PORT);
присваиванием
servaddr.sin_port = htons(7);
Теперь мы можем использовать с нашим клиентом любой узел, на котором работает стандартный эхо-сервер.
Затем мы переписываем функцию dg_cli
recvfrom
. Мы показываем ее в листинге 8.5.Листинг 8.5
. Версия функции dg_cli, проверяющая возвращаемый адрес сокета//udpcliserv/dgcliaddr.c
1 #include "unp.h"
2 void
3 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
4 {
5 int n;
6 char sendline[MAXLINE], recvline[MAXLINE + 1];
7 socklen_t len;
8 struct sockaddr *preply_addr;
9 preply_addr = Malloc(servlen);
10 while (Fgets(sendline, MAXLINE, fp) != NULL) {
11 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
12 len = servlen;