RPC 协议
Hprose 远程过程调用(RPC)协议支持批量调用,引用参数传递,同步和异步通讯。
Hprose 远程过程调用(RPC)协议非常简单。它由几段数据组成。每段数据包括至少一个字节的标记。下面是Hprose 远程过程调用(RPC)协议的标记列表:
- 0x46('F'): 函数列表
- 0x43('C'): RPC 调用
- 0x52('R'): RPC 结果
- 0x41('A'): RPC 参数
- 0x45('E'): RPC 错误
- 0x7A('z'): 结束
函数列表
远程过程调用(RPC)服务器可以发布一个或多个函数/方法。每个函数/方法拥有一个名字。函数/方法名以字符串的形式表示。函数列表是一个函数方法名的 List。
如果客户端只发送一个结束标记 'z' 到服务器端,而没有其它数据。那么服务器端应该返回函数列表。
Hprose 远程过程调用(RPC)协议是动态弱类型的。它支持动态类型参数,变长参数函数和方法。所以函数列表只是一个函数/方法名列表,而没有参数和结果的相关信息。
例如,如果服务器端发布了两个函数,一个是 'hello',另一个是 'MD5',那么函数列表应该以如下形式返回:
- Fa2{s5"hello"s3"MD5"}z
Hprose 远程过程调用(RPC)服务器支持发布缺失的函数/方法。这表示如果客户端调用一个服务器端不存在的函数/方法,它可以被一个统一的处理器所处理。如果服务器端设置有这样的一个统一的处理器。那么函数列表中需要有一个 '*' 项来表示它。
例如:
- Fa1{s1"*"}z
函数/方法名和缺失函数/方法统一处理器可以被一起发布。
例如:
- Fa3{s1"*"s5"hello"s3"MD5"}z
Hprose 客户端可以忽略该实现,但是服务器端必须实现它。
RPC 调用
RPC 调用由客户端发起,例如:
- Cs5"hello"a1{s5"world"}z # result = client.hello("world");
你可以传递 0 个或多个参数,例如:
- Cs3"sum"a3{012}z # result = client.sum(0, 1, 2);
如果没有参数,参数列表可以省略,例如:
- Cs9"deleteAll"z # client.deleteAll();
你可以进行引用参数传递,例如:
- Cs4"sort"a1{a10{2465318790}}tz # void Sort(ref int[] a); // defined in server
- # int[] a = new int[] {2, 4, 6, 5, 3, 1, 8, 7, 9, 0};
- # client.sort(ref a);
hprose 发布的函数/方法名是不区分大小写的。例如上面的例子中,服务器端定义的函数/方法名是 "Sort",但是在客户端,可以使用 'sort','Sort' 或 'SORT' 来调用它,这都没问题。
在标记 'C' 之后,标记 's' 表示函数/方法名,标记 'a' 表示参数列表,标记 't' 表示引用参数传递。不要用标记 'f' 表示值参数传递,因为这是默认行为,服务器端不需要处理 'f' 标记。标记 'z' 表示调用结束。
函数/方法名和参数列表是独立的对象序列化。因此它们具有独立的整数引用编号。
Hprose 支持批量调用,例如:
- Cs5"hello"a1{s5"world"}Cs3"sum"a3{012}z # client.beginBatch();
- # client.hello("world");
- # client.sum(0, 1, 2);
- # results = client.endBatch();
批量调用是 hprose 客户端的可选特征,客户端可以不必实现的。
RPC 应答
RPC 应答由服务器端返回,例如:
- string hello(string str) {
- return "Hello " + str + "!";
- }
- int sum(int a, int b, int c) {
- return a + b + c;
- }
- Cs5"hello"a1{s5"world"}z # 客户端调用
- Rs12"Hello world!"z # 服务器端应答
- Cs3"sum"a3{012}z # 客户端调用
- R3z # 服务器端应答
返回值可以是 hprose 支持的任意类型。
如果发布的函数/方法不具有返回值,则返回值为 Null。
如果是引用参数传递的话,在返回值后面将紧跟着返回参数列表,例如:
- void Sort(ref int[] a) {
- Array.Sort(a);
- }
- Cs4"sort"a1{a10{2465318790}}tz # 客户端调用
- RnAa1{a10{0123456789}}z # 服务器端应答
在标记 'R' 之后的是序列化的结果。在标记 'A' 之后的是序列化的参数列表。在应答的末尾是标记 'z'。
函数/方法的结果和参数列表也是独立的对象序列化,所以它们也是具有各自独立的整数引用编号。
在上面的例子中,服务器端函数的参数其实无需定义成 ref 参数。事实上,Array.Sort 可以直接发布。当然,这涉及到协议的实现,这里我们不过多的讨论它。
对于批量调用的应答,这里有个例子:
- Cs5"hello"a1{s5"world"}Cs3"sum"a3{012}z # 客户端批量调用
- Rs12"Hello world!"R3z # 服务器端应答
RPC 错误
如果在远程函数/方法执行过程中发生错误,hprose 服务器应该以如下格式返回错误信息:
- E<error_message>z
<error_message>
是序列化的错误信息字符串。例如:
- void errorExample() {
- throw new Exception("This is a error example.");
- }
- Cs12"errorExample"z # 客户端调用
- Es24"This is a error example."z # 服务器端应答
如果在批处理调用中发生错误,批处理调用可以被中断。例如:
- Cs5"hello"a1{s5"world"}Cs12"errorExample"Cs3"sum"a3{012}z # 客户端批处理调用
- Rs12"Hello world!"Es24"This is a error example."z # 服务器端应答
也可以不被中断,例如:
- Cs5"hello"a1{s5"world"}Cs12"errorExample"Cs3"sum"a3{012}z # 客户端批处理调用
- Rs12"Hello world!"Es24"This is a error example."R3z # 服务器端应答