(3) IDL compiler的功能就是对编辑好的IDL文件进行编译,编译后生成了下面的三个文件:
1.Header是一个头文件,此头文件包含了此IDL文件中的全局唯一标示符,数据类型定义,有关的常量定义,以及函数原型。它要同时在客户代码和服务器代码中包含(比如在C语言中用#include语句)。
2.Client stub即客户端的stub程序。
3.Server stub即服务器端的stub程序。
先介绍一下stub:RPC的一个重要思想就是使远程调用看起来象当地的调用一样,也就是说调用进程无需知道被调进程具体在哪台机器上执行。Stub就是用来保证此特性的很重要的部分。具体的讲,比如在客户端,一个进程在执行过程中调用到了某个函数fn(),此函数的具体实现是在远程的某台机器上,那么此进程实际上是调用了位于当地机器上的另外一个版本的fn()(起名为c_fn()),此c_fn()就是客户端的一个stub. 对应的,当客户端的消息发送到服务器端时,服务器端也不是把消息直接就交给真正的fn(),而是同样先交给一个不同版本的fn()(起名为s_fn()),此s_fn()就是服务器端的一个stub.
Stub的主要功能是对要发送的参数进行marshal(可理解成一种打包操作)和对接受到的参数(或返回值)进行unmarshal(解包)。Marshal操作将要发送的数据制成一种标准的格式(在DCE RPC系统中,此格式称做Network Data Representation(NDR)格式),unmarshal再从NDR格式数据包中读出所需数据。事实上,在实际系统中这些操作涉及的机制是相当复杂的。就拿marshal操作来说,实际上要传送的参数类型可能是多种的,如传值,传指向数组的指针,传指向结构的指针,而结构里又可能包含强制(arbitrary)指针,包含循环(cyclic)指针等。对这些复杂情况的具体操作这里就不再介绍,可参看DCE RPC specification.
接下来就看看客户端和服务端的stub具体做些什么:
Client stub的功能:负责收集调用远程函数需要的参数,并将这些参数marshal成消息,然后调用运行时系统(runtime system)将此消息发送给服务器端。还负责当服务器端将结果消息返回后,将结果消息unmarshal,把结果返回给应用进程。
Server stub的功能:负责对发送给它的参数消息unmarshal收集参数,然后调用位于本机上的真正客户应用进程需要调用的过程。还负责将此过程执行的结果marshal成消息,然后调用服务器端的运行时系统将结果消息发送给客户端。
(4) Client code和Server code就是由应用程序开发人员所写的客户代码和服务器代码,客户代码可能调用到各种各样的过程,服务器代码就是这些过程的真正实现。图中针对的是C语言写的代码,因此用到的编译器都是C编译器。客户代码和客户stub代码分别经过编译生成各自的目标文件,这些目标文件再与运行时库连接就生成了整个客户端可执行文件。服务器端也同理。
1.2 RPC执行过程:

下面就根据图2简要介绍一下RPC系统的执行过程:
首先,客户进程调用了一个远程过程add(i,j),则实际上调用了本地相应的stub函数,stub函数就将两个参数和函数名一起marshal成一个消息(如图),然后通过底层的操作系统将消息发到了服务器端。服务器端的操作系统把消息交给了服务器端相应的stub函数,此函数对消息unmarshal,提出参数信息后就利用这些参数进行了一个当地的add调用,而add的真正实现代码就被执行,得到需要的结果。同理可知结果的返回过程。
上述可以说是一个最简单的RPC执行过程,而在实际的RPC系统中,系统要有通用的平台性(要能为应用程序开发者屏蔽异构的平台,能处理各种不同情况下的调用),要能处理远程调用出现的异常,以及跨域调用的安全性等问题,都导致真正的系统是非常复杂的。这里对传统RPC系统只做了最简单的描述,在下面的面向对象系统(DCOM)中再做较为复杂的阐述。