Linux 下使用Webservice gSOAP教程(一)

转自:https://my.oschina.net/u/3183495/blog/1841737

gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,在数据接入其他平台,如C#、JAVA实现的一些webservice服务,在需要将数据对接其他设备的应用中,gSOAP可以为开发提供很大方便。

    一般的,一个webservice服务端,会提供一个wsdl文件、一个接入的url地址和一份对应的字段定义文档,有了这些资料,就可以使用gSOAP进行webservice对接。

    关于gSOAP的安装、部署、这里不做介绍,网上有很多相应的指导方法。

    在进行对接之前,首先要明确服务端使用的是soap1.1还是soap1.2,通常soap1.2可以兼容soap1.1,反之不行。

    gSOAP工具包中,有2个文件wsdl2h和soapcpp2,这两个工具文件用来根据wsdl文件反编译出一套代码,代码中含有服务的接口、以及接口对应的各个字段,可以根据需求生成c或c++的代码,在自己的代码中,将需要传输到服务端的字段拼装后,即可调用接口进行数据的传输。

反编译代码步骤:

1.使用wsdl2h工具,根据wsdl文件生成一个c/c++语法结构的头文件

    Wsdl2h –s –o 头文件名 wsdl文件名

    这一步将会得到一个头文件,如:head.h该步的目的:实现WSDL文件到.h文件的数据映射。

2.使用gSoap的预编译器soapcpp2。

根据上一步得到的头文件来生成存根文件(soapStub.h)和客户端代码框架。

/usr/local/gSOAP/bin/soapcpp2 -i -x -C -L head.h

这一步将会得到几个.nsmap、.h和.cpp文件,如:calc.nsmap、soapC.cpp、soapH.h、soapStub.h、soapcalcProxy.cpp、soapcalcProxy.h

该步的目的:生成相应的底层通信代码。

 

注1 :wsdl2h的用法(WSDL/schema 解析和代码生成器)wsdl2h [opt] 头文件名 WSDL文件名或URL

wsdl2h常用选项

-o 文件名,指定输出头文件

-n 名空间前缀 代替默认的ns

-c 产生纯C代码,否则是C++代码

-s 不要使用STL代码

-t 文件名,指定type map文件,默认为typemap.dat

-e 禁止为enum成员加上名空间前缀

type map文件用于指定SOAP/XML中的类型与C/C++之间的转换规则。

如果不用s就会生成带STL的头文件,这样,在后边的编译中需要加入STL的头

stlvector.h,位于:gsoap/import/目录下。

注2 :soapcpp2的用法(编译和代码生成器)

soapcpp2 [opt] 头文件名

soapcpp2常用选项

-C 仅生成客户端代码

-S 仅生成服务器端代码

-L 不要产生soapClientLib.c和soapServerLib.c文件

-c 产生纯C代码,否则是C++代码(与头文件有关)

-I 指定import路径(见上文)

-x 不要产生XML示例文件

-i 生成C++封装(代理),客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)。

-j 和-i类似,区别在于生成的代理类不继承于soap struct,而是包含了包含了一个soap结构的指针。此种方式生存的代理类便于互相通信

 

注3 :gSoap工具wsdl2和soapcpp所生成文件的简单分析

(1) wsdl2生成的具有C/C++语法结构的头文件,其作用就是:将XML语法结构的WSDL文件映射为C/C++语法结构的.h文件;并为下一步做准备。

(2) soapcpp(采用参数:-i -x -C -L)生成的文件共有6个文件:PlayerBeanPortBinding.nsmap、soapC.cpp、soapH.h、soapPlayerBeanPortBindingProxy.cpp、soapPlayerBeanPortBindingProxy.h、soapStub.h。

a. PlayerBeanPortBinding.nsmap文件

该文件的作用:An XML-to-C/C++ namespace mapping table,即WSDL文件与生成的客户端代码框架的一个名字空间的映射表。

b. soapStub.h

该文件就是直接由wsdl2生成的头文件转化而来,它详细定义了WSDL所描述的各项服务和数据结构。

它是soap的存根文件,定义了由wsdl2生成的头文件里对应的远程调用模型(RPC)。

c. soapPlayerBeanPortBindingProxy.和soapPlayerBeanPortBindingProxy.cpp

这两个文件是客户端代码的一个简单封装,它封装了底层通信,并向外提供一个很简单的界面,该界面展示了用户能够使用的所有服务(由WSDL所描述)。

d. soapH.h和soapC.cpp

这个两个文件是soap的序列和反序列化代码,

注4 :设置字符编码

在利用gSoap编写Web Service客户端和服务器端的程序时,需要设置其编码方式。接口为:soap_set_mode,其实它是就是一个宏:

#define soap_set_mode(soap, n) ((soap)->imode |= (n),(soap)->omode |= (n))

如果要设置为UTF8

可以这样调用:soap_set_mode(&soap,SOAP_C_UTFSTRING);

详细信息可参考该宏所在文件:stdsoap2.h

 

客户端调用WSDL

工具:gsoap-2.8.66(这个是目前各个公司使用最多的,也是最完善的)

系统环境:Centos7 64位

步骤:    

1.下载gsoap工具 下载地址:https://sourceforge.net/projects/gsoap2/files/

2.解压工具:unzip gsoap_2.8.66.zip

         

3.编译我们需要的工具(由于我们要做到最少依赖,所以只需要编译用到的soapcpp2和wsdl2h两个工具)

