解决不同语言交互的方法有不少,对我了解的windows系统和.NET平台,有以下几种做法:

  • P/Invoke: 调用native cpp的方法,处在同一个内存区间,访问方便,但包装困难,可能抛出运行时异常。
  • 读写文件:通过一头读文件,一头写文件的方式实现交互。诸位别笑,本科时候我就用过这种方式解决问题。
  • 命名管道/socket: 通过字节数组的方式实现交互,命名管道是windows系统提供的功能,可提供安全快捷的程序间交互。socket不依赖于操作系统,只要给定包格式,在任何支持socket的语言平台下都能支持。但缺点也很明显,如果交互复杂,那么解析这种byte[]数组将会非常复杂而且难以维护。
  • RPC: 又称之为远程过程调用,也是我们今天的主角。

      数据即程序,RPC说白了依旧是传递数据的过程,只是过程在代码上更像函数调用。如下图:

image

      目前主流的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

复制代码

image1

再将对应的库头文件加入到项目中,完成后如下图所示:

image

现在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;
}

复制代码

    1. 设置端口,绑定端口和启动.这些都没什么好说的。值得注意的是调试等级,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:
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