linux c++ gsoap如何构建服务端、多个客户端设置命名空间以及MTOM协议传输文件

本文档介绍了如何使用C++的gSOAP库构建服务端和客户端,包括在Apache中集成gSOAP服务,设置命名空间隔离以避免编译错误,以及利用MTOM协议进行文件传输。详细步骤包括生成接口代码、实现服务端接口、构建客户端、设置命名空间以及MTOM传输文件的回调函数配置。同时,文中提供了官方文档链接和示例代码,可供开发者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

使用c++ gsoap构建服务端

使用c++ gsoap构建多个客户端并设置命名空间隔离

使用c++ gsoap的MTOM协议传输文件


gsoap是十多年前的协议框架了,挺老的,而且网上能搜到有用的资料不多,但是因为项目需要与第三方外部协议对接只能慢慢摸索,不过有个官方详细文档可以参考。

官方文档链接:https://www.genivia.com/doc/guide/html/index.html

gsoap下载地址:https://www.genivia.com/downloads.html

使用c++ gsoap构建服务端

我做的项目是把gsoap当作插件安装到apache服务中,也就是需要依赖apache httpd服务

首先需要服务接口的.wsdl接口文件,这个文件由客户提供。在这个例如为testA.wsdl文件目录下执行如下命令

生成头文件:
    wsdl2h -o testA.h  testA.wsdl
生成c++服务端接口代码:
    soapcpp2 -j -S testA.h
    -j:  生成的服务代理类和对象包含struct soap
    -S:  只生成服务器端代码
在该目录下会生成固定的.cpp、.h、.xml、.nsmap文件

其中soapxxxxxxxxxServiceSoapBindingService.h的头文件中,类的尾部有wsdl定义的接口虚函数,例如有一个testfun接口:

virtual int testfun(std::string *ns1__param_, std::string &ns1__paramResponse_) SOAP_PURE_VIRTUAL;
...

把接口拷贝出来到另一个.c文件中,例如testserver.c去实现这些类虚函数,这就是客户端调用的同步接口。例如:

#include <stdio.h>
#include <string.h>
#include "soapxxxxxxxxxServiceSoapBindingService.h"
#include "xxxxxxxServiceSoapBinding.nsmap"
//在gsoap源码目录:gsoap-2.8\gsoap\mod_gsoap\mod_gsoap-1.0\apache_20
#include "apache_gsoap.h"

IMPLEMENT_GSOAP_SERVER()
extern "C" int soap_serve(struct soap *soap)
{
  xxxxxxServiceSoapBindingService service(soap);
  int err = service.serve();
  service.destroy();
  return err;
}
//接口需要实现
int xxxxxxxxxServiceSoapBindingService::testfun(std::string *ns1__param_, std::string &ns1__funResponse_)
{
    //todo 
    //业务逻辑...
    //最后给ns1__funResponse_赋值结果,返回的时候客户端能收到
    return SOAP_OK;
}

因为编译是c的所以把所有cpp文件改为c后缀了。

从gsoap源码目录中找到公共代码stdsoap2.h和stdsoap2.c,拷贝到和接口同一个目录下。

编译生成testserver.so库:

apxs -a -c -S CC=g++ testserver.c soapC.c soapxxxxxServiceSoapBindingService.c stdsoap2.c

需要在运行环境httpd/conf.d/中增加配置项test.conf:

LoadModule gsoap_module       /usr/lib64/httpd/modules/mod_gsoap.so

<IfModule mod_gsoap.c>
    <Location /testfun>
          SetHandler gsoap_handler
         SOAPLibrary /usr/lib64/testserver.so
          Order allow,deny
          Allow from all
     </Location>
</IfModule>

testfun是接口名

mod_gsoap.so插件也需要拷贝到对应的环境目录

testserver.so是服务端接口库拷贝到对应的环境目录

接下来重启一下httpd服务,用postman调接口测试ok。

使用c++ gsoap构建多个客户端并设置命名空间隔离

客户端的例子在gsoap源码中附带了很多,而且官方文档也有详细说明。

构建一个客户端比较简单这里不再说明,因为开发的时候是多人合作开发,这里讲述多个wsdl生成的客户端代码如何合并成一个可执行文件,如何对c++客户端代码进行自定义命名空间隔离,在编译的时候不会报多重定义和未定义等错误。

官方文档链接:https://www.genivia.com/doc/guide/html/index.html  

所在目录:How to build a client or server in a C++ code namespace

首先需要客户端的.wsdl文件,这个文件由客户提供。

在这个例如为testB.wsdl文件,有一个getfun方法,目录下执行命令

生成头文件:
    wsdl2h -o testB.h testB.wsdl

然后手动修改testB.h,增加命名空间(使用-qname参数应该可以),例如如下:

namespace myclient {    //增加自定义namespace,包住头文件所有代码内容
#include <vector>
template <class T> class std::vector;

int ns1__getfun(
    std::string :_param,
    std::string :&:_getFunReturn
);
}
//end

