gSOAP 提供了两个工具来方便开发人员使用 C/C++ 语言快速开发Web 服务应用,通过 gSOAP 提供的这两个工具,开发人员可以快速生成服务端与客户端代码框架,接下来开发人员只需要实现具体的接口函数即可。
gSOAP工具可以在Windows、Linux和Macosx操作系统下运行,gSOAP工具包中自带有Windows和Macosx操作系统的wsdl2h和soapcpp2可执行文件。wsdl2h 工具根据 WSDL 文件生成 C/C++ .h 头文件;soapcpp2 工具则从上面生成的头文件生成 SOAP 服务端和客户端框架代码。
gSOAP开源版下载网址(最新版本):gSOAP Toolkit download | SourceForge.net
======================================================
下载gSOAP工具,进行解压。创建一个文件夹onvif1,从gSOAP工具中拷贝如下文件和文件夹到onvif1文件夹中:
soap_2.8\gsoap-2.8\gsoap\bin\win32\soapcpp2.exe
gsoap_2.8\gsoap-2.8\gsoap\bin\win32\wsdl2h.exe
gsoap_2.8\gsoap-2.8\gsoap\stdsoap2.c
gsoap_2.8\gsoap-2.8\gsoap\stdsoap2.h
gsoap_2.8\gsoap-2.8\gsoap\typemap.dat
gsoap_2.8\gsoap-2.8\gsoap\import\
gsoap_2.8\gsoap-2.8\gsoap\custom\
还有一个onvif模块wsdl文件remotediscovery.wsdl,也放入该目录下。
=====================================================
2、进行wsdl2h编译,得到的文件onvif.h
wsdl2h 工具根据 WSDL 文件生成 C/C++ .h 头文件。
WSDL(Web Service Description Language)即 Web 服务描述语言,它使用 XML 来对 Web 服务进行描述。进入电脑的cmd环境,进入刚才创建的onvif1目录。
wsdl2h 的用法:
格式用法: wsdl2h -o 头文件名 WSDL文件名或URL
在本篇中,主要使用gSOAP工具在Windows操作系统下生成onvif协议的设备搜索代码框架,的wsdl2h和soapcpp2可执行文件,onvif设备搜索的WSDL文件的URL地址为:
RemoteDiscovery(设备发现)
https://www.onvif.org/ver10/networ/wsdl/remotediscovery.wsdl
方法一
本篇中在C:\WINDOWS\system32\cmd.exe中使用URL的wsdlh2的命令为:
wsdl12h -P -x -c -s -t ./typemap.dat -o samples/onvif/onvif.h https://www.onvif.org/ver10/networ/wsdl/remotediscovery.wsdl
方法二
在C:\WINDOWS\system32\cmd.exe中不使用模块的URL,可以先下载需要使用的onvif模块,命令行后加上模块名,这样wsdlh2的命令为:
wsdl2h -o onvif.h -c -s -t ./typemap.dat remotediscovery.wsdl
-c为产生纯c代码,默认生成 c++代码;
-s为不使用STL库,
-t为typemap.dat的标识。
onvif模块wsdl文件下载地址为:
https://download.csdn.net/download/weixin_44651073/87580490
运行结束后,得到变异的c++头文件(onvif.h)
=================================================================
soapcpp2 工具则从上面生成的头文件生成 SOAP 服务端和客户端框架代码。例如对于上面的cacl.h
,使用 soapcpp2 命令:
soapcpp2 -2 -c onvif.h -I .\custom -I .\import -I .\import
soapcpp2 也支持额外的参数:
-i 生成 C++ 包装类,客户端为 xxxProxy.h(.cpp),服务端为xxxService.h(.cpp)
-I 指定 import 的路径,比如需要引入stlvector.h文件来支持 STL vector 的序列化
-C 仅生成客户端代码
-S 仅生成服务端代码
-c 产生纯 C 代码,否则是 C++ 代码
-x 不要产生 XML 示例文件
-L 不要产生soapClientLib.c和soapServerLib.c文件
最后运行的结果为:
命令的其他参数详细说明如下:
可以使用命令wsdl2h.exe -help查看
E:\onvif1>wsdl2h.exe -help
Usage: wsdl2h [-a] [-b] [-c|-c++|-c++11|-c++14|-c++17] [-D] [-d] [-e] [-F] [-f] [-g] [-h] [-I path] [-i] [-j] [-k] [-L] [-l] [-M] [-m] [-N name] [-n name] [-O1|-O2|-O3|-O4|-Ow2|-Ow3|-Ow4] [-P|-p] [-Q] [-q name] [-R] [-r proxyhost[:port[:uid:pwd]]] [-r:uid:pwd] [-Sname] [-s] [-T] [-t typemapfile] [-U] [-u] [-V] [-v] [-w] [-W] [-x] [-y] [-z#] [-_] [-o outfile.h] infile.wsdl infile.xsd http://www... ...-a generate indexed struct names for local elements with anonymous types
-b bi-directional operations (duplex ops) added to serve one-way responses
-c generate C source code
-c++ generate C++ source code (default)
-c++11 generate C++11 source code
-c++14 generate C++14 source code
-c++17 generate C++17 source code
-D make attribute members with default/fixed values optional with pointers
-d use DOM to populate xs:any, xs:anyType, and xs:anyAttribute
-e don't qualify enum names
-F add transient members to structs to simulate struct-type derivation in C
-f generate flat C++ class hierarchy by removing inheritance
-g generate global top-level element and attribute declarations
-h display help info and exit
-Ipath use path to locate WSDL and XSD files
-i don't import (advanced option)
-j don't generate SOAP_ENV__Header and SOAP_ENV__Detail definitions
-k don't generate SOAP_ENV__Header mustUnderstand qualifiers
-L generate less documentation by removing generic @note comments
-l display license information
-M suppress error "must understand element with wsdl:required='true'"
-m use xsd.h module to import primitive types
-Nname use name for service prefixes to produce a service for each binding
-nname use name as the base namespace prefix instead of 'ns'
-O1 optimize by omitting duplicate choice/sequence members
-O2 optimize -O1 and omit unused schema types (unreachable from roots)
-O3 optimize -O2 and omit unused schema root attributes
-O4 optimize -O3 and omit unused schema root elements (use only with WSDLs)
-Ow2 optimize -O2 while retaining all derived types of used base types
-Ow3 optimize -O3 while retaining all derived types of used base types
-Ow4 optimize -O4 while retaining all derived types of used base types
-ofile output to file
-P don't create polymorphic types inherited from xsd__anyType
-p create polymorphic types inherited from base xsd__anyType
-Q make xsd__anySimpleType equal to xsd__anyType to use as the base type
-qname use name for the C++ namespace of all declarations
-R generate REST operations for REST bindings specified in a WSDL
-rhost[:port[:uid:pwd]]connect via proxy host, port, and proxy credentials uid and pwd
-r:uid:pwdconnect with authentication credentials uid and pwd
-Sname use name instead of 'soap' for the C++ class members with soap contexts
-s don't generate STL code (no std::string and no std::vector)
-tfile use type map file instead of the default file typemap.dat
-U allow UTF-8-encoded Unicode C/C++ identifiers when mapping XML tag names
-u don't generate unions
-V display the current version and exit
-v verbose output
-W suppress warnings
-w always wrap response parameters in a response struct (<=1.1.4 behavior)
-X don't qualify part names to disambiguate doc/lit wrapped patterns
-x don't generate _XML any/anyAttribute extensibility elements
-y generate typedef synonyms for structs and enums
-z1 compatibility with 2.7.6e: generate pointer-based arrays
-z2 compatibility with 2.7.7-2.7.15: (un)qualify element/attribute refs
-z3 compatibility with 2.7.16-2.8.7: (un)qualify element/attribute refs
-z4 compatibility up to 2.8.11: don't generate union structs in std::vector
-z5 compatibility up to 2.8.15: don't include minor improvements
-z6 compatibility up to 2.8.17: don't include minor improvements
-z7 compatibility up to 2.8.59: don't generate std::vector of class of union
-z8 compatibility up to 2.8.74: don't gen quals for doc/lit wrapped patterns
-z9 compatibility up to 2.8.93: always qualify element/attribute refs
-z10 compatibility up to 2.8.96: gen quals even when defined w/o namespace
-_ don't generate _USCORE (replace with Unicode code point _x005f)
infile.wsdl infile.xsd http://www... list of input sources (if none reads stdin)
在生成的onvif.h代码中,要修改上面生成的onvif.h
: 在onvif.h
头文件开头加入:#import "wsse.h"
为了方便,我们将所有需要的代码放到同一个目录中。我们在刚才建立的onvif1
目录下创建一个application
目录,存放我们需要的所有代码。
比如soap下生成的soapC.cpp、soapClient.cpp、soapH.h、soapStub.h、wsdd.nsmap;gsoap源码目录下的stdsoap2.cpp、stdsoap2.h;gsoap/plugin目录下的wsseapi.h、wsseapi.cpp、smdevp.h、smdevp.cpp、mecevp.cpp、mecevp.h、threads.cpp、threads.h、wsaapi.cpp、wsaapi.h等。并创建一个main.cpp(注意,如果只有.c没有.cpp的,那么就将其拷贝到application目录下,然后将尾部改成.cpp):
=========================================================================
#include
#include
#include #ifdef WIN32
#include
#else
#include
#include
#include
#include
#include
#endif/* 从技术层面来说,通过单播、多播、广播三种方式都能探测到IPC,但多播最具实用性*/
#define COMM_TYPE_UNICAST 1 // 单播
#define COMM_TYPE_MULTICAST 2 // 多播
#define COMM_TYPE_BROADCAST 3 // 广播
#define COMM_TYPE COMM_TYPE_MULTICAST/* 发送探测消息(Probe)的目标地址、端口号 */
#if COMM_TYPE == COMM_TYPE_UNICAST#define CAST_ADDR "100.100.100.15" // 单播地址,预先知道的IPC地址
#elif COMM_TYPE == COMM_TYPE_MULTICAST#define CAST_ADDR "239.255.255.250" // 多播地址,固定的239.255.255.250
#elif COMM_TYPE == COMM_TYPE_BROADCAST#define CAST_ADDR "100.100.100.255" // 广播地址
#endif#define CAST_PORT 3702 // 端口号/* 以下几个宏是为了socket编程能够跨平台,这几个宏是从gsoap中拷贝来的 */
#ifndef SOAP_SOCKET
# ifdef WIN32
# define SOAP_SOCKET SOCKET
# define soap_closesocket(n) closesocket(n)
# else
# define SOAP_SOCKET int
# define soap_closesocket(n) close(n)
# endif
#endif#if defined(_AIX) || defined(AIX)
# if defined(_AIX43)
# define SOAP_SOCKLEN_T socklen_t
# else
# define SOAP_SOCKLEN_T int
# endif
#elif defined(SOCKLEN_T)
# define SOAP_SOCKLEN_T SOCKLEN_T
#elif defined(__socklen_t_defined) || defined(_SOCKLEN_T) || defined(CYGWIN) || defined(FREEBSD) || defined(__FreeBSD__) || defined(OPENBSD) || defined(__QNX__) || defined(QNX) || defined(OS390) || defined(__ANDROID__) || defined(_XOPEN_SOURCE)
# define SOAP_SOCKLEN_T socklen_t
#elif defined(IRIX) || defined(WIN32) || defined(__APPLE__) || defined(SUN_OS) || defined(OPENSERVER) || defined(TRU64) || defined(VXWORKS) || defined(HP_UX)
# define SOAP_SOCKLEN_T int
#elif !defined(SOAP_SOCKLEN_T)
# define SOAP_SOCKLEN_T size_t
#endif#ifdef WIN32
#define SLEEP(n) Sleep(1000 * (n))
#else
#define SLEEP(n) sleep((n))
#endif/* 探测消息(Probe),这些内容是ONVIF Device Test Tool 15.06工具搜索IPC时的Probe消息,通过Wireshark抓包工具抓包到的 */
const char *probe = "uuid:fc0bad56-5f5a-47f3-8ae2-c94a4e907d70 urn:schemas-xmlsoap-org:ws:2005:04:discovery http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe dn:NetworkVideoTransmitter ";int main(int argc, char **argv)
{int ret;int optval;SOAP_SOCKET s;SOAP_SOCKLEN_T len;char recv_buff[4096] = {0};struct sockaddr_in multi_addr;struct sockaddr_in client_addr;#ifdef WIN32WSADATA wsaData;if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0 ) { // 初始化Windows Sockets DLLprintf("Could not open Windows connection.\n");return 0;}if ( LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) {printf("the version of WinSock DLL is not 2.2.\n");return 0;}
#endifs = socket(AF_INET, SOCK_DGRAM, 0); // 建立数据报套接字if (s < 0) {perror("socket error");return -1;}#if COMM_TYPE == COMM_TYPE_BROADCASToptval = 1;ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*)&optval, sizeof(int));
#endifmulti_addr.sin_family = AF_INET; // 搜索IPC:使用UDP向指定地址发送探测消息(Probe)multi_addr.sin_port = htons(CAST_PORT);multi_addr.sin_addr.s_addr = inet_addr(CAST_ADDR);ret = sendto(s, probe, strlen(probe), 0, (struct sockaddr*)&multi_addr, sizeof(multi_addr));if (ret < 0) {soap_closesocket(s);perror("sendto error");return -1;}printf("Send Probe message to [%s:%d]\n\n", CAST_ADDR, CAST_PORT);SLEEP(1);for (;;) { // 接收IPC的应答消息(ProbeMatch)len = sizeof(client_addr);memset(recv_buff, 0, sizeof(recv_buff));memset(&client_addr, 0, sizeof(struct sockaddr));ret = recvfrom(s, recv_buff, sizeof(recv_buff) - 1, 0, (struct sockaddr*)&client_addr, &len);printf("===Recv ProbeMatch from [%s:%d]===\n%s\n\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), recv_buff);SLEEP(1);}soap_closesocket(s);return 0;
}