C++中gSOAP的使用详解

目录
  • SOAP简介
  • gSOAP
    • 准备工作
    • 头文件
    • 构建客户端应用程序
      • 生成soap源码
      • 建立客户端项目
    • 构建服务端应用程序
      • 生成SOAP源码
      • 建立服务端项目
    • 打印报文
  • SOAP测试
  • 项目源码
  • 总结

本文主要介绍C++中gSOAP的使用方法,附带介绍SOAP协议的基础知识,适用于第一次使用gSOAP的开发人员。gSOAP官网上的示例代码存在一些错误,对初次接触的人不太友好,本文是在官方示例calc++的基础上进行了一些补充、改动。

SOAP简介

SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息,具体内容可以参考SOAP教程。SOAP的本质是通过HTTP协议以XML格式进行数据交互,只不过这个XML格式的定义是大家公认的。

使用SOAP时需注意,SOAP的XML命名空间由于版本的不同可能存在差异(如soapevnSOAP-ENV),在调用SOAP服务前最好确认服务器的XML格式。

gSOAP

gSOAP 有商业版、开源版两个版本,开源版使用GPLv2开源协议,支持多个操作系统,具体内容参考github或者官网

gSOAP提供了一组编译工具(可以认为是代码生成器)和一些库文件,简化C/C++语言开发web服务或客户端程序的工作,开发人员可以专注于实现应用程序的逻辑:

  • 编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,能够自动完成本地C或C++数据类型和XML数据结构之间的转换。
  • 库文件提供了SOAP报文生成、HTTP协议通讯的实现,及相关的配套设施,用于最终的SOAP报文的生成、传输。

本文使用的库文件主要是以下几个:

  • stdsoap2.hstdsoap2.cpp:HTTP协议的实现、最终的SOAP报文生成,如果是C语言则使用stdsoap2.h、stdsoap2.c
  • typemap.dat: wsdl2h工具根据wsdl文件生成头文件时需要此文件,可以更改项目的xml命名空间(后面再细说)
  • threads.h:实现高性能的多线程服务器需要的文件,可以并发处理请求,并且在服务操作变得耗时时不会阻塞其他客户端请求

准备工作

先进入官网的下载页面,然后选择开源版本:

也可以直接点击开源版本的官方下载链接https://pan.baidu.com/s/156PEw9OMbzQel39Oozknow提取码: gy4x。

将下载的压缩包解压(本文使用的是gsoap_2.8.117.zip),解压后的文件放到自己习惯的位置(推荐放到C盘)。

在命令行提示符窗口中,使用cd命令进入..\gsoap_2.8.117\gsoap-2.8\gsoap\bin\win64目录:

注:后面需要把头文件、typemap.dat放到该程序目录下,生成的文件也在这里。

头文件

使用gSOAP的编译工具生成代码,需要一个定义API的头文件,此处使用官方示例中的calc.h头文件,对两个数字进行加、减、乘、除或乘方运算:。

calc.h头文件可以通过wsdl2h工具自动生成(需要Web 服务的 WSDL文件 ),运行的cmd命令如下:

wsdl2h -o calc.h http://www.genivia.com/calc.wsdl

也可以手动定义一个calc.h头文件,内容如下:

//gsoap ns service method add Sums two values
int ns__add(double a, double b, double &result);
//gsoap ns service method sub Subtracts two values
int ns__sub(double a, double b, double &result);
//gsoap ns service method mul Multiplies two values
int ns__mul(double a, double b, double &result);
//gsoap ns service method div Divides two values
int ns__div(double a, double b, double &result);
//gsoap ns service method pow Raises a to b
int ns__pow(double a, double b, double &result);

构建客户端应用程序

客户端应用程序在命令行中运行并使用命令行参数调用计算器 Web 服务来对两个数字进行加、减、乘、除或乘方运算。

生成soap源码

为客户端生成服务和数据绑定接口:

soapcpp2 -j -r -CL calc.h

其中 option-j生成 C++ 代理类,option-r生成报告,option-CL仅生成客户端,而没有(未使用的)lib 文件。

这会生成以下几个文件:

其中,xml文件是SOAP报文的示例,便于后期的调试,以add方法为例。

add方法的请求报文ns.add.req.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:ns="http://tempuri.org/ns.xsd">
 <SOAP-ENV:Body>
  <ns:add>
   <a>0.0</a>
   <b>0.0</b>
  </ns:add>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

add方法的响应报文ns.add.req.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:ns="http://tempuri.org/ns.xsd">
 <SOAP-ENV:Body>
  <ns:addResponse>
   <result>0.0</result>
  </ns:addResponse>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

建立客户端项目

