В определенном смысле процессы отправки сообщения на удаленный хост и получения ответа очень похожи на вызов функции в языке программирования. В обоих случаях вы передаете один или несколько параметров и получаете результат. Это соображение навело разработчиков на мысль о том, что можно попробовать организовать запросно-ответное взаимодействие по сети, выполняемое в форме вызовов процедур. Такое решение позволяет упростить и сделать более привычной разработку сетевых приложений. Например, представьте себе процедуру запроса IP-адреса хоста, get_IP_address(host name), работающую посредством отправки UDP-пакетов на сервер DNS, ожидания ответа и отправки повторного запроса в случае наступления тайм-аута (если одна из сторон работает недостаточно быстро). Таким образом, все детали, связанные с особенностями сетевых технологий, скрыты от программиста.
Ключевая работа в этой области написана в 1984 году Бирреллом и Нельсоном (Birrell and Nelson). По сути, было предложено разрешить программам вызывать процедуры, расположенные на удаленных хостах. Когда процесс на устройстве 1 вызывает процедуру на устройстве 2, вызывающий процесс устройства 1 блокируется, и на устройстве 2 выполняется вызванная процедура. Информация от вызывающего процесса может передаваться в виде параметров и приходить обратно в виде результата процедуры. Передача сообщений по сети скрыта от программиста приложения. Такая технология известна под названием удаленного вызова процедур (Remote Procedure Call, RPC). Она легла в основу многих сетевых приложений. Традиционно вызывающая процедура считается клиентом, а вызываемая — сервером. Мы будем называть их так же.
Идея RPC состоит в том, чтобы сделать вызов удаленной процедуры максимально похожим на локальный вызов. В простейшем случае для вызова удаленной процедуры клиентская программа должна быть связана с маленькой библиотечной процедурой, клиентской заглушкой (client stub), которая представляет серверную процедуру в пространстве клиентских адресов. Аналогично сервер должен быть связан с процедурой под названием серверная заглушка (server stub). Эти процедуры скрывают тот факт, что вызов клиентом серверной процедуры не является локальным.
Реальные шаги, выполняемые при удаленном вызове процедуры, показаны на илл. 6.29. Шаг 1 заключается в вызове клиентом клиентской заглушки. Это локальный вызов процедуры, при котором параметры самым обычным образом помещаются в стек. Шаг 2 состоит в упаковке параметров клиентской заглушки в сообщение и в осуществлении системного вызова для его отправки. Упаковка параметров называется маршалингом (marshaling). На шаге 3 операционная система передает сообщение с клиентского устройства на сервер. Шаг 4 заключается в том, что операционная система передает входящий пакет серверной заглушке. На шаге 5 серверная заглушка вызывает серверную процедуру с распакованными параметрами. При ответе выполняются те же самые шаги, но передача происходит в обратном направлении.
Илл. 6.29. Этапы выполнения удаленного вызова процедуры. Серым цветом выделены заглушки
Важнее всего здесь то, что клиентская процедура, написанная пользователем, выполняет обычный (то есть локальный) вызов клиентской заглушки, имеющей то же имя, что и серверная процедура. Поскольку клиентская процедура и клиентская заглушка существуют в одном адресном пространстве, параметры передаются обычным способом. Аналогично серверная процедура вызывается процедурой, находящейся в том же адресном пространстве, с ожидаемыми параметрами. С точки зрения серверной процедуры не происходит ничего необычного. Таким образом, вместо ввода/вывода с помощью сокетов сетевая коммуникация осуществляется обычным вызовом процедуры.
Несмотря на элегантность концепции RPC, в ней есть подводные камни. Речь идет прежде всего об использовании указателей в качестве параметров. В обычной ситуации передача указателя процедуре не представляет никаких сложностей. Вызываемая процедура может использовать указатель так же, как и вызывающая, поскольку они обе существуют в одном виртуальном адресном пространстве. При удаленном вызове процедуры передача указателей невозможна, потому что адресные пространства клиента и сервера отличаются.