
- •Глава 2 Связь
- •2.1. Уровни протоколов
- •2.1. Уровни протоколов 83
- •84 Глава 2. Связь
- •2.1. Уровни протоколов 85
- •2.1.1. Низкоуровневые протоколы
- •86 Глава 2. Связь
- •2.1. Уровни протоколов 87
- •2.1.2. Транспортные протоколы (метод_Метелап лр_1)
- •88 Глава 2. Связь
- •2.1. Уровни протоколов 89
- •92 Глава 2. Связь
- •2.1.3. Протоколы верхнего уровня
- •2.1. Уровни протоколов 91
- •92 Глава 2. Связь
- •2.2. Удаленный вызов процедур 93
- •2.2. Удаленный вызов процедур
- •94 Глава 2. Связь
- •2.2.1. Базовые операции rpc
- •2.2. Удаленный вызов процедур 95
- •96 Глава 2. Связь
- •2.2. Удаленный вызов процедур 97
- •98 Глава 2. Связь
- •2.2.2. Передача параметров
- •2.2. Удаленный вызов процедур 99
- •100 Глава 2. Связь
- •2.2. Удаленный вызов процедур 101
- •102 Глава 2. Связь
- •2.2. Удаленный вызов процедур 103
- •2 .2.3. Расширенные модели rpc
- •104 Глава 2. Связь
- •2.2. Удаленный вызов процедур 105
- •106 Глава 2. Связь
- •2.2.4. Пример — dce rpc
- •2.2. Удаленный вызов процедур 107
- •108 Глава 2. Связь
- •2.2. Удаленный вызов процедур 109
- •110 Глава 2. Связь
- •2.3. Обращение к удаленным объектам 111
- •2.3. Обращение к удаленным объектам
- •112 Глава 2. Связь
- •2.3.1. Распределенные объекты
- •2.3. Обращение к удаленным объектам 113
- •114 Глава 2. Связь
- •2.3.2. Привязка клиента к объекту
- •2.3. Обращение к удаленным объектам 115
- •116 Глава 2. Связь
- •2.3. Обращение к удаленным объектам 117
- •2.3.3. Статическое и динамическое удаленное обращение к методам
- •118 Глава 2. Связь
- •2.3.4. Передача параметров
- •2.3. Обращение к удаленным объектам 119
- •120 Глава 2. Связь
- •2.3.5. Пример 1 — удаленные объекты dce
- •2.3. Обращение к удаленным объектам 121
- •122 Глава 2. Связь
- •2.3.6. Пример 2 — Java rmi
- •2.3. Обращение к удаленным объектам 123
- •124 Глава 2. Связь
- •2.3. Обращение к удаленным объектам 125
- •126 Глава 2. Связь
- •2.4. Связь посредством сообщений
- •2.4.1. Сохранность и синхронность во взаимодействиях
- •2 .4. Связь посредством сообщений 127
- •128 Глава 2. Связь
- •2.4. Связь посредством сообщений 129
- •130 Глава 2. Связь
- •2.4. Связь посредством сообщений 131
- •2.4.2. Нерезидентная связь на основе сообщений
- •132 Глава 2. Связь
- •2.4. Связь посредством сообщений 133
- •134 Глава 2. Связь
- •2.4. Связь посредством сообщений 135
- •136 Глава 2. Связь
- •2.4.3. Сохранная связь на основе сообщений
- •2.4. Связь посредством сообщений 137
- •1 38 Глава 2. Связь
- •2.4. Связь посредством сообщений 139
- •140 Глава 2. Связь
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, и все это — в остроконечном формате. Только при наличии такой дополнительной информации сообщение может быть однозначно интерпретировано.