建立一个C++的控制台应用项目,引入上面生成的几个文件:

  • soapStub.h: 没有注释的纯 C/C++ 头文件语法的规范副本。
  • soapH.h:声明 XML 序列化程序。
  • soapC.cpp: 实现 XML 序列化程序。
  • soapProxy.h: 定义客户端 XML 服务 API 类Proxy。
  • soapProxy.cpp: 实现客户端 XML 服务 API 类Proxy。
  • calc.nsmap: XML 命名空间绑定表到 #include。

还需要引入gSOAP解压目录下的stdsoap2.h和stdsoap2.cpp文件。

为方便调试,客户端程序示例改为固定参数调用add方法,代码如下:

#include "soapProxy.h"
#include "ns.nsmap"
/* the Web service endpoint URL */
const char server[] = "http://localhost:8080";
int main(int argc, char** argv)
{
    /*if (argc < 4)
    {
        fprintf(stderr, "Usage: [add|sub|mul|div|pow] num num\n");
        exit(1);
    }*/
    Proxy calc(server);
    double a, b, result;
    /*a = strtod(argv[2], NULL);
    b = strtod(argv[3], NULL);*/
    a = 3;
    b = 23;
    /*switch (*argv[1])*/
    switch ('a')
    {
    case 'a':
        calc.add(a, b, result);
        break;
    case 's':
        calc.sub(a, b, result);
        break;
    case 'm':
        calc.mul(a, b, result);
        break;
    case 'd':
        calc.div(a, b, result);
        break;
    case 'p':
        calc.pow(a, b, result);
        break;
    default:
        fprintf(stderr, "Unknown command\n");
        exit(1);
    }
    if (calc.soap->error)
        calc.soap_stream_fault(std::cerr);
    else
        std::cout << "result = " << result << std::endl;
    calc.destroy(); /* clean up */
    std::cout << std::endl;
    return 0;
}

构建服务端应用程序

实现一个独立的迭代服务器,它接受主机端口上的传入请求,支持多线程,可以并发处理请求,并且在服务操作变得耗时时不会阻塞其他客户端请求。

生成SOAP源码

为服务器端生成服务和数据绑定接口:

soapcpp2 -j -r -SL calc.h

其中 option-j生成 C++ 服务类,option-r生成报告,option-SL仅生成服务器端,而没有(未使用的)lib 文件。

生成的文件和客户端的文件名称一样,只是内容不同。

建立服务端项目

建立一个C++的控制台应用项目,引入的生成文件和客户端的相同。为了支持多线程,需要引入文件threads.h。
服务端代码如下:

#include "soapService.h"
#include "ns.nsmap"
#include "threads.h"
int port = 8080;
void* process_request(void* arg)
{
    Service* service = (Service*)arg;
    THREAD_DETACH(THREAD_ID);
    if (service)
    {
        service->serve();
        service->destroy(); /* clean up */
        delete service;
    }
    return NULL;
}
int main()
{
    Service service(SOAP_IO_KEEPALIVE);                      /* enable HTTP kee-alive */
    service.soap->send_timeout = service.soap->recv_timeout = 5; /* 5 sec socket idle timeout */
    service.soap->transfer_timeout = 30;                         /* 30 sec message transfer timeout */
    SOAP_SOCKET m = service.bind(NULL, port, 100);              /* master socket */
    if (soap_valid_socket(m))
    {
        while (soap_valid_socket(service.accept()))
        {
            THREAD_TYPE tid;
            void* arg = (void*)service.copy();
            /* use updated THREAD_CREATE from plugin/threads.h https://www.genivia.com/files/threads.zip */
            if (arg)
                while (THREAD_CREATE(&tid, (void* (*)(void*))process_request, arg))
                    Sleep(1);
        }
    }
    service.soap_stream_fault(std::cerr);
    service.destroy(); /* clean up */
    return 0;
}
/* service operation function */
int Service::add(double a, double b, double& result)
{
    result = a + b;
    return SOAP_OK;
}
/* service operation function */
int Service::sub(double a, double b, double& result)
{
    result = a - b;
    return SOAP_OK;
}
/* service operation function */
int Service::mul(double a, double b, double& result)
{
    result = a * b;
    return SOAP_OK;
}
/* service operation function */
int Service::div(double a, double b, double& result)
{
    if (b)
        result = a / b;
    else
        return soap_senderfault("Division by zero", NULL);
    return SOAP_OK;
}
/* service operation function */
int Service::pow(double a, double b, double& result)
{
    result = ::pow(a, b);
    if (soap_errno == EDOM)   /* soap_errno is like errno, but portable */
        return soap_senderfault("Power function domain error", NULL);
    return SOAP_OK;
}

打印报文

在实际调试中,需要确定SOAP协议过程中具体的报文,只需要改动stdsoap2.cpp源码即可。参考gsoap报文打印,实现保存最后一次报文到特定的文件。

stdsoap2.h头文件include下添加fstream,内容如下:

#include "stdsoap2.h"
#include <fstream>

