ww哦步骤:
第一步:生成头文件
webservice接口一般会有一个对外接口文档。比如:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?WSDL
问号后面的参数表示WSDL文档,是一个XML文档,看不懂配置没关系,接下来我们要通过这个文档生成c++头文件。
(1) 下载gsoap工具
下载连接:gSOAP Toolkit download | SourceForge.net
下载完成之后解压,进入gsoap\bin\win32目录下,该目录下有两个文件wsdl2h.exe和soapcpp2.exe。wsdl2h.exe用来生成头文件。光生成头文件是不够的,这个时候要用soapcpp2.exe来生成对应的c++文件结构,可以用于项目中调用(第二步介绍)。先来看看怎么生成头文件。

在gsoap\bin\win32目录下打开cmd命令。输入:
javascript
wsdl2h.exe -o head.h http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?WSDL

执行完成之后可以看到当前目录下多了一个head.h的头文件。可以打开看看,里面都是一些接口函数。

注意:由webservice在传输过程中默认使用UTF-8编码,当然gsoap在生成头文件的时候默认也是窄字符。比如string或者char*。此时如果再调用过程中参数有中文就会乱码。解决方案有两个。
方法一:在后面调用的时候在代码中加一句(具体加在哪里后面介绍):
javascript
soap_set_mode(&m_soap, SOAP_C_UTFSTRING);
方法二:由于默认使用根目录下的typemap.dat进行编译成窄字符。此时我们不适用它,而是在当前目录下新建一个mytypemap.dat。内容如下:xsd__string = | std::wstring | wchar_t* 。然后重新执行cmd命令。
javascript
wdsl2h.exe -o head.h -t mytypemap.dat http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?WSDL
重新生成之后可以发现head.h中字符串类型都变成了wchar_t * 或者wstring类型。
第二步:生成可调用API
在当前目录下执行cmd命令:
javascript
soapcpp2.exe -C -x -I ..\..\import head.h

其中-C是只生成客户端代码。-x表示不生成xml(我们只需要c++代码)-I 是指定import目录。
执行成功之后,当前目录下多了一些文件:

第三步:导入到项目
将上面图片中红色框中的文件添加到你的项目中。另外还有需要添加两个文件。gsoap根目录下的stdsoap2.h和stdsoap2.cpp也需要添加到项目中。
对添加进去的3个cpp文件右击->属性->所有配置->c/c+±>预编译头,选择不使用预编译头。如下如所示。
配置完成之后,就可以开始写代码了。
第四步:编写代码
新建一个基于MFC应用程序工程,名称为:soapTest
新建一个soapTestDlg.cpp,引入头文件
javascript
#include "WeatherWebServiceSoap.nsmap"
#include "soap.h"
我在项目中引用这两个头文件的时候,编译疯狂报错。仔细检查看了下,全是冲定义,原因是头文件 WeatherWebServiceSoap.nsmap中命名空间和项目中原有的socket库冲突了,解决方法是将#include "WeatherWebServiceSoap.nsmap"写到最前面,如果使用了预编译头,最好写在 stdafx.h文件中。头文件引入之后,再次编译,不报错了。如果不添加预编译头,则需要设置soapC.cpp、soapClientLib.cpp、soapClient.cpp、stdsoap2.cpp为不使用预编译头。

在soapClient.cpp中可以看到服务的所有接口。函数名是soap_call __ns1_XXX形式。
首先创建soap对象并初始化:
javascript
struct soap m_soap;
//SOAP初始化
soap_init(&m_soap);
soap_set_mode(&m_oSoap, SOAP_C_UTFSTRING);
其中soap_set_mode(&m_oSoap, SOAP_C_UTFSTRING);
正是一开始说的为了适配中文字符。如果一开始使用了wstring或者wchar_t * 则可以忽略。然后定义reqXml字符串,调用接口函数。xml字符串中双引号需要转义。
javascript
char* _reqXml = "<root>.....</root>";
char* _Return;
soap_call_ns1__XXX(&m_soap, NULL, NULL, G2U(_reqXml), _Return);
string ret = U2G(_Return);
由于上面代码中使用的是窄字符作为参数,即char *,需要转换成UTF-8格式。具体函数如下:
javascript
char* U2G(const char* utf8)
{
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
if (wstr) delete[] wstr;
return str;
}
char* G2U(const char* gb2312)
{
int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);
len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
if (wstr) delete[] wstr;
return str;
}
此时编译应该可以通过了。但是,现实是残酷的。返回的_Return值为NULL,说明出错了。我的地址是https的,如果,我直接用http请求的话会返回301,查找资料说301表示重定向,意思是这里不能像浏览器那样地址栏输入http,浏览器会帮你重定向到https。此时还是得改成https。再次编译。 额~还是报错。。。错误码可以在调试期间,查看m_soap结构体中error字段的值,如果一直是0,说明没问题。具体错误代码可以上网查一下。
改成https再调试可以看到m_soap->error的值是30,查看文档,发现30代表没有进行SSL安全认证。好吧~
在soap_init(&m_soap)后面添加如下代码:
javascript
soap_ssl_init();
if (soap_ssl_server_context(&m_soap, SOAP_SSL_NO_AUTHENTICATION, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
soap_print_fault(&m_soap, stderr);
exit(-1);
}
其中soap_ssl_server_context参数中如果有SSL证书,和密码就填进去。没有全NULL就完事儿。
再次编译,还是无法通过~检查代码,发现在stdsoap2.h
头文件中WITH_OPENSSL没有定义。下面是灰色的。

解决方法:右击项目属性->C/C+±>预处理器->预处理器定义中添加WITH_OPENSSL

添加之后,发现灰色没有了

编译之后再次报错,错误信息是soap_ssl_server_context
函数无法解析的外部符号。
原因
该函数的实现没有定义。忘记引入openssl库了。下载地址:https://slproweb.com/products/Win32OpenSSL.html

下载完成,安装,安装完成目录。

接着在vs中右击项目->属性->配置属性->VC++目录中包含目录和库目录添加进去,如下图。

在vs中右击项目->属性->配置属性->链接库->输入->附加依赖库项中把libcrypto.lib和libssl.lib添加进去,如下图

文件结构

注意:
如果报xx已经在 soapClient.obj 中定义的错误,如下图

解决方法:
把soapClientLib.cpp的全部注释掉,再次编译即可

以下是gsoap+opensll工具