C++中gSOAP的使用详解

编辑: admin 分类: c#语言 发布时间: 2021-12-12 来源:互联网
目录
  • 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

          总结

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

          【转自:http://www.1234xp.com/kt.html 转载请说明出处】