大家好,很久没有写相关的技术文章了,最近一直考虑一个问题,就是对于一个老C++程序员来说,后续路程如何走下去:该学写什么,了解写什么?想了想,突然注意到最近公司接到的客户需求:将监控行业相关数据以webservice接口形式提供给外部调用。之前在了解onvif协议的时候,顺便了解过gsoap工具和soap协议,但是不深也未曾亲自实现过,想想在北京的时候一个同事在我面前提过webservice,说功能如何如何强大,自己想做一个基于webservice的服务来着,当然我不熟悉C#,也不知道C#编写webservice有多简单,之前也做过几年Java,由于这两种语言都可以做web相关技术,对于Java和C#而言照道理讲应该比较容易,但是从事C++这么多年可能由于工作内容原因,一直没有使用到webservice相关的技术,这回,基于该需求对C++提供webservice服务做进一步调研:
其实,在网络上已经以Webserice形式提供了很多类型的接口,例如获取网络时间、获取天气、获取手机归属地等等,可见webservice应用已经非常广泛,特别是在网络发展飞速的今天,它是一个跨平台的、可扩展的RPC应用,所以学习它非常实用,说不定哪天你就需要将服务其功能转为webservice接口对接其他系统或平台,这时,这技术就用的上了。
GSoap是一个开源的工具,功能非常强大,各位可以到网络上搜索学习看看这个不错的工具,gSOAP编译工具提供了一个SOAP/XML关于C/C++语言的实现,从而让C/C++语言开发web服务或客户端程序的工作变得轻松了很多。绝大多数的C++web服务工具包提供一组API函数类库来处理特定的SOAP数据结构,这样就使得用户必须改变程序结构来适应相关的类库。这里我主要通过C/S模式调用GSOAP来实现服务端和客户端。首先我的目标是提供一组简单数学的接口:加减乘除四个接口。
在使用GSOAP提供几个接口之前,首先带大家熟悉一下Windows下GSOAP里面携带的两个工具:wsdl2h.exe以及soapcpp2.exe,两工具可以在下载的gsoap2.8.29(下载可以到开源社区去下载:http://sourceforge.net/projects/gsoap2)的gsoap/bin/win32目录找到,当然这是gsoap在windows提供的两个可直接的工具,下面我们先来认识一下这两个简单易用的工具:
wsdl2h工具: 正如其名wsdl到h,也就是webservice description language(.wsdl文件)转换成.h的头文件工具,换句话说就是用来转为.h的工具,这里我们不介绍wsdl文件如何编写,也不介绍wsdl转h功能,因为我们自己已经写好的.h文件,所以用不到,如果想了解,请到onvif官网下载相关wsdl然后用这个工具试试!具体功能如下:
(1)、它仅仅负责生成wsdl中描述的相应的头文件,这些头文件不能直接使用,需要经过soapcpp2.exe转换为gsoap生成的接口后才能使用
(2)、输入条件为一个或多个wsdl或xsd文件或者相应的URL路径,如果输入wsdl或xsd则输出为第一个名称的.h文件,如果为URL则默认为标准输出
携带参数:
-s: don't generate STL code 也就是不使用标准模板库,如果不使用-s参数那么默认使用STL库,也就是必须将以来的文件./gsoap/stdsoap2.h和stdsoap2.cpp拷贝至应用的程序当中。
-c: generate C source code c意思是生成c文件接口,默认是c++头文件
-o:output to file 指定输出文件名称,可以重命名生成的文件名称
-t:use type map file instead of the default file typemap.dat 使用类型定义文件,可以在typemap.dat(自带的)中修改生成选项,生成符合要求的头文件,如编码
当然,该用具还有其他n个参数使用,此处仅仅介绍几个常用的,其他控制参数自己去了解学习。
soapcpp2.exe:该工具功能是将我们提供或有wsdl2h生成的.h的头文件(接口)转换为gsoap提供的形式的接口和rpc代理相关的代码和框架(当然我没有具体去了解,vs中你可以搜索到携带相应接口名的其他代理方法并且有相应的实现)
相关参数如下:
(1)、-1生成SOAP1.1协议的代码
(2)、-2生成SOAP1.2协议的代码
(3)、-C只是生成客户端相关的代码
(4)、-S只是生成服务端相关的代码
(5)、-i generate C++ service proxies and objects inherited from soap struct 生成对象为soap的子类,会影响生成文件的使用方法。如 class SOAP_CMAC BasicCMIRPService : public soap
其他参数此处不再介绍,如需要更详细解释,请到官网查询相应工具使用介绍。
好,学习两个工具之后,我们就可以将以上工具派上用场了,当然我们这里不需要wsdl2h工具,因为我们是C++程序员,头文件我们自己编写提供即可,不需要编写wsdl在转为.h文件,省去了不少麻烦,我们直接使用soapcpp2工具将头文件转对应的头即可,首先服务端提供加减乘除4个接口,客户端调用加减乘除4个接口,约定接口如下:
int Add(int nNum1, int nNum2, int* pResult);
int Sub(int nNum1, int nNum2, int* pResult);
int Mul(int nNum1, int nNum2, int* pResult);
int Div(int nNum1, int nNum2, int* pResult);
返回值我们以参数地址形式提供出去而不是以函数返回值形式提供跟gsoap生成接口有关,它生成接口调用的时候返回值表示传入参数是否正确或调用成功用到了返回值,所以我们自己的接口为了不覆盖它的使用得这么写,当然,形式变化而已,无所谓!既然客户端和服务器都以改4接口为通信接口,那么这4个接口就作为我们要转换为gsoap代理或框架时soapcpp2用到的头文件的声明,新建服务端程序,新建win32工程项目:GSoapServer空项目,添加类GSoapServer,然后删除该类所有声明和实现(目的就是用GSaopServer.h和GSaopServer.cpp两个配对文件,不需要相应的类),在GSoapServer.h中声明以上几个函数:
#pragma once
int Add(int nNum1, int nNum2, int* pResult);
int Sub(int nNum1, int nNum2, int* pResult);
int Mul(int nNum1, int nNum2, int* pResult);
int Div(int nNum1, int nNum2, int* pResult);
在GSoapServer.cpp中添加main函数
#include "GSoapServer.h"
int main(int argc, char** argv)
{
return 0;
}
编译该项目,保证编译通过无错误,接下来见gsoap2.8工具目录下的./gsoap/bin/win32/gsoapcpp2.exe 拷贝至与GSoapServer.h相同目录下,使用命令行cmd,进入该目录
如:
键入:cd E:/project/C++/test/GSoapServer/GSoapServer
键入:E:
使用soapcpp2.exe: soapcpp2.exe GSoapServer.h
此时,由于我们没有指定-C或-S,它会为我们生成客户端和服务端的所有代码,如果只是生成客户端代码添加-C参数,此时会生成soapH.c、soapC.cpp、soapStub.h、soapClient.cpp soapClientLib.cpp代码(加上我们以来的stl库stdsoap2.h和stdsoap2.cpp供7个文件,当然包含被转的GSoapServer.h文件就是8个,此处生成后要不要GSaopServer.h已经无所谓了);如果只想生成服务端的代码,那么添加-S参数,此时会生成soapH.c、soapC.cpp、soapStub.h、soapServer.cpp soapServerLib.cpp以及soap.nsmap文件,也就是8个文件(不计算GsopServer.h被转换的头文件)
其中soap.h和soapC.cpp是具体的实现,soapStub.h为代理的声明(服务端我们需要实现声明的4个接口-该四个接口和我们约定的不太一样,第一个参数多了
soap*对象指针)
对于服务端,我们将soap.h soapC.cpp soapStub.h soapServer.cpp以及依赖的stl库stdsoap2.h和stdsoap2.cpp 五个文件添加的项目中(soapServerLib.cpp不需要,否则会出错),添加后所有的cpp属性设置预编译头为“不使用预编译头”,最后找到soapStub.h文件最后几行会发现我们约定的即可接口声明(接口稍微有变化,多了soap*参数),拷贝到GSoapServer.cpp工程目录的main函数之前,并且实现之:
/** Web service operation 'Add' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Add(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 + nNum2;
return SOAP_OK;
}
/** Web service operation 'Sub' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Sub(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 - nNum2;
return SOAP_OK;
}
/** Web service operation 'Mul' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Mul(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 * nNum2;
return SOAP_OK;
}
/** Web service operation 'Div' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Div(struct soap*, int nNum1, int nNum2, int *pResult)
{
if(0 == nNum2)
{
*pResult = 0;
return -1;
}
*pResult = nNum1 / nNum2;
return SOAP_OK;
}
编译,发现没有错误,如果有预编译错误,设置cpp属性去掉预编译头即可,切记不需要soapServerLib.cpp生成的文件,soapServer.cpp是必要的,它实现了soap_serve服务,也就是我们服务器提供的服务!
具体的服务器main函数实现如下:
#include "GSoapServer.h"
#include “soap.nsmap” // 服务端与客户端必须包含的文件
/** Web service operation 'Add' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Add(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 + nNum2;
return SOAP_OK;
}
/** Web service operation 'Sub' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Sub(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 - nNum2;
return SOAP_OK;
}
/** Web service operation 'Mul' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Mul(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 * nNum2;
return SOAP_OK;
}
/** Web service operation 'Div' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Div(struct soap*, int nNum1, int nNum2, int *pResult)
{
if(0 == nNum2)
{
*pResult = 0;
return -1;
}
*pResult = nNum1 / nNum2;
return SOAP_OK;
}
int main(int argc, char**argv)
{
struct soap math_service;
soap_init(&math_service);
// bind端口返回SOCKET套接字-雷同socket套接口函数服务器监听过程
if(soap_bind(&math_service, NULL, 8686, 100))
{
soap_print_fault(&math_service, stderr);
return -1;
}
fprintf(stderr, "start math webservice ...\n");
// 当然这里可以用多个soap在多个现成中使用soap_new创建新的soap,每个线程一个
while(true)
{
int nClient = (int)soap_accept(&math_service);
if(nClient < 0)
{
soap_print_fault(&math_service, stderr);
return -1;
}
fprintf(stderr, "client[%d] connect success. ..\n, nClient);
soap_serve(&math_service);// provide service
soap_end(&math_service);// end service
}
return 0;
}
启动服务端程序,打开IE输入:http://localhost:8686/
会发现IE返回:
<SOAP-ENV:Fault 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">
<faultcode>at source</faultcode>
<faultstring>HTTP GET method not implemented</faultstring>
</SOAP-ENV:Fault>
说明,服务端启动成功!截图如下:
接下来写客户端程序:
客户端很简单,因为所有的文件我们已经用soapcpp2.exe工具生成了,新建GSoapClient工程,空工程或默认的win32控制台都可以,将服务端生成的文件soapH.h soapStub.h stdsoap2.h以及对应的源文件soapC.cpp soapClient.cpp stdsoap2.cpp 拷贝至客户端工程目录下并添加文件至项目中,然后设置cpp对应的属性,去掉预编译头文件,包含soap.nsmap文件,实现并调用webservice接口如下:
// GSoapClient.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdlib.h>
#include "soap.nsmap"
int _tmain(int argc, _TCHAR* argv[])
{
struct soap math_soap;
soap_init(&math_soap);
int nResult = 0;
const char* pWebService = "http://127.0.0.1:8686";
soap_call_Add(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60+30=%d\n", nResult);
soap_call_Sub(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60-30=%d\n", nResult);
soap_call_Mul(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60*30=%d\n", nResult);
soap_call_Div(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60/30=%d\n", nResult);
soap_end(&math_soap);
soap_done(&math_soap);
system("pause");
return 0;
}
工程文件列表如图下:
头文件包含注意:
编译生成客户端程序:
启动服务端:
启动客户端打印结果:
服务端打印(说明每一次webservice都对应一个连接不是?呵呵):
以上就是C++中使用gsoap工具完成webservice服务整个服务端以及客户端的实现过程,比较简单,这个功能简单使用,研究了很久,网络上很多文章写的有点乱,很难总结,总觉得要写一篇简单易懂,根据我的操作不会错误的文章,花了大概2-3个小时看资料、写Demon、截图写文章等等,虽然比较费时间,但想想,我能为各位网友出一份力,为互联网技术出一份力,苦了累了到无所谓了,感谢大家的支持,如果需要代码,记得给我送上分(作为我为大家的付出的回报,行不,下载后可以返回的,分数不会少,对你对我都有益,行动起来吧!谢谢观看~)
本人使用的gsoap2.8.29连接下载:http://download.csdn.net/detail/lixiang987654321/9466782
本人C++写的webservice接口Demon下载:http://download.csdn.net/detail/lixiang987654321/9466781