По-разному обстоит дело и с семантикой вызова. Например, если выполняется с использованием ненадежного транспортного протокола (UDP), система выполняет повторную передачу сообщения через короткие промежутки времени (тайм-ауты). Если приложение-клиент не получает отклик, то с уверенностью можно сказать, что процедура была выполнена ноль или большее число раз. Если отклик был получен, приложение может сделать вывод, что процедура была выполнена хотя бы однажды. При использовании надежного транспортного протокола (TCP) в случае получения отклика можно сказать, что процедура была выполнена один раз. Если же отклик не получен, определенно сказать, что процедура выполнена не была, нельзя. [82]
Как это работает?
По существу, собственно система RPC является встроенной в программу- клиент и программу-сервер. Отрадно, что при разработке распределенных приложений, не придется вникать в подробности протокола RPC или программировать обработку сообщений. Система предполагает существование соответствующей среды разработки, которая значительно облегчает жизнь создателям прикладного программного обеспечения. Одним из ключевых моментов в RPC является то, что разработка распределенного приложения начинается с определения интерфейса объекта — формального описания функций сервера, сделанного на специальном языке. На основании этого интерфейса затем автоматически создаются заглушки клиента и сервера. Единственное, что необходимо сделать после этого, — написать фактический код процедуры.
В качестве примера рассмотрим RPC фирмы Sun Microsystems.
Система состоит из трех основных частей:
□ rpcgen(1) — RPC-компилятор, который на основании описания интерфейса удаленной процедуры генерирует заглушки клиента и сервера в виде программ на языке С.
□ Библиотека XDR (eXternal Data Representation), которая содержит функции для преобразования различных типов данных в машинно- независимый вид, позволяющий производить обмен информацией между разнородными системами.
□ Библиотека модулей, обеспечивающих работу системы в целом.
Рассмотрим пример простейшего распределенного приложения для ведения журнала событий. Клиент при запуске вызывает удаленную процедуру записи сообщения в файл журнала удаленного компьютера.
Для этого придется создать как минимум три файла: спецификацию интерфейсов удаленных процедур log.x (на языке описания интерфейса), собственно текст удаленных процедур log.c и текст головной программы клиента
main()
—
client.c (на языке С) .
Компилятор rcpgen(1) на основании спецификации log.x создает три файла: текст заглушек клиента и сервера на языке С (log_clnt.c и log_svc.с) и файл описаний log.h, используемый обеими заглушками.
Итак, рассмотрим исходные тексты программ.
log.x
В этом файле указываются регистрационные параметры удаленной процедуры — номера программы, версии и процедуры, а также определяется интерфейс вызова — входные аргументы и возвращаемые значения. Таким образом, определена процедура RLOG, в качестве аргумента принимающая строку (которая будет записана в журнал), а возвращаемое значение стандартно указывает на успешное или неудачное выполнение заказанной операции.
program LOG_PROG {
version LOG_VER {
int RLOG(string) = 1;
} = 1;
} = 0x31234567;
Компилятор rpcgen(1) создает файл заголовков log.h, где, в частности, определены процедуры:
log.h
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#ifndef _LOG_H_RPCGEN
#define _LOGH_H_RPCGEN
#include <rpc/rpc.h>
/* Номер программы */
#define LOG_PROG ((unsigned long)(0x31234567))
#define LOG_VER ((unsigned long)(1)) /* Номер версии */
#define RLOG ((unsigned long)(1)) /* Номер процедуры */
extern int *rlog_1();
/* Внутренняя процедура - нам ее использовать не придется */
extern int log_prog_1_freeresult();
#endif /* !_LOG_H_RPCGEN */
Рассмотрим этот файл внимательно. Компилятор транслирует имя
RLOG
, определенное в файле описания интерфейса, в
rlog_1
, заменяя прописные символы на строчные и добавляя номер версии программы с подчеркиванием. Тип возвращаемого значения изменился с
int
на
int*
. Таково правило — RPC позволяет передавать и получать только адреса объявленных при описании интерфейса параметров. Это же правило касается и передаваемой в качестве аргумента строки. Хотя из файла
print.h это не следует, на самом деле в качестве аргумента функции
rlog_1()
также передается адрес строки.
Помимо файла заголовков компилятор rpcgen(1) создает модули заглушки клиента и заглушки сервера. По существу, в тексте этих файлов заключен весь код удаленного вызова.
Заглушка сервера является головной программой, обрабатывающей все сетевое взаимодействие с клиентом (точнее, с его заглушкой). Для выполнения операции заглушка сервера производит локальный вызов функции, текст которой необходимо написать:
log.с
#include <rpc/rpc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "log.h"
int* rlog_1(char** arg) {
/* Возвращаемое значение должно определяться как static */
static int result;
int fd; /* Файловый дескриптор журнала */
int len;
result = 1;
/* Откроем файл журнала (создадим, если он не существует),
в случае неудачи вернем код ошибки result == 1. */
if ((fd = open("./server.log",
O_CREAT | O_RDWR | O_APPEND)) < 0)
return(&result);
len = strlen(*arg);
if (write(fd, arg, strlen(arg) != len)
result = 1;
else