soap_begin_recv函数开始处添加以下代码:

//发送完请求报文 获取请求报文信息(作为客户端的时候)
std::string str_reqXml = "";
std::string strBuf;
std::string::size_type pos1 = std::string::npos;
std::string::size_type pos2 = std::string::npos;
strBuf = soap->buf;
pos1 = strBuf.find("<?xml", 0);
pos2 = strBuf.find("</SOAP-ENV:Envelope>", 0);
if (pos1 != std::string::npos && pos2 != std::string::npos)
{
    str_reqXml = strBuf.substr(pos1, pos2 - pos1 + 20);
}
std::ofstream  outfile;
outfile.open("reqXml.txt");
outfile << str_reqXml;
outfile.close();

soap_body_end_in函数开始处添加以下代码:

//接收完应答报文 获取应答报文信息(作为客户端的时候)
std::string str_resXml = "";
std::string strBuf;
std::string strEnd = "</SOAP-ENV:Envelope>";
std::string::size_type pos1 = std::string::npos;
std::string::size_type pos2 = std::string::npos;
pos1 = std::string::npos;
pos2 = std::string::npos;
soap->buf[SOAP_BUFLEN - 1] = '\0';
strBuf = soap->buf;
pos1 = strBuf.find("<?xml", 0);
pos2 = strBuf.find(strEnd, 0);
if (pos1 != std::string::npos && pos2 != std::string::npos)
{
    str_resXml = strBuf.substr(pos1, pos2 - pos1 + strEnd.length());
}
std::ofstream  outfile;
outfile.open("resXml.txt");
outfile << str_resXml;
outfile.close();

soap_recv_raw函数结尾处(return前)添加以下代码:

//请求报文(作为服务端的时候)
std::string req_data;
req_data.assign(soap->buf, ret);
std::ofstream  outfile;
outfile.open("req_data.txt");
outfile << req_data;
outfile.close();

soap_flush_raw函数结尾处(return前)添加以下代码:

//应答报文(作为服务端的时候)
std::string res_data;
res_data.assign(s, n);
std::ofstream  outfile;
outfile.open("res_data.txt");
outfile << res_data;
outfile.close();

注:客户端可以一直打印报文,服务端只能在Debug运行时才会打印报文,应该有其它方法可以解决。

SOAP测试

运行上面的服务器、客户端项目,可以看到运行API调用结果,也可以使用SoapUI进行测试。

进入官网的下载链接:https://www.soapui.org/downloads/soapui/,下载开源版本并安装。

打开软件,在菜单栏的“File”中选择“New SOAP Project”:

展开左侧的项目,双击“Request”,开始进行测试:

项目源码

