XML-RPC 实现C++和C#交互
我们通常会面对这样的问题:整合不同平台或不同类库,这些类库可能来自不同的语言,甚至不同的操作系统。 如何解决这类棘手的问题呢?
一.方案介绍
解决不同语言交互的方法有不少,对我了解的windows系统和.NET平台,有以下几种做法:
- P/Invoke: 调用native cpp的方法,处在同一个内存区间,访问方便,但包装困难,可能抛出运行时异常。
- 读写文件:通过一头读文件,一头写文件的方式实现交互。诸位别笑,本科时候我就用过这种方式解决问题。
- 命名管道/socket: 通过字节数组的方式实现交互,命名管道是windows系统提供的功能,可提供安全快捷的程序间交互。socket不依赖于操作系统,只要给定包格式,在任何支持socket的语言平台下都能支持。但缺点也很明显,如果交互复杂,那么解析这种byte[]数组将会非常复杂而且难以维护。
- RPC: 又称之为远程过程调用,也是我们今天的主角。
数据即程序,RPC说白了依旧是传递数据的过程,只是过程在代码上更像函数调用。如下图:
目前主流的RPC有两种: XML和JSON。 XML是曾经的主角,兼容性更好。但如今移动互联网要求数据流量要小,而XML的缺点也随之暴露出来,JSON由于节省数据(大大减少了包头和标记的开销),如今变得更受欢迎。新浪微博API,如今全部升级为JSON了。 RPC的实质是http协议,它封装了底层实现的细节,能让我们将注意力放在应用逻辑的实现,而非建立连接这样的问题。
RPC的优点很多,其中我最喜欢的是它的容器,声明一个Array,里面可以塞任何你想要的数据,int,string,double,struct甚至另外一个array都可以。当然,不能传递抽象类或接口,毕竟不是同一内存区域。
本文我们只介绍XML-RPC实现C++和C#两个应用程序之间的交互。JSON的C#版本Jayrock对RPC的支持,尤其是对非ASP.NET环境几乎没有,连一篇像样的文档都找不到,所以我们仅仅讨论XML-RPC。
二. 方案实现
我们打算将C#作为客户端,C++作为服务器端。
1. C++的服务器实现
我们在VS2010中新建C++工程,将附件中的XMLRPC.LIB静态库拷入当前工程文件夹,设置当前工程为release模式。同时在C++工程设置中,添加两个lib引用: xmlrpc.lib, ws2_32.lib
之所以用release模式,是因为在debug模式下xmlrpc.lib库会出现如下的编译错误:花了很久时间都没解决,如果有大神能帮助解决这个问题,请一定留言.
1> All outputs are up-to-date.
1>XmlRpc.lib(XmlRpcServer.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcUtil.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcServerMethod.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcValue.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcDispatch.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcServerConnection.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcSocket.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
1>XmlRpc.lib(XmlRpcSource.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '0' doesn't match value '2' in HelloServer.obj
再将对应的库头文件加入到项目中,完成后如下图所示:
现在C#程序员普遍已经忘掉了怎么编译和使用C++静态库,我也是其中之一,如果你在上面几步遇到困难,不妨查看相关资料。
接下来,我们将开始编写工作代码:
首先是添加头文件引用,一般只要引用XMLRPC.H和XMLRPCVALUE.H即可, 同时声明一个全局的XmlRpcServer 服务。(实在太不习惯C++中不new就能创建实例的语法了)
#include "RPC/XmlRpc.h"
#include "RPC/XmlRpcValue.h"
#include <iostream>
#include <stdlib.h> using namespace XmlRpc; // The server
XmlRpcServer s;
添加主函数:
int main()
{
/*if (argc != 2) {
std::cerr << "Usage: HelloServer serverPort\n";
return -1;
}*/
//int port = atoi(argv[1]);
int port=2567;
XmlRpc::setVerbosity(3); // Create the server socket on the specified port
s.bindAndListen(port); // Enable introspection
s.enableIntrospection(true); // Wait for requests indefinitely
s.work(-1.0); return 0;
}
- 设置端口,绑定端口和启动.这些都没什么好说的。值得注意的是调试等级,setVerbosity。等级越高,输出信息越详细,最高等级可输出完整的xml交互文件供调试,但会严重拖累系统速度的。在你测试功能完毕后,不妨将其设为0。
接下来我们创建几个类,来实现服务端功能:
// A variable number of arguments are passed, all doubles, result is their sum.
class Sum : public XmlRpcServerMethod
{
public:
Sum(XmlRpcServer* s) : XmlRpcServerMethod("Sum", s) {} void execute(XmlRpcValue& params, XmlRpcValue& result)
{
int nArgs = params.size();
double sum = 0.0;
for (int i=0; i<nArgs; ++i)
sum += double(params[i]);
result = sum;
}
} sum(&s);
所有功能都以继承于XmlRpcServerMethod类,同时改写其execute函数。 有点意思的是,这个XmlRPCValue数据类型,是类似C#的Dictionary,或JAVA的hashset。 你可以通过类似C#索引器(字典)的方式,添加或读取该结构中的内容。比如上面的params[i]。由于代码简单,就不多做详细解释。
类后跟了一个实例,sum(&s),这样就在服务器中注册了该功能。
再创建一个类,来实现字符串操作:
// One argument is passed, result is "Hello, " + arg.
class HelloName : public XmlRpcServerMethod
{
public:
HelloName(XmlRpcServer* s) : XmlRpcServerMethod("HelloName", s) {} void execute(XmlRpcValue& params, XmlRpcValue& result)
{
std::string resultString = "Hello, ";
resultString += std::string(params[0]);
result = resultString;
}
} helloName(&s);
也不多做解释了。
我们再看一下,怎么存取RPC中的字典和数组,这才是精髓部分:
class StructData : public XmlRpcServerMethod
{
public:
StructData(XmlRpcServer* s) : XmlRpcServerMethod("GetStruct", s) {} void execute(XmlRpcValue& params, XmlRpcValue& result)
{ XmlRpcValue A;
A.setSize(2); A[0]["a"]=123;
A[0]["b"]=456;
A[1]["a"]=43;
A[1]["b"]=425; result["a"]=A;
result["b"]=123;
}
} structData(&s);
设置字典时,是不需要指定其size的,但若设定的是数组,则必须使用setsize方法设定其大小。字典的值也可包含另外一个XmlRpcValue 结构体。
RPC中可以很好的处理字符串,int,double, datetime类型,但枚举类型的支持并不好,我建议直接传int.
完成了这三个服务后,我们来编写C#的客户端。
2. C# RPC客户端实现:
新建一个C#工程,创建以下的接口类:
public interface IRPCMethod
{
[XmlRpcMethod("HelloName")]
string HelloName(string Name); [XmlRpcMethod("Sum")]
double Sum(double a,double b); [XmlRpcMethod("GetStruct")]
XmlRpcStruct GetStruct();
}
当然,要引用CookComputing.XmlRpc库。 使用接口类的作用是剥离RPC对系统的影响,让系统可以“透明的”调用RPC代码。值得注意的是,名称必须与C++中的名称一致,否则会出现找不到方法的异常。
我们注册客户端服务:
var chnl = new HttpChannel(null, new XmlRpcClientFormatterSinkProvider(), null);
ChannelServices.RegisterChannel(chnl, false); var svr = (IRPCMethod)Activator.GetObject(typeof(IRPCMethod), "http://localhost:2567/");
Console.WriteLine("成功注册信道");
string ret = svr.HelloName("haha");
Console.WriteLine("调用helloName方法:" + ret);
double result = svr.Sum(23, 18);
Console.WriteLine("调用Sum方法:" + result.ToString()); XmlRpcStruct result2 = svr.GetStruct();
至于result2结构体,你可以通过调试来查看具体的运行结果。
在服务器端,可以看到调用所花费的流量和方法名称。
三. 其他
这里我们关注一些额外的问题:
1.流量
RPC的一种场景是本地不同程序调用,这种情况下速度很快。但在跨机器或是移动设备上,就必须考虑流量因素了。XML的“性价比”并不高:
<?xml version="1.0"?>
<methodCall>
<methodName>echo</methodName>
<params>
<param><value><string>Hello Chris</string></value></param>
<param><value><i4>123</i4></value></param>
</params>
</methodCall>
实际的有用数据,仅占所有字节数的5%,甚至更少,除非是大批量的传输本文数据。如果是流量敏感,推荐使用JSON.
2. 性能
我们当然要关心,RPC在本机调用会有多快?和哪些因素敏感? 笔者配置是i7 2600K, 8GB DDR, Gbps网络适配器,VS的debug模式。在执行Sum操作时,一千次耗时4.3ms。 在执行更复杂的结构体传递(大概有20个double,三个string,两个int时), 千次耗时5.8ms。
因此,可以得知,XML的转换和解析几乎不耗时,建立连接后,执行一次在ms量级,对数据结构复杂程度不敏感,因此,若是实时性敏感应用,建议一次性多传些数据。
3. 兼容性
笔者发现,RPC的兼容性并不太好,在和JAVA采用RPC交互时,就遇到了困难,”XML解析异常”。JSON的的兼容性不见得比XML更好,实际操作更是问题多多。涉及RPC的社区,普遍文档较少,例子不全,出现问题也不好排查,更没有太多跨语言的RPC实例。因此,如果你能用P/Invoke, 还是推荐用直接调用的做法。 RPC肯定还是比byte流的socket方便很多。
四 .总结和源代码下载
RPC将原本复杂的数据传输问题简化了,使我们从复杂的数据包结构,JAVA和C的double编码和socket传输中脱离出来,提供了更简单方便的方案。但必须看到,它并不完善,我们只能一步步的探索。
另外想问一句,Unity3D的RPC是何种格式?
有任何问题,欢迎随时交流。
源代码下载:
http://files.cnblogs.com/buptzym/XML_RPC.rar
XML-RPC 实现C++和C#交互的更多相关文章
- 什么是XML RPC?
# -*- coding: cp936 -*- #python 27 #xiaodeng #什么是XML RPC? #中文叫:远程过程调用 #使用http协议做传输协议的rpc机制,使用xml文本的方 ...
- The type javax.xml.rpc.ServiceException cannot be resolved.It is indirectly
The type javax.xml.rpc.ServiceException cannot be resolved.It is indirectly 博客分类: 解决方案_Java 问题描述:T ...
- xml rpc SimpleXMLRPCServer [python]
SimpleXMLRPCServe 其实里面xml的概念不是很强,主要是rpc !不用关心什么xml . rpc 是就是远程调用,把函数什么的放到远程服务器上,本地调用就行了.用 SimpleXMLR ...
- supervisord支持扩展(xml RPC API & Third Party Applications and Libraries)
XML-RPC API Documentation http://www.supervisord.org/api.html Third Party Applications and Libraries ...
- ActiveMQ与xml rpc
最近项目在做平台间的消息传递,也让我对平台间消息的传递进行了深一步的探讨.先叙述一下概况 公司上一个版本用的是winform做的监控软件,主要做设备的通信和控制,基本的连接如下
- 解决java.lang.NoClassDefFoundError: javax/xml/rpc/service错误的方法
最近在做WebService项目,本地测试没有问题,打算部署到服务器上,但是部署后,访问时出现了如下图1的错误: 图1 图1报的是没有找到定义的类的错误.刷新页面有又出现了另外“新”的错误: 图2 根 ...
- JAVA 程序生成jar包运行报错 Exception in thread "Thread-1" java.lang.NoClassDefFoundError: javax/xml/rpc 的解决方法
最近开发支付宝生活缴费的项目,java程序要使用.NET 的WebService服务,后来正式部署出现这错误,网上查资料是少了一个“jaxrpc.jar”文件,但是我本地调试正常,最后是删除我目前导出 ...
- 通过GWT RPC实现客户端与服务器端的交互
GWT有两种与服务端进行交互的技术:GWT RPC和基于普通AJAX的远程交互技术.这里只介绍GWT RPC技术. 一个完整的RPC服务至少包含三个Java文件: 1.客户端远程接口类,需要继承Rem ...
- JQuery + XML作为前后台数据交换格式实践
JQuery + xml作为前后台数据交换 JQuery提供良好的异步加载接口AJAX,可以局部更新页面数据, http://api.jquery.com/category/ajax/ xml作为一种 ...
- RPC数据通信
RPC全称为Remote Procedure Call,翻译过来为“远程过程调用”.目前,主流的平台中都支持各种远程调用技术,以满足分布式系统架构中不同的系统之间的远程通信和相互调用.远程调用的应用场 ...
随机推荐
- linux命令(21):more命令
实例1:显示文件中从第3行起的内容 [root@host-172-168-80-55 home]# cat test.log aaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb ...
- 基于 Docker 的 DevOps 搭建
一直对 DevOps 感兴趣,最近心血来潮,搭乘 Docker 搞一搞,Let‘s Go !!! 1.拉取 GitLab docker pull gitlab/gitlab-ce 2.Run Git ...
- 关于Logstash中grok插件的正则表达式例子
一.前言 近期需要对Nginx产生的日志进行采集,问了下度娘,业内最著名的解决方案非ELK(Elasticsearch, Logstash, Kibana)莫属. Logstash负责采集日志,Ela ...
- centos系统服务管理
系统服务管理工具: chkconfig(所有linux发行版都有),用法很简单,如下: usage: chkconfig --list [name] chkconfig --ad ...
- Vue生命周期总结
查看生命周期图 <!DOCTYPE html> <html> <head> <title></title> <meta http-eq ...
- 【ASP.NET MVC】 路由机制:命名路由
首先看一下命名路由和没有命名的差别: 命名路由: routes.MapRoute( name: "Test", // Route name url: "code/p/{a ...
- [thinkphp] 隐藏后台地址
转自 http://document.thinkphp.cn/manual_3_2.html#module_deploy 如果不希望用户直接访问某个模块,可以设置模块映射(对后台的保护会比较实用). ...
- HashMap底层数据结构和算法解析
1.Hash Map的数据结构? A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点.当链表长度超过8时,链表转换为红黑树. transient Node<K,V>[] ta ...
- 洛谷——P1165 日志分析
P1165 日志分析 题目描述 M 海运公司最近要对旗下仓库的货物进出情况进行统计.目前他们所拥有的唯一记录就是一个记录集装箱进出情况的日志.该日志记录了两类操作:第一类操作为集装箱入库操作,以及该次 ...
- Linux命令之chgrp
chgrp [选项] … GROUP FILE … chgrp [选项] … --reference=RFILE FILE … chgrp命令是用来改变文件的组所有权.将改变每一个FILE的所属组为G ...