微软RPC官方教程
http://msdn.microsoft.com/en-us/library/windows/desktop/aa379010(v=vs.85).aspx
注意:原文版本较老,我更新和改变了部分内容。并提供了完整的程序。编译环境SDK 7.0 WinXP VS2010。
RPC官方教程
此手册可使你从已经存在的单独程序,一步步地创建简单的、单客户端、单服务端的分布式程序。步骤如下:
- 设计接口和创建程序配制文件。
- 使用MIDL编译器来创建C语言客户端桩、服务端桩及相关头文件。
- 写客户端程序。它负责管理链接服务端。
- 写一个包含远程过程的服务端。
- 编译并链接RPC动态库来运行这些分布式程序。
客户端程序通过远程过程调用传递一个字符串给服务端,服务端输出字符串“Hello,World”到命令行控制台中。完成的源码在SDK/RPC/Hello目录中。
此文内容分类如下:
- 单独程序
- 定义接口
- 生成UUID
- IDL文件
- ACF文件
- 生成Stub文件
- 客户端程序
- 服务端程序
单独程序
此脱单独程序由一个函数组成。此程序是我们的分布式程序的样板。函数HelloProc定义在单独的源文件中,这个它可以单独程序或分布式程序中复用它。
- /* file hellop.c */
- #include <stdio.h>
- #include <windows.h>
- void HelloProc(char * pszString)
- {
- printf("%s\n", pszString);
- }
- /* file: hello.c, a stand-alone application */
- #include "hellop.c"
- void main(void)
- {
- char * pszString = "Hello, World";
- HelloProc(pszString);
- }
定义接口
接口定义一个标准规范,定义了客户端与服务端是如何通信的。此接口定义了如何识别对方,客户端程序如何远程调用及如何处理远程调用的参数和返回值。它也指定了数据的传输方式。你以MIDL(Microsoft Interface Definition Language)方式定义的接口包含了C风格的参数,并使用关键字定义这些参数的属性。这些属性描述了数据如何在网络上传送。接口定义(IDL)文件包含了类型定义,属性定义和函数原型定义,这些用于描述数据如何在网络中传输。ACF文件包含配置程序的属性,但不影响网络。
生成UUID
第一步是使用uuidgen工作生成一个UUID。使用UUID客户端和服务端就可以彼此辨别。Uuidgen工具(uuidgen.exe)在安装SDK时就已经自动安装了。
下面的命令创建了一个UUID和Hello.idl临时文件。
uuidgen /i /ohello.idl
你的hello.idl文件内容可能是这样(当然UUID会不同)
- [
- uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
- version(1.0)
- ]
- interface INTERFACENAME
- {
- }
DIL文件
IDL文件由一个或多个接口定义组成,每一个接口定义都有一个接口头和一个接口体。接口头包含了使用此接口的信息,如UUID。这此信息封装一对中括号之内。之后是interface关键和接口名。接口体包含了C风格的数据类型和函数原型和带属性的参数。这此参数的属性描述了数据如何在网络上传送。此例中,定义头中包含了UUID和版本号。版本号作兼容之用。客户端、服务端的版本只有兼容了,才可以连接。接口体包含了HelloProc函数原型。函数的参数pszString有两个属性[in]和[string]。[in]函数告诉运行库此参数只能从客户端传送到服务端。[string]属性指定了桩要按C风格的字符串来处理此参数。客户端程序可以关闭服务端程序,所以此接口包含了另一个函数Shutdown。
- //file hello.idl
- [
- uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
- version(1.0)
- ]
- interface hello
- {
- void HelloProc([in, string] unsigned char * pszString);
- void Shutdown(void);
- }
ACF文件
ACF文件使你自定义你的客户端或服务端的RPC接口,但不影响网络特征。例如,如果你的客户端程序包含了一个复杂的数据结构,此数据结构只在本地机上有意义,那么你就可以在ACF文件中指定如何描述独立与机器的数据结构,使用数据结构用于远程过程调用。此教程演示了一个ACF文件 ---指定一个绑定的handle类型,用来代表客户端与服务端的连接。[implicit_handle]属性允许客户端程序为它的远程过程调用选择一个服务端。ACF定义了此句柄为handle_t类型(MIDL基本数据类型)。MIDL编译器将绑定ACF文件指定的句柄名字hello_IfHandle,放在生成的头文件中。注意此ACF文件的体为空。
- //file: hello.acf
- [
- implicit_handle (handle_t hello_IfHandle)
- ]
- interface hello
- {
- }
MIDL编译器有一个选项 /app_config。此选项可使你包含某一个ACF属性,如implicit_handle。在IDL文件中,而不是创建一个ACF文件。如果你的程序不需要指定大量配置和不考虑OSF兼容,则可以考虑使用此选项。
生成Stub文件
在定义了客户端/服务端接口之后,你将编写客户端和服务端源码。之后使用一个makefile文件生成桩和头文件。再编译链接客户端/服务端程序。 但是,如果你第一次探究分布式计算环境。你可能想调用MIDL编译器,来查看MIDL产生了什么文件。 MIDL编译器(Midl.exe)在安装SDK时已自动安装。当你编译这些文件时,确保Hello.idl 和 Hello.acf在同一文件夹中。下面的命令将生成Hello.h头文件和客户端、服务端桩: Hello_c.c and Hello_s.c.
midl hello.idl
你将在服务端程序中提供这两个函数。如果数据从服务端传送到客户端(要使用一个[out]参数),你也要在客户端提供两个内存管理函数。全局变量,hello_IfHandle,和客户端、服务端接口名字:hello_v1_0_c_ifspec 和 hello_v1_0_s_ifspec. 客户端和服务端程序将在运行时使用这些接口句柄。你仅使用桩文件Hello_c.c and hello_s.c就可以了。
- /* this ALWAYS GENERATED file contains the definitions for the interfaces */
- /* File created by MIDL compiler version 7.00.0555 */
- /* at Sun Dec 01 09:32:11 2013
- */
- /* Compiler settings for hello.idl:
- Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555
- protocol : dce , ms_ext, c_ext, robust
- error checks: allocation ref bounds_check enum stub_data
- VC __declspec() decoration level:
- __declspec(uuid()), __declspec(selectany), __declspec(novtable)
- DECLSPEC_UUID(), MIDL_INTERFACE()
- */
- /* @@MIDL_FILE_HEADING( ) */
- #pragma warning( disable: 4049 ) /* more than 64k source lines */
- /* verify that the <rpcndr.h> version is high enough to compile this file*/
- #ifndef __REQUIRED_RPCNDR_H_VERSION__
- #define __REQUIRED_RPCNDR_H_VERSION__ 475
- #endif
- #include "rpc.h"
- #include "rpcndr.h"
- #ifndef __RPCNDR_H_VERSION__
- #error this stub requires an updated version of <rpcndr.h>
- #endif // __RPCNDR_H_VERSION__
- #ifndef __hello_h__
- #define __hello_h__
- #if defined(_MSC_VER) && (_MSC_VER >= 1020)
- #pragma once
- #endif
- /* Forward Declarations */
- #ifdef __cplusplus
- extern "C"{
- #endif
- #ifndef __hello_INTERFACE_DEFINED__
- #define __hello_INTERFACE_DEFINED__
- /* interface hello */
- /* [implicit_handle][version][uuid] */
- void HelloProc(
- /* [string][in] */ unsigned char *pszString);
- void Shutdown( void);
- extern handle_t hello_IfHandle;
- extern RPC_IF_HANDLE hello_v1_0_c_ifspec;
- extern RPC_IF_HANDLE hello_v1_0_s_ifspec;
- #endif /* __hello_INTERFACE_DEFINED__ */
- /* Additional Prototypes for ALL interfaces */
- /* end of Additional Prototypes */
- #ifdef __cplusplus
- }
- #endif
- #endif
客户端程序
此例在SDK下RPC目录下Hello目录中。 Hello.c源文件包含了MIDL生成头文件Hello.h。在Hello.h中包含了Rpc.h和rpcndr.h,还有HelloProc和Shutdown,及客户端、服务端使用的数据类型。在此例中必须使用MIDL。.因为此客户端管理与服务端的连接。客户端程序调用运行时函数来建立一个指向服务端的句柄,在远程过程调用完成后,释放这个句柄。函数RpcStringBindingCompose组合此函数的参数生成一个以字符串表示的绑定句柄(binding handle)并为此字符串分配内存。
函数RpcBindingFromStringBinding从字符串表示的绑定句柄,创建了一个服务绑定句柄,hello_ClientIfHandle。调用函数RpcStringBindingCompose,它的参数并不是UUID。因为此教程假定只有一个接口”hello”的实现。另外,此调用没有指定网络地址。因为此程序使用默认机制:本地调用机制。Protocol sequence是一个字符串,表示了具体网络传送。
Endpoint指定了具体的protocol sequence名称。此例使用了命名管道为网络传输方式,所以protocol sequence为”ncacn_np”。Endpoint名字为”\pipe\hello”。
远程过程调用:HelloProc和Shutdown,包含在RPC的异常处理函数中。RPC的异常处理函数是一系列宏,可使程序发生异常时,在代码外处理异常。如果RPC运行模块报告了一个异常,程序就会跳转到RpcExcept代码块中。
在此代码块中,你可以插入代码,来做一些清理工作,然后退出程序。.此例中程序仅简单地知道用户发生了一个异常。如果你不想使用异常,你可以使用ACF属性comm_status和fault_status来报告错误。在远程过程调用完成后,客户端程序首先调用RpcStringFree来释放先前字符串绑定申请的内存。
- /* file: helloc.c */
- #include <stdlib.h>
- #include <stdio.h>
- #include <ctype.h>
- #include "hello.h"
- #include <windows.h>
- void main()
- {
- RPC_STATUS status;
- unsigned char * pszUuid = NULL;
- unsigned char * pszProtocolSequence = "ncacn_np";
- unsigned char * pszNetworkAddress = NULL;
- unsigned char * pszEndpoint = "\\pipe\\hello";
- unsigned char * pszOptions = NULL;
- unsigned char * pszStringBinding = NULL;
- unsigned char * pszString = "hello, server";
- unsigned long ulCode;
- status = RpcStringBindingCompose(pszUuid,
- pszProtocolSequence,
- pszNetworkAddress,
- pszEndpoint,
- pszOptions,
- &pszStringBinding);
- if (status) exit(status);
- //status = RpcBindingFromStringBinding(pszStringBinding, &hello_ClientIfHandle);
- status = RpcBindingFromStringBinding(pszStringBinding, &hello_IfHandle);
- if (status) exit(status);
- RpcTryExcept
- {
- HelloProc(pszString);
- Shutdown();
- }
- RpcExcept(1)
- {
- ulCode = RpcExceptionCode();
- printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
- }
- RpcEndExcept
- status = RpcStringFree(&pszStringBinding);
- if (status) exit(status);
- status = RpcBindingFree(&hello_IfHandle);
- if (status) exit(status);
- exit(0);
- }
- /******************************************************/
- /* MIDL allocate and free */
- /******************************************************/
- void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
- {
- return(malloc(len));
- }
- void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
- {
- free(ptr);
- }
服务端程序
此例中必须 使用MIDL。根据你自己的编码风格,你可以一个或多个独立的文件中实现这些远程过程。此教程的程序,Hellos.c源文件包含了主要的服务端框架代码。Hellop.c包含了远程过程。将远程过程单独放在一个单独的文件中有一个好处:在转换成分布程序之前,可以链接它到非分布式程序上调试。在非分布式程序上调通之后,你可以编译链接此文件到分布式服务端。Hello.h头文件为客户端源文件,服务端源码也要包含此头文件。服务端调用RPC运行库函数RpcServerUseProtseqEp和RpcServerRegisterIf来建立有效的客户端。此例中,程序向函数RpcServerRegisterIf传递接口句柄。另一个参数为NULL。之后,服务端调用函数RpcServerListen来等待客户端的请求。
服务端必须包含两个内存分配函数。这两个函数被服务桩程序调用:midl_user_allocate和midl_user_free。这两个函数在服务端分配和释放内存,当远程过程传送参数给服务端时。此例中,midl_user_allocate和midl_user_free简单地封装了C库函数malloc和free。(注意,在MIDL编译器生成的前置声明中,”MIDL”是大小的。头文件Rpcndr.h定义了midl_user_free和midl_user_allocate。这两个函数指向MIDL_user_free 和MIDL_user_allocate。
- /* file: hellos.c */
- #include <stdlib.h>
- #include <stdio.h>
- #include <ctype.h>
- #include "hello.h"
- #include <windows.h>
- handle_t hello_IfHandle;
- void main()
- {
- RPC_STATUS status;
- unsigned char * pszProtocolSequence = "ncacn_np";
- unsigned char * pszSecurity = NULL;
- unsigned char * pszEndpoint = "\\pipe\\hello";
- unsigned int cMinCalls = 1;
- unsigned int fDontWait = FALSE;
- status = RpcServerUseProtseqEp(pszProtocolSequence,
- RPC_C_LISTEN_MAX_CALLS_DEFAULT,
- pszEndpoint,
- pszSecurity);
- if (status) exit(status);
- //RPC_S_INVALID_ENDPOINT_FORMAT
- //status = RpcServerRegisterIf(hello_ServerIfHandle, NULL, NULL);
- //status = RpcServerRegisterIf(hello_IfHandle, NULL, NULL);
- status = RpcServerRegisterIf(hello_v1_0_s_ifspec, NULL, NULL);
- if (status) exit(status);
- status = RpcServerListen(cMinCalls,
- RPC_C_LISTEN_MAX_CALLS_DEFAULT,
- fDontWait);
- if (status) exit(status);
- }
- /******************************************************/
- /* MIDL allocate and free */
- /******************************************************/
- void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
- {
- return(malloc(len));
- }
- void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
- {
- free(ptr);
- }
- e(ptr);
- }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
参考
- RpcStringBindingCompose function
- The RpcStringBindingCompose function creates a string binding handle.
- Syntax
- C++
- RPC_STATUS RPC_ENTRY RpcStringBindingCompose(
- TCHAR *ObjUuid,
- TCHAR *ProtSeq,
- TCHAR *NetworkAddr,
- TCHAR *EndPoint,
- TCHAR *Options,
- TCHAR **StringBinding
- );
String Binding
The string binding is an unsigned character string composed of strings that represent the binding object UUID, the RPC protocol sequence, the network address, and the endpoint and endpoint options.
ObjectUUID@ProtocolSequence:NetworkAddress[Endpoint,Option]
微软RPC官方教程的更多相关文章
- Windows XP SP3 Professional 微软(MSDN)官方原版系统
Windows XP SP3 Professional 微软(MSDN)官方原版系统 Windows XP(版本号:5.1,开发代号:Whistler)是微软公司推出供个人电脑使用的操作系统,其RTM ...
- MVC4学习之官方教程中迁移版本库报错
因工作需要,学习MVC4,但是微软官方教程中迁移版本库步骤在本地测试报错 官方教程地址:http://www.asp.net/mvc/overview/older-versions/getting-s ...
- Unity性能优化(3)-官方教程Optimizing garbage collection in Unity games翻译
本文是Unity官方教程,性能优化系列的第三篇<Optimizing garbage collection in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...
- Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译
本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...
- Unity性能优化(2)-官方教程Diagnosing performance problems using the Profiler window翻译
本文是Unity官方教程,性能优化系列的第二篇<Diagnosing performance problems using the Profiler window>的简单翻译. 相关文章: ...
- Unity性能优化(1)-官方教程The Profiler window翻译
本文是Unity官方教程,性能优化系列的第一篇<The Profiler window>的简单翻译. 相关文章: Unity性能优化(1)-官方教程The Profiler window翻 ...
- jeecg表单页面控件权限设置(请先看官方教程,如果能看懂就不用看这里了)
只是把看了官方教程后,觉得不清楚地方补充说明一下: 1. 2. 3. 4.用"jeecgDemoController.do?addorupdate"这个路径测试,不出意外现在应该可 ...
- [转]Google Guava官方教程(中文版)
Google Guava官方教程(中文版) http://ifeve.com/google-guava/
- Google Guava官方教程(中文版)
Google Guava官方教程(中文版) 原文链接 译文链接 译者: 沈义扬,罗立树,何一昕,武祖 校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...
随机推荐
- fedora28 安装automake
yum install automake yum install hg //版本管理
- Java ----单个list 删除元素
转载:https://www.cnblogs.com/lostyears/p/8809336.html 方式一:使用Iterator的remove()方法 public class Test { pu ...
- luoguP3799 妖梦拼木棒 [组合数学]
题目背景 上道题中,妖梦斩了一地的木棒,现在她想要将木棒拼起来. 题目描述 有n根木棒,现在从中选4根,想要组成一个正三角形,问有几种选法? 输入输出格式 输入格式: 第一行一个整数n 第二行n个整数 ...
- Feign远程调用源码阅读
- NX二次开发-UFUN和NXOpen结合开发中Tag_t对象与TaggedObject对象转换方法
本文通过举四个例子来告诉大家在NX二次开发过程中会经常用到UFUN和NXOpen结合去开发,在UFUN中我们得到的是Tag_t对象,在NXOpen中得到的是TaggedObject对象,这两个是需要进 ...
- Python 爬虫-抓取小说《鬼吹灯之精绝古城》
想看小说<鬼吹灯之精绝古城>,可是网页版的好多广告,还要一页一页的翻,还无法复制,于是写了个小爬虫,保存到word里慢慢看. 代码如下: """ 爬取< ...
- Java-Class-C:org.springframework.http.HttpHeaders
ylbtech-Java-Class-C:org.springframework.http.HttpHeaders 1.返回顶部 1.1. import org.springframework.htt ...
- Java 网络编程(1):使用 NetworkInterface 获得本机在局域网内的 IP 地址
原文地址:https://segmentfault.com/a/1190000007462741 1.问题提出 在使用 Java 开发网络程序时,有时候我们需要知道本机在局域网中的 IP 地址.很常见 ...
- AtCoder ABC 127F Absolute Minima
题目链接:https://atcoder.jp/contests/abc127/tasks/abc127_f 题目大意 初始状态下$f(x) = 0$,现在有 2 种模式的询问,第一种以“1 a b” ...
- python 19 lambda函数
转自http://www.cnblogs.com/BeginMan/p/3178103.html 一.lambda函数 1.lambda函数基础: lambda函数也叫匿名函数,即,函数没有具体的名称 ...