Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Т2. Связь_Таненбаум_СРС_ПРИС.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
1.59 Mб
Скачать

100 Глава 2. Связь

Поскольку сообщение передается по сети байт за байтом (на самом деле бит за битом), первый посланный байт будет и первым принятым. На рис. 2.9, б мы ви­дим, как будет выглядеть сообщение с рис. 2.9, а, принятое на компьютере SPARC. Нумерация байтов здесь такова, что нулевым байтом считается левый (верхний байт), а не правый (нижний байт), как в процессорах Intel. После того как сер­верная заглушка прочитает параметры по адресам 0 и 4, сервер получит, соответ­ственно, целое число, равное 83 886 080 (5х224), и строку JILL.

Очевидное, но, к сожалению, неверное решение — просто инвертировать бай­ты каждого слова после того, как оно будет принято (рис. 2.9, в). Теперь целое число стало правильным, а строка превратилась в LLIJ. Проблема состоит в том, что целые нужно приводить к другому порядку следования байтов, а строки — нет. Не имея дополнительной информации о том, где строка, а где целое, мы не в состоянии исправить положение дел.

Передача параметров по ссылке

Теперь мы подошли к сложной проблеме: как передавать указатели или, в общем случае, ссылки? Общий ответ таков: с величайшим трудом. Мы помним, что ука­затель имеет смысл только в адресном пространстве того процесса, в котором он используется. Возвращаясь к нашему примеру с процедурой read, который мы об­суждали ранее, второй параметр (адрес буфера) для клиента может быть равен, например, 1000, но нельзя же просто передать на сервер число 1000 и ожидать, что это сработает. На сервере адрес 1000 вполне может прийтись на середину тек­ста программы.

Одно из решений состоит в том, чтобы вообще забыть про указатели и ссыл­ки в качестве параметров. Однако важность таких параметров делает такое реше­ние абсолютно неподходящим. На самом деле в нем нет особой необходимости. В примере с процедурой read клиентской заглушке известно, что второй пара­метр указывает на массив символов. Предположим на минуту, что заглушка также знает и величину этого массива. После этого вырисовывается следующая страте­гия: скопировать этот массив в сообщение и передать его на сервер. Серверная заглушка может после этого вызвать сервер, передав ему указатель на этот мас­сив, даже если числовое значение этого указателя будет отличаться от передан­ного во втором параметре вызова процедуры read. Изменения, которые с помо­щью указателя сделает сервер (то есть по указанному адресу запишет данные), прямо отразятся на буфере сообщения серверной заглушки. Когда сервер закон-

2.2. Удаленный вызов процедур 101

ч ит работу, оригинальное сообщение будет отослано назад, клиентской заглуш­ке, которая скопирует буфер клиенту. В результате вызов по ссылке будет под­менен копированием/восстановлением. Несмотря на то что это не одно и то же, часто такой замены вполне достаточно.

Небольшая оптимизация позволяет сделать этот механизм вдвое эффектив­нее. Если обеим заглушкам известно, входящим или исходящим параметром является буфер для сервера, то одну из копий можно удалить. Если массив ис­пользуется сервером в качестве исходных данных (то есть при вызове write), то копировать его обратно не нужно. Если это результат, то нет необходимости из­начально передавать его серверу.

В качестве последнего комментария отметим, что нет ничего особенно хоро­шего в том, что мы можем работать с указателями на простые массивы и структу­ры, если нам по-прежнему недоступна работа с более общими вариантами указате­лей — с указателями на произвольные структуры данных, например на сложные графы. В некоторых системах делается попытка решить эту проблему путем пе­редачи серверной заглушке реальных указателей с последующей генерацией спе­циального кода в процедурах сервера для работы с этими указателями. Так, для получения данных, которые соответствуют указателю, сервер может сделать спе­циальный запрос.

Спецификация параметров и генерация заглушек

После всех этих объяснений становится ясно, что сокрытие механизма удаленно­го вызова процедур требует, чтобы вызывающая и вызываемая системы догово­рились о формате сообщений, которыми они обмениваются, и при необходимо­сти пересылки, например, данных сложной структуры, следовали определенному порядку действий. Другими словами, при совершении RPC обе стороны должны следовать одному протоколу.

В качестве простого примера рассмотрим процедуру, показанную на рис. 2.10, а. Она имеет три параметра: символ, число с плавающей точкой и массив из пяти целых чисел. Предполагая, что длина слова составляет четыре байта, протокол RPC может предписать передачу символа в крайнем правом байте слова (остав­ляя последующие три пустыми), числа с плавающей точкой — в целом слове, а массива — в виде последовательности слов с общей длиной, равной длине мас­сива, и предшествующим словом, содержащим длину последовательности (рис. 2.10, б). Если ввести подобные правила, то клиентская заглушка будет знать, что для процедуры foobar необходимо использовать тот формат, который представлен на рис. 2.10, б, а серверная заглушка — что именно такой формат бу­дет иметь входящее сообщение для вызова процедуры foobar.

Определение формата сообщения — это только одна сторона протокола RPC. Этого недостаточно. Также нам необходимо, чтобы клиент и сервер пришли к до­говоренности по вопросу представления простых типов данных, таких как целые числа, символы, логические значения и т. д. Так, протокол может предписать, чтобы целые передавались без знака, символы в 16-битной кодировке Unicode, а числа с плавающей точкой — в формате стандарта IEEE 754, и все это — в ост­роконечном формате. Только при наличии такой дополнительной информации сообщение может быть однозначно интерпретировано.