本文章是对gsoap中生成的cpp类型的代码框架的应用。因为要实现一些ONVIF规范中的一些功能,所以选择了gsoap生成代码框架。但是发现好多博客上都是生成的C的代码框架,使用起来很麻烦。而且引用插件功能的时候,什么时候需要修改文件内容,哪些配置需要加入什么插件代码,这些东西没有说明,而且gsoap本身生成代码框架的方式也不唯一,因此一开始不知所措。之前没有进行过C++开发,使用gcc和makefile进行编译更是没有概念,再加上gsoap的英文文档内容那不是一般的多,所以我感觉一开始看的时候,看博客文档看的头昏脑胀的。后来总结了一下该如何生成框架代码,如何编译以及怎么加入插件。
(其实CPP框架代码和C框架代码的编译方式是一样的,源码包中提供的插件代码都是c文件,在CPP框架下也不用修改为cpp后缀的,直接使用即可,我一开始不知道如何生成是因为,生成CPP代码框架的时候,不知道加入哪些文件,怎么编译)
所有的内容都是参考自源码包中的文档
常用插件
gsoap/plugin包含常用的插件:
The WS-Addressing plugin
The WS-Security plugin
The WS-Discovery plugin
The WS-ReliableMessaging plugin
The WS-Addressing plugin使用
如果你需要用到ONVIF中的web 服务寻址功能,那么需要添加此插件。一般也被The WS-Discovery plugin 和The WS-ReliableMessaging plugin需要。
首先,ONVIF使用WS-Addressing 2005/08,所以为了支持WS-Addressing 2005/08并支持 WS-Addressing 8/2004 应该在wsdl2h生成的头文件中添加
#import "wsa5.h"
这会把WS-Addressing 2005/08所需要的SOAP头结构体的定义导入到头文件中,并由soapcpp2插入到代码框架中。
使用:
- 在服务对应的WSDL文件上运行 wsdl2h -t typemap.dat
- 检查生成的头文件中是否包含 wsa5.h的导入,如果没有加入
#import "wsa5.h"
- 在wsdl2h生成的头文件上运行 soapcpp2 -a
(参数解释在后面) - 重新编译并链接stdsoap2.c/cpp dom.c/cpp(当需要的时候) wsaapi.c 和其他由soapcpp2 产生的代码文件
wsdl2h为要使用WS-Addressing服务的WSDL文件自动生成带有#import wsa.h
的头文件
wsdl2h产生的头文件中可能包含以下导入语句
#import "soap12.h"
#import "wsa.h" // or wsa3.h (2003/03), wsa4.h (2004/03), wsa5.h (2005/03)
这些wsa文件中定义了WS-Addressing信息中的头元素。
The WS-Security plugin使用
插件代码的实现位于wsseapi.h 和 wsseapi.c中,另外还需要smdevp.c smdevp.h mecevp.c mecevp.h threads.c threads.h struct_timeval.c struct_timeval.h ,其中各个文件的作用如下:
smdevp: streaming XML signature and message digest engine
mecevp: streaming XML encryption engine
threads: multi-threading and locking support
为支持ws-security功能,需要在wsdl2h生成的头文件中添加
#import "wsse.h"
这会把WS-Security所需要的SOAP头结构体的定义导入到头文件中
使用:
- 在需要WS-Security头的服务对应的WSDL文件上运行 wsdl2h -t typemap.dat
- 检查生成的头文件中是否存在wsse.h的导入语句,如果没有,在生成的头文件中加入导入语句
- 在wsdl2h生成的头文件上运行soapcpp2,产生代码
- 用编译标志-DWITH_DOM -DWITH_OPENSSL 编译 stdsoap2.c/pp, dom.c/pp, smdevp.c, mecevp.c, wsseapi.c, threads.c, struct_timeval.c和生成的源文件,最终链接文件的时候需要加上-lssl -lcrypto -lz,
wsse引擎是线程安全的,但是如果需要支持HTTPS的话,需要学习 WS-Security and HTTPS章节的内容,才能确保和HTTPS整合的WS-Security的线程安全。
wsse 的代码实现是在wsseapi.h和wsseapi.c(c和c++)中。
gsoap/custom/struct_timeval.c compile and link this file (C and C++).
gsoap/plugin/smdevp.c compile and link this file (C and C++).
gsoap/plugin/mecevp.c compile and link this file (C and C++).
compile all sources with -DWITH_OPENSSL -DWITH_DOM.
if you have zlib installed, compile all sources also with -DWITH_GZIP.
link with -lssl -lcrypto -lz -lgsoapssl++ (or -lgsoapssl for C, or compile stdsoap2.cpp for C++ and stdsoap2.c for C).
(上述配置没有说明要加上treads.c,因为没有HTTPS配置的wsee是线程安全的,但是最好加上,没有用也无所谓,另外WS-Discovery插件也可能需要threads.c文件)
wsdl2h生成的头文件中应该导入wsse.h#import "wsse.h"
,这个声明默认支持WS-Security1.0 ,并接受WS-Security1.1,ONVIF使用wsse.h就可以了。为了默认支持WS-Security1.1并接受WS-Security1.0, 可以使用#import "wsse11.h"
The WS-Discovery plugin使用
wsdd的服务端实现需要此插件。
WS-Discovery插件的实现在wsddapi.c中
使用:
- 在代码中定义WS-Discovery的事件处理句柄
- 使用wsdd API 的函数
- 编译并链接stdsoap2.c dom.c/cpp(当需要的时候),threads.c(当需要的时候) wsddapi.c wsaapi.c 和soapcpp2生成的源文件。(因此WS-Discovery插件是需要WS-Addressing插件的)
wsddapi.c 实现了WS-Discovery的ad-hoc模式和managed模式。
The WS-ReliableMessaging plugin使用
WS-ReliableMessaging 插件在 wsrmapi.h和wsrmapi.c
使用:
- 在需要WS-ReliableMessaging和WS-Addressing头的WSDL文件上运行
wsdl2h -b -t typemap.dat
, - 检查生成的头文件中是否导入了wsrm.h和 wsa5.h,如果没有的话,加入相应的导入语句
#import "wsrm.h"
#import "wsa5.h"
- 在生成的头文件上运行
soapcpp2 -a
- 重新编译并链接 stdsoap2.c/cpp dom.c/cpp(当需要的时候) wsrmapi.c wsaapi.c duration.c 和 soapcpp2生成的源文件
wsrm插件使用wsa插件实现的WS-Addressing 2005,两个插件都需要注册,wsrm插件API是独立的。 除非必须将特定于WS-Addressing的标头添加到消息中,否则无需使用wsa插件API。
使用gsoap生成onvif的开发环境
一、wsdl2h利用wsdl文件生成头文件
这里我们采用在线下载的方式,因为一个wsdl文件还依赖许多xsd文件进行验证,如果手动将这些xsd文件下载下来的话很麻烦。而采用在线下载的方式,wsdl2h工具可以自动处理并下载xsd文档。
wsdl2h -O4 -P -x -o onvif.h \
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl \
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl
-O4 优化生成的头文件,(去除多余或者无用的部分,有四个优化等级)
-P 该选项禁止从xsd__anyType继承得到的类型的生成,因为ONVIF协议不需要继承xsd__anyType
-x 此选项删除为xsd:any和xsd:anyAttribute组件生成的结构和类的_XML类型成员。
-o 输出文件名
这里先只使用两个wsdl文件测试一下,如果还需要另外的功能,添加wsdl文档就可以了
虽然给的wsdl文档的url是http协议的,但实际上这里wsdl2h下载wsdl文档采用的是https的方式,在下载包里有windows和mac的wsdl2h二进制文件,但是不支持https。如果是linux的话,需要手动编译安装,./configure
make
sudo make install
注意:
- ONVIF使用WS-Addressing 2005/08 ,但是wsdd10.h假定WS-Addressing是 WS-Addressing 2004/08,因此需要将wsdd10.h改为wsdd5.h
- 加入
#import wsa5.h
(wsdd需要wsa插件) - 加入
#import wsse.h
(因为有些操作需要进行安全验证)
二、利用soapcpp2生成源代码文件
./soapcpp2 -2 -C -I./import:./custom -j -x onvif.h
参数解释:
-2 在生成的源代码中全局使用SOAP1.2绑定
-C 只产生客户端代码
-I 指定onvif.h中import文件的寻找路径,多个路径用":"分隔开
-x 不产生示例xml文档
-j 这个选项使soapcpp2产生客户端的代理类和服务端的服务类代码,
三、将gsoap下载包中的插件代码拷贝到工程中
wsa插件需要: dom.cpp wsaapi.c wsaapi.h
wsse插件需要:smdevp.c smdevp.h mecevp.c mecevp.h threads.c threads.h struct_timeval.c struct_timeval.h
四、实现自己的代码部分
这部分就是写一个自己想要实现的功能,比如使用wsdd中的设备探测功能发现当前网段下支持ONVIF的设备。这部分就属于自定义了。
四、编译
对每个文件的编译选项
-W -Wall -g -fPIC -DWITH_OPENSSL -DWITH_DOM -DWITH_GZIP -lssl -lcrypto -lz
编译选项解释:
-W 只显示编译器认为会出现错误的警告
-Wall 显示所有警告
(补充 -w是关闭所有警告)
-g 在编译阶段产生调试信息
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。
-DWITH_NONAMESPACES 如果在编译时定义了此宏(默认情况下未定义),则消除了对全局名称空间表的依赖,而是用户必须通过调用soap_set_namespaces显式设置XML名称空间。对于soapcpp2 -i或者-j参数产生的代理对象,在创建时会自动分配一个命名空间,仍可以不使用soap_set_namespaces
-DWITH_OPENSSL 当这个宏在编译的时候被定义,为HTTPS或者WS-Security启用与Openssl的链接
-DWITH_DOM 如果在编译时定义了此宏(默认情况下未定义),则启用WS-Security签名验证和XML重新规范化,即,在将gSOAP WSSE插件用于WS-Security时必须定义此宏。
-DWITH_GZIP 如果在编译时定义了此宏(默认情况下未定义),则在运行时启用SOAP_ENC_ZLIB模式标志时,启用与zlib库的链接以进行HTTP消息压缩(使用compress和gzip方法)。
将每个文件用g++ 加上上述的编译选项编译成对象文件,然后再链接到一起就ok啦。
wsdl2h参数
-b 选项可确保为需要客户端回调服务的双工通信添加单向响应消息操作。
soapcpp2 参数
-a
-A
POST /WebServices/WeatherWebService.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.3603)
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://WebXml.com.cn/getSupportCity"
Host: www.webxml.com.cn
Content-Length: 348
Expect: 100-continue
Connection: Keep-Alive
上面是一个webservice请求的http请求头,添加-a 参数生成的服务端代码在处理请求时,如果http的请求头中包含SOAPAction字段,根据该字段调用服务端代码,如果该字段不存在,按照请求体中的请求消息名称调用服务端代码。添加-A参数生成的服务端代码在处理请求时,要求使用http请求头中的SOAPAction调用服务端代码。
XML DOM API:
使用c++ API时,需要以下文件。
stdsoap2.h: gsoap engine
stdsoap2.cpp: gsoap engine
dom.cpp: DOM 解释器和DOM C/C++ API实现
dom.h: 头文件,不要在代码中#include该文件