接着生成c++服务端接口代码:
    soapcpp2  -C -L -n testB.h 
    -C:  只生成客户端代码
    -n:  生成的服务函数重命名soap_serve为name_serve并将生成的命名空间表(nsmap文件)
    -L:  不生成lib文件
在该目录下会生成固定的.cpp、.h、.xml、.nsmap文件

需要提取公共 SOAP Header 和 Fault 序列化程序 ,否则编译出现未定义错误。当前项目目录新建一个空env.h文件,执行:

soapcpp2 -penv env.h

生成的envC.cpp、envH.h、envStub.h是必要的文件,需要包含envH.h到客户端中,客户端伪代码如下:

#include "testBH.h"    
#include "envH.h"
#include "testB.nsmap"
//命名空间声明 第一步(重要)
using namespace myclient;
//服务端IP
#define SERVER "https://127.0.0.1/"

int main(int argc, char **argv)
{
    struct soap soap;
    soap_init1(&soap, SOAP_ENC_XML);    //初始化soap
    if (soap_ssl_client_context(&soap,
        SOAP_SSL_NO_AUTHENTICATION, //去掉ssl认证
        NULL,
        NULL,
        NULL,
        NULL,
        NULL
        ))
    {
        soap_print_fault(&soap, stderr);
        printf("client Soap init fail!!!\n");
        exit(EXIT_FAILURE);
    }
    std::string getfunReturn;
    //设置上下文的命名空间 第二步(重要)
    soap_set_namespaces(&soap, myclient_namespaces);    //myclient_namespaces定义在namap中
    //接口调用
    soap_call_ns1__getfun(&soap, SERVER "getfun", "", "test123", getfunReturn);
    if (!soap.error)
    {
        printf("result = %s\n", getfunReturn.c_str());
    }

    //释放销毁soap
    soap_destroy(&soap);
    soap_end(&soap);
    soap_done(&soap);
    return 0;
}

其他客户端也可以这么生成,然后包含进来使用。

编译命令需要加上-DWITH_NONAMESPACES:

g++ -g -o myclient main.cpp testBClient.cpp testBC.cpp envC.cpp stdsoap2.cpp -DWITH_NONAMESPACES -lssl -lcrypto -DWITH_OPENSSL

使用c++ gsoap的MTOM协议传输文件

源码请参考:

(1)gsoap源码包里的dmeo:\gsoap_2.8.122\gsoap-2.8\gsoap\samples\mtom-stream\

(2)官方文档链接:https://www.genivia.com/doc/guide/html/index.html#MTOM        

         所在目录:MTOM attachments

建议可以直接编译测试源码附带的mtom-stream例子,然后抓包测试下发送文件是怎么样的过程。

代码里它的方法是使用XOP Include 元素xop:Include在接口头文件中定义为_xop__Include结构或类,然后把文件发送和接收的回调注册到soap,这样在调接口时发送和接收 MTOM XOP 附件的过程是完全自动化的。

  //服务端侧
  soap.fmimereadopen = mime_read_open;                //这三个read是注册发送文件回调
  soap.fmimereadclose = mime_read_close;
  soap.fmimeread = mime_read;
  soap.fmimewriteopen = mime_server_write_open;        //这三个write是注册接收文件回调
  soap.fmimewriteclose = mime_server_write_close;    
  soap.fmimewrite = mime_server_write;
  //客户端侧
  soap.fmimereadopen = mime_read_open;                //这三个read是注册发送文件回调
  soap.fmimereadclose = mime_read_close;
  soap.fmimeread = mime_read;
  soap.fmimewriteopen = mime_client_write_open;        //这三个write是注册接收文件回调
  soap.fmimewriteclose = mime_client_write_close;
  soap.fmimewrite = mime_client_write;

soap初始化需要设置SOAP_ENC_MTOM参数,在调接口的时候,需要用户指定打开哪一个文件去发送,也就是需要给xop__Include赋值文件信息等。

但是客户给的wsdl里面接口没有定义 xop__Include没有办法赋值,也就触发不了传输文件的回调,这个时候发送端如何触发发送回调给接收端发文件呢?

这个问题也搞了很久,直到看到官方文档的这两函数soap_set_mime、oap_set_mime_attachment,需要在发送端的接口中手动启动和添加附件!例如:

//初始化并启用 MIME
soap_set_mime(soap, NULL, "test"); 
//添加附件,len是文件长度必填,发送哪个文件可以在soap->user中保存,然后在mime_read_open回调中去打开
if (soap_set_mime_attachment(soap, NULL, len, SOAP_MIME_BINARY , "*/*" , "" , NULL, NULL))
{
    soap_clr_mime(soap); 
    printf("soap_set_mime_attachment error!\n");
    return soap->error;
} 

个人笔记,仅供学习,如有错漏欢迎指正~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值