4.开始编译:

    (1)进入到gsoap/src目录下,执行 make -f MakefileManual soapcpp2                 

          Linux 下使用Webservice gSOAP教程(一)_第1张图片

    (2)进入到gsoap/wsdl目录下,执行 make -f MakefileManual(如果你安装了OpenSSL就执行 make -f MakefileManual secure)
         Linux 下使用Webservice gSOAP教程(一)_第2张图片

         至此,工具都已编译完成,编译好的程序在gsoap/bin目录下。

5. 生成所需文件:

  (1)进入到gsoap/bin目录下,执行以下命令:

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

           会在当前目录下生成一个 calc.h文件

         Linux 下使用Webservice gSOAP教程(一)_第3张图片

   (2)接着执行以下命令:

            ./soapcpp2 -j -CL -I/path/to/gsoap/import calc.h

         Linux 下使用Webservice gSOAP教程(一)_第4张图片

  (3)可以在gsoap/bin目录下看到已经生成的所有文件

         Linux 下使用Webservice gSOAP教程(一)_第5张图片

6.编写calcclient.cpp客户端例子:

#include "calc.nsmap"      
#include "soapcalcProxy.h" 

int main()
{
  calcProxy calc;
  double sum;
  if (calc.add(1.23, 4.56, sum) == SOAP_OK)
    std::cout << "Sum = " << sum << std::endl;
  else
    calc.soap_stream_fault(std::cerr);
  calc.destroy(); // same as: soap_destroy(calc.soap); soap_end(calc.soap);

}

7.  把gsoap目录下的stdsoap2.h文件复制到gsoap/bin目录下:

    Linux 下使用Webservice gSOAP教程(一)_第6张图片

8.执行编译命令,会生成calcclient程序:

    c++ -o calcclient calcclient.cpp soapC.cpp soapcalcProxy.cpp /home/webservice/gsoap-2.8/gsoap/stdsoap2.cpp

    

     注:加上stdsoap2.cpp的具体路径,不然编译出错,这个是我的路径:/home/webservice/gsoap-2.8/gsoap/。

9.执行calcclient程序:

    ./calcclient   出现结果Sum = 5.79

    

  至此,一个简单的客户端小Demo就完成了。也算是初入gsoap。

  创建独立(多线程)服务器与客户端调用服务器

步骤:

1.手动编写calc.h文件

//gsoap ns service name:            calc Simple calculator service described at https://www.genivia.com/dev.html
//gsoap ns service protocol:        SOAP
//gsoap ns service style:           rpc
//gsoap ns service encoding:        encoded
//gsoap ns service namespace:       http://localhost/calc.wsdl
//gsoap ns service location:        http://localhost/calcserver.cgi

//gsoap ns schema namespace:        urn:calc

//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);

2.使用soapcpp2工具生成客户端所需文件(执行命令之前先删除上一个教程所生成的文件):

  ./soapcpp2 -j -CL calc.h

  

3.编写calcclient.cpp客户端代码:

#include "soapcalcProxy.h"
#include "calc.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(0);
  }
  calcProxy calc(server);
  double a, b, result;
  a = strtod(argv[2], NULL);
  b = strtod(argv[3], NULL);
  switch (*argv[1])
  {
    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(0);
  }
  if (calc.soap->error)
    calc.soap_stream_fault(std::cerr);
  else
    std::cout << "result = " << result << std::endl;
  calc.destroy(); /* clean up */
  return 0;
}

4.执行编译命令生成客户端程序:

  c++ -o calcclient calcclient.cpp soapC.cpp soapcalcProxy.cpp /home/webservice/gsoap-2.8/gsoap/stdsoap2.cpp

  注: /home/webservice/gsoap-2.8/gsoap/ 是stdsoap2.cpp的具体路径

5.使用soapcpp2工具生成服务端所需文件:

  ./soapcpp2 -j -SL calc.h

   

6.编写calcserver.cpp服务端代码:

#include 
#include "soapcalcService.h"
#include "calc.nsmap"
#include "threads.h" 

int port = 8080;

void *process_request(void *arg)
{
  calcService *service = (calcService*)arg;
  THREAD_DETACH(THREAD_ID);
  if (service)
  {
    service->serve();
    service->destroy(); /* clean up */
    delete service;
  }
  return NULL;
}

 
int main()
{
  calcService 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::err);
  service.destroy(); /* clean up */
  return 0;
}

/* service operation function */
int calcService::add(double a, double b, double &result)
{
  result = a + b;
  return SOAP_OK;
} 
/* service operation function */
int calcService::sub(double a, double b, double &result)
{
  result = a - b;
  return SOAP_OK;
} 
/* service operation function */
int calcService::mul(double a, double b, double &result)
{
  result = a * b;
  return SOAP_OK;
} 
/* service operation function */
int calcService::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 calcService::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;
}

7.执行编译命令生成服务端程序:

   c++ -o calcserver calcserver.cpp -I/home/webservice/gsoap-2.8/gsoap -I/home/webservice/gsoap-2.8/gsoap/plugin /home/webservice/gsoap-2.8/gsoap/stdsoap2.cpp soapC.cpp soapcalcService.cpp -pthread

  注:-I/home/webservice/gsoap-2.8/gsoap  是stdsoap2.h 的具体路径  -I/home/webservice/gsoap-2.8/gsoap/plugin 是threads.h的具体路径  /home/webservice/gsoap-2.8/gsoap 是stdsoap2.cpp的具体路径

8.启动服务端程序:

   ./calcserver

9.启动客户端程序:

   ./calcclient add 10 20

   result = 30;

   

   至此,本篇教程结束,完成了创建独立(多线程)服务器,客户端调用服务器的整个过程。

你可能感兴趣的:(Linux)