В качестве примера рассмотрим 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
/* Номер программы */
#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
#include
#include
#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
result = 0;
close(fd);
return(&result); /* Возвращаем результат — адрес result */
}
Заглушка клиента принимает аргумент, передаваемый удаленной процедуре, делает необходимые преобразования, формирует запрос на сервер portmap(1M) , обменивается данными с сервером удаленной процедуры и, наконец, передает возвращаемое значение клиенту. Для клиента вызов удаленной процедуры сводится к вызову заглушки и ничем не отличается от обычного локального вызова.
Читать дальше