SoapTest提取码: 89mu

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • spring boot 开发soap webservice的实现代码

    介绍 spring boot web模块提供了RestController实现restful,第一次看到这个名字的时候以为还有SoapController,很可惜没有,对于soap webservice提供了另外一个模块spring-boot-starter-web-services支持.本文介绍如何在spring boot中开发soap webservice接口,以及接口如何同时支持soap和restful两种协议. soap webservice Web service是一个平台独立的,低耦

  • RPC、RMI、SOAP的区别详解

    ============================================================================ RPC与RMI的区别 ============================================================================ RPC:(Remote Procedure Call) 被设计为在应用程序间通信的平台中立的方式,它不理会操作系统之间以及语言之间的差异. 支持多语言. RMI:(Rem

  • php中curl和soap方式请求服务超时问题的解决

    公司中有不少服务是以curl或者soap方式连接第三方公司做的服务来交互数据,最近新增加了个需求,就是第三方服务发版时候,连接不上对方服务器时候要进行重试,其它原因导致的业务处理失败,则按失败处理,不会再进行调用. 思路就是判断curl或者soap连接不上对方服务器时候,抛出TimeoutException异常,捕获后做重试处理,其它错误导致的抛出的Exception则按失败处理. curl处理 $ch = curl_init($url); $options = array( CURLOPT_R

  • php实现通过soap调用.Net的WebService asmx文件

    本文实例讲述了php实现通过soap调用.Net的WebService asmx文件.分享给大家供大家参考,具体如下: 最近,帮一个同行测试用.net写的WebService接口,C#调用通过,现在需要测试一下php版本对它的调用,经过各种探索,相关的PHP调用webservice的过程如下: 1.打开php相关扩展: 找到配置文件php.ini 文件, 打开以下扩展 extension = php_soap.dll extension = php_curl.dll extension = ph

  • js调用webservice构造SOAP进行身份验证

    本文实例为大家分享了js调用webservice构造SOAP进行身份验证的相关内容,注释清除,供大家参考,具体内容如下 <html> <head> <title>无标题页</title> <script language="javascript" type="text/javascript"> // <!CDATA[ //define var xmlhttp; var value=new Array(

  • C++中gSOAP的使用详解

    目录 SOAP简介 gSOAP 准备工作 头文件 构建客户端应用程序 生成soap源码 建立客户端项目 构建服务端应用程序 生成SOAP源码 建立服务端项目 打印报文 SOAP测试 项目源码 总结 本文主要介绍C++中gSOAP的使用方法,附带介绍SOAP协议的基础知识,适用于第一次使用gSOAP的开发人员.gSOAP官网上的示例代码存在一些错误,对初次接触的人不太友好,本文是在官方示例calc++的基础上进行了一些补充.改动. SOAP简介 SOAP 是一种简单的基于 XML 的协议,它使应用

  • Angularjs中数据绑定的实例详解

    Angularjs中数据绑定的实例详解 这是一个最简单的angularjs的例子,关于数据绑定的,大家可以执行一下,看看效果 <html ng-app> <head> <title>angularjs-include</title> <script type="text/javascript" src="js/angular/angular.min.js"></script> </head

  • 基于angular中的重要指令详解($eval,$parse和$compile)

    在angular的服务中,有一些服务你不得不去了解,因为他可以说是ng的核心,而今天,我要介绍的就是ng的两个核心服务,$parse和$compile.其实这两个服务讲的人已经很多了,但是100个读者就有100个哈姆雷特,我在这里讲讲自己对于他们两个服务的理解. 大家可能会疑问,$eval呢,其实他并不是一个服务,他是scope里面的一个方法,并不能算服务,而且它也基于parse的,所以只能算是$parse的另一种写法而已,我们看一下ng源码中$eval的定义是怎样的就知道了 $eval: fu

  • python中 logging的使用详解

    日志是用来记录程序在运行过程中发生的状况,在程序开发过程中添加日志模块能够帮助我们了解程序运行过程中发生了哪些事件,这些事件也有轻重之分. 根据事件的轻重可分为以下几个级别: DEBUG: 详细信息,通常仅在诊断问题时才受到关注.整数level=10 INFO: 确认程序按预期工作.整数level=20 WARNING:出现了异常,但是不影响正常工作.整数level=30 ERROR:由于某些原因,程序 不能执行某些功能.整数level=40 CRITICAL:严重的错误,导致程序不能运行.整数

  • JavaWeb Servlet中Filter过滤器的详解

    JavaWeb Servlet中Filter过滤器的详解 1.简述 Filter过滤器,对web服务器所有web资源进行过滤,从而实现一些特殊的功能(权限访问控制.过滤敏感词汇.压缩响应信息).Filter能够对Servlet容器的请求和响应进行检查和修改,其本身不能生成请求request和响应response,只提供过滤作用(Servlet被调用之前检查Request对象修改其相关信息,Servlet被调用后检查Response修改其相关信息),Filter对象常驻服务器. 2.Lifecyc

  • C++ 中构造函数的实例详解

    C++ 中构造函数的实例详解 c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初学者有所帮助. 1. 构造函数是干什么的 class Counter { public: // 类Counter的构造函数 // 特点:以类名作为函数名,无返回类型 Counter() { m_value = 0; } private: // 数据成员 int m_va

  • 基于C++中setiosflags()的用法详解

    cout<<setiosflags(ios::fixed)<<setiosflags(ios::right)<<setprecision(2); setiosflags 是包含在命名空间iomanip 中的C++ 操作符,该操作符的作用是执行由有参数指定区域内的动作:   iso::fixed 是操作符setiosflags 的参数之一,该参数指定的动作是以带小数点的形式表示浮点数,并且在允许的精度范围内尽可能的把数字移向小数点右侧:   iso::right 也是se

  • JSP Spring配置文件中传值的实例详解

    JSP Spring配置文件中传值的实例详解 通过spring提供方法,在配置文件中取传值 调用get方法  targetObject :指定调用的对象       propertyPath:指定调用那个getter方法 例1: public class Test1 { private String name = "nihao"; public String getName() { return name; } } Xml代码 <bean id="t1" cl

  • Angular 中 select指令用法详解

    最近在angular中使用select指令时,出现了很多问题,搞得很郁闷.查看了很多资料后,发现select指令并不简单,决定总结一下. select用法: <select ng-model="" [name=""] [required=""] [ng-required=""] [ng-options=""]> </select> 属性说明: 发现并没有ng-change属性 ng-

  • Angular中的$watch方法详解

    在$apply方法中提到过脏检查,首先apply方法会触发evel方法,当evel方法解析成功后,会去触发digest方法,digest方法会触发watch方法. (1)$watch简介 在digest执行时,如果watch观察的的value与上一次执行时不一样时,就会被触发. AngularJS内部的watch实现了页面随model的及时更新. $watch方法在用的时候主要是手动的监听一个对象,但对象发生变化时触发某个事件. (2)watch方法用法 $watch(watchFn,watch

随机推荐