Для протокола TCP необходимость во введении тайм-аута повтора отсутствует, поскольку этот протокол является надежным. Если сервер Не получает запроса от клиента, время ожидания по протоколу TCP со стороны клиента закончится и клиент повторит передачу. Когда сервер получает запрос клиента, он уведомляет об этом последний. Если уведомление о получении будет утрачено по пути к клиенту, тот должен будет еще раз переслать запрос. Повторные запросы сбрасываются сервером, но уведомления об их получении отсылаются клиенту. В надежных протоколах правильность доставки (время ожидания, повторная передача, обработка лишних копий данных и лишних уведомлений) обеспечивается на транспортном уровне и не входит в задачи библиотеки RPC. Один запрос, отправленный клиентом на уровне RPC, будет получен сервером ровно в одном экземпляре на уровне RPC. В противном случае клиент RPC получит сообщение о невозможности связаться с сервером. При этом совершенно не важно, что происходит на сетевом и транспортном уровнях.
После создания дескриптора клиента можно использовать функцию clnt_control для получения информации и изменения свойств клиента. Эта функция работает аналогично fcntl для дескрипторов файлов или getsockopt и setsockopt для сокетов:
#include rpc/rpc.h
bool_t clnt_control(CLIENT
/* Возвращает TRUE в случае успешного завершения, FALSE – в случае ошибки */
Здесь
Изменим программу-клиент из листинга 16.2, добавив в нее вызов данной функции, и выведем значения тайм-аутов. В листинге 16.9 приведен текст новой программы-клиента.
//sunrpc/square5/client.c
1 #include "unpipc.h"
2 #include "square.h"
3 int
4 main(int argc, char **argv)
5 {
6 CLIENT *cl;
7 square_in in;
8 square_out *outp;
9 struct timeval tv;
10 if (argc != 4)
11 err_quit("usage: client hostname integer-value protocol");
12 cl = Clnt_create(argv[1], SQUARE_PROG, SQUARE_VERS, argv[3]);
13 Clnt_control(cl, CLGET_TIMEOUT, (char*)tv);
14 printf("timeout = %ld sec, %ld usec\n", tv.tv_sec, tv.tv_usec);
15 if (clnt_control(cl, CLGET_RETRY_TIMEOUT, (char *) tv) == TRUE)
16 printf("retry timeout = %ld sec, %ld usec\n", tv.tv_sec, tv.tv_usec);
17 in.arg1 = atol(argv[2]);
18 if ((outp = squareproc_1(in, cl)) == NULL)
19 err_quit("%s", clnt_sperror(cl, argv[1]));
20 printf(result: %ld\n", outp-res1);
21 exit(0);
22 }
10-12 Теперь протокол, являющийся последним аргументом clnt_create, указывается в качестве нового параметра командной строки.
13-14 Первым аргументом clnt_control является дескриптор клиента, вторым — тип запроса, а третьим — указатель на буфер. Наш первый запрос имеет значение CLGET_TIMEOUT; при этом возвращается значение общего тайм-аута в структуре timeval, адрес которой передается третьим аргументом. Этот запрос корректен для всех протоколов.
15-16 Следующий запрос имеет значение CLGET_RETRY_TIMEOUT. При этом должно возвращаться значение тайм-аута повтора, но этот запрос корректен только для протокола UDP. Следовательно, если функция возвращает значение FALSE, мы ничего не печатаем.
Изменим также и программу-сервер, добавив в нее ожидание продолжительностью 1000 секунд вместо 5, чтобы гарантировать получение тайм-аута по запросу клиента. Запустим сервер на узле bsdi, а клиент запустим дважды, один раз указав в качестве протокола TCP, а другой — UDP. Результат будет не таким, как мы ожидали:
solaris % date ; client bsdi 44 tcp ; date
Wed Apr 22 14:46:57 MST 1998
timeout = 30 sec, 0 usec
bsdi: RPC: Timed out
Wed Apr 22 14:47:22 MST 1998