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

准备工作

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

也可以直接点击开源版本的官方下载链接gsoap_2.8.117 提取码: f78f

将下载的压缩包解压(本文使用的是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.hstdsoap2.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”,开始进行测试:



项目源码

C++中gSOAP的使用的更多相关文章

  1. C++实现一个SOAP客户端

    目录 简介 实现客户端 准备xml文件 引入库文件 构建请求数据的xml 执行Http协议的POST方法 解析响应数据的xml 测试客户端 附件 简介 在C++中,一般使用gSOAP来实现客户端.服务 ...

  2. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  3. gSoap实现ONVIF中xsd__anyType到具体结构类型的转换

    上一篇文章已经粗略计划要讨论gsoap关于序列化/解析编程. 本文则阐述一下关于gsoap生成代码的一些重要特征方法及使用.如题,下我们从ONVIF生成的C码中,挑选简单的一个类型来试验一下与xsd_ ...

  4. 转 gSOAP中使用TCP协议传输数据

    一  模型 TCP/IP是一个协议族(Internet protocol suite),包含众多的协议,传输控制协议(TCP)和网际协议(IP)分属不同的层次,是保证数据完整传输的两个基本的重要协议. ...

  5. gSOAP MTOM

    前言 需要准备的知识:wsdl,soap,gSOAP,C++,fidder. 首先介绍几个相关的概念 1.MTOM基础概念      MTOM(Message Transmission Optimiz ...

  6. gsoap框架下的onvif程序流程分析

    SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap) { do { unsigned int k = soap->max_keep_al ...

  7. 基于gSOAP使用头文件的C语言版web service开发过程例子

    基于gSOAP使用头文件的C语言版web service开发过程例子 一服务端 1 打开VS2005,创建一个工程,命名为calcServer. 2 添加一个头文件calc.h,编辑内容如下: 1// ...

  8. 在Windows下用gSoap实现简单加法实例

    实现一个简单的a+b程序,在服务器端写一个程序,里面包含了a+b的函数,然后通过客户端代码向其发送两个数字,在服务器运算得到结果返回给客户端显示出来. 1.在gSoap的官网上下载文件夹,本人的版本是 ...

  9. [转贴]gsoap使用心得!

    最近换了个工作环境,现在在大望路这边上班,呵,刚上班接到的任务就是熟悉gsoap!废话少说,现在开始gSoap学习! gSOAP是一个夸平台的,用于开发Web Service服务端和客户端的工具,在W ...

随机推荐

  1. PHP中非常好玩的Calendar扩展学习

    为什么说这个 Calendar 扩展很好玩呢?因为你基本用不到它!这个扩展是一套关于日期历法的扩展,但是对于我们来说,它没有农历的相关操作,所以对于我们中国人来说这个扩展并没有什么实际的作用.不过这并 ...

  2. LINUX服务器带宽跑满、负载过高问题排查

    1.centos 安装流量监控iftop apt-get  install iftop -y 2.查看网卡名称 ifconfig 3.查看端口占用情况 iftop -i 网卡名称 -P 执行 nets ...

  3. struts2 使用ajax进行图片上传

    第一步:引入一个插件    jquery.form.js /*! * jQuery Form Plugin * version: 3.36.0-2013.06.16 * @requires jQuer ...

  4. Git:分布式版本控制系统

    参考廖雪峰的 Git 教程:https://www.liaoxuefeng.com/wiki/896043488029600  讲解很详细,这里只做一些个人笔记: 各系统安装 Git :https:/ ...

  5. P3352-[ZJOI2016]线段树【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P3352 题目大意 \(n\)个数字的一个序列,每次随机选择一个区间让这个区间所有数等于这个区间的最大值,重复\(q ...

  6. vue-devtools 安装

    vue火了很久了,但是一直赶不上时代步伐的我今天才开始学,首先,根据vue官网介绍,推荐安装Vue Devtools.它允许你在一个更友好的界面中审查和调试 Vue 应用. 首先,将vue-devto ...

  7. SpringBoot-Thymeleaf模板引擎

    模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想 ...

  8. SPOJ16636 Journey IE2

    SPOJ16636 Journey IE2 更好的阅读体验 在Byteland有n个城市,编号从1到n.这些城市由m条双向道路网络连接.众所周知,每一对城市最多只能由一条道路连接. Byteman最近 ...

  9. Java(17)面向对象之多态

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201621.html 博客主页:https://www.cnblogs.com/testero ...

  10. Java(19)接口知识及综合案例

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201629.html 博客主页:https://www.cnblogs.com/testero ...