client.c
#include
#include "log.h"
main(int argc, char* argv[]) {
CLIENT *cl;
char *server, *mystring, *clnttime;
time_t bintime;
int* result;
if (argc != 2) {
fprintf(stderr, "Формат вызова: %s Адрес_хоста\n", argv[0]);
exit(1);
}
server = argv[1];
/* Получим дескриптор клиента. В случае неудачи — сообщим
о невозможности установления связи с сервером */
if ((cl =
clnt_create(server, LOG_PROG, LOG_VER, "udp")) == NULL) {
clnt_pcreateerror(server);
exit(2);
}
/* Выделим буфер для строки */
mystring = (char*)malloc(100);
/* Определим время события */
bintime = time((time_t*)NULL);
clnttime = ctime(&bintime);
sprintf(mystring, "%s - Клиент запущен", clntime);
/* Передадим сообщение для журнала — время начала
работы клиента. В случае неудачи — сообщим об ошибке */
if ((result = rlog_1(&mystring, cl)) == NULL) {
fprintf(stderr, "error2\n");
clnt_perror(cl, server);
exit(3);
}
/* В случае неудачи на удаленном компьютере сообщим об ошибке */
if (*result != 0)
fprintf(stderr, "Ошибка записи в журнал\n");
/* Освободим дескриптор */
clnt_destroy(cl);
exit(0);
}
Заглушка клиента log_clnt.скомпилируется с модулем client.сдля получения исполняемой программы клиента.
cc -o rlog client.c log_clnt.c -lns1
Заглушка сервера log_svc.си процедура log.cкомпилируются для получения исполняемой программы сервера.
cc -o logger log_svc.c log.c -lns1
Теперь на некотором хосте server.nowhere.ru необходимо запустить серверный процесс:
$ logger
После чего при запуске клиента rlog на другой машине сервер добавит соответствующую запись в файл журнала.
Схема работы RPC в этом случае приведена на рис. 6.20. Модули взаимодействуют следующим образом:
1. Когда запускается серверный процесс, он создает сокет UDP и связывает любой локальный порт с этим сокетом. Далее сервер вызывает библиотечную функцию svc_register(3N) для регистрации номеров программы и ее версии. Для этого функция обращается к процессу portmap(1M) и передает требуемые значения. Сервер portmap(1M) обычно запускается при инициализации системы и связывается с некоторым общеизвестным портом. Теперь portmap(3N) знает номер порта для нашей программы и версии. Сервер же ожидает получения запроса. Заметим, что все описанные действия производятся заглушкой сервера, созданной компилятором rpcgen(1M) .
2. Когда запускается программа rlog , первое, что она делает, — вызывает библиотечную функцию clnt_create(3N) , указывая ей адрес удаленной системы, номера программы и версии, а также транспортный протокол. Функция направляет запрос к серверу portmap(1M) удаленной системы server.nowhere.ru и получает номер удаленного порта для сервера журнала.
3. Клиент вызывает процедуру rlog_1()
, определенную в заглушке клиента, и передает управление заглушке. Та, в свою очередь, формирует запрос (преобразуя аргументы в формат XDR) в виде пакета UDP и направляет его на удаленный порт, полученный от сервера portmap(1M) . Затем она некоторое время ожидает отклика и в случае неполучения повторно отправляет запрос. При благоприятных обстоятельствах запрос принимается сервером logger (модулем заглушки сервера). Заглушка определяет, какая именно функция была вызвана (по номеру процедуры), и вызывает функцию rlog_1()
модуля log.c. После возврата управления обратно в заглушку преобразует возвращенное функцией rlog_1()
значение в формат XDR, и формирует отклик также в виде пакета UDP. После получения отклика заглушка клиента извлекает возвращенное значение, преобразует его и возвращает в головную программу клиента.
Рис. 6.20. Работа системы RPC
Поддержка сети в BSD UNIX
Перейдем теперь к обсуждению внутренней архитектуры сетевого в UNIX. Разговор начнем с ветви UNIX, в которой реализация TCP/IP появилась впервые — BSD UNIX.
Сетевая подсистема UNIX может быть представлена состоящей из трех уровней, каждый из которых отвечает за выполнение определенных функций:
Транспортный уровень |
Обмен данными между процессами |
Сетевой уровень |
Маршрутизация сообщений |
Уровень сетевого интерфейса |
Передача данных по физической сети |
Два верхних уровня представляют собой модули коммуникационных протоколов, а нижний уровень по существу является драйвером устройства. Легко заметить, что представленные уровни соответствуют транспортному, сетевому уровням и уровню канала данных модели OSI.
Читать дальше