首先声明:文章中的内容大都来源于网络,做的过程遇到了不少问题,所以想写下此文,记录一个完整的过程,以飨后人。
gSOAP是一个夸平台的,用于开发Web Service服务端和客户端的工具,在Windows、Linux、MAC OS和UNIX下使用C和C++语言编码,集合了SSL功能。
下载地址:http://sourceforge.net/projects/gsoap2
官方网站:http://genivia.com/Products/gsoap/index.html
1、配置gSOAP
下载gSOAP,解压后在gsoap\bin\win32里找到wsdl2h.exe和soapcpp2.exe程序。通过这两个程序可以生成客户端需要的C/C++文件。这两个程序的使用方法:
wsdl2h.exe: 编译wsdl文件生成c/c++头文件
-o 文件名,指定输出头文件
-n 名空间前缀 代替默认的ns
-c 产生纯C代码,否则是C++代码
-s 不要使用STL代码
-t 文件名,指定type map文件,默认为typemap.dat
-e 禁止为enum成员加上名空间前缀
soapcpp2.exe: gSOAP编译器,编译头文件生成服务器和客户端都需要的c/c++文件
(如果使用STL,需要从压缩包里找到stlvector.h放到soapcpp2.exe目录下,否则运行失败)
-C 仅生成客户端代码
-S 仅生成服务器端代码
-L 不要产生soapClientLib.c和soapServerLib.c文件
-c 产生纯C代码,否则是C++代码(与头文件有关)
-I 指定import路径(见上文)
-x 不要产生XML示例文件
-i 生成C++包装,客户端为xxxxProxy.h(.cpp),服务器端为xxxxService.h(.cpp)
2、创建VC工程,把生成的文件拷到工程目录
把“\gsoap-2.8\gsoap”目录下找到 stdsoap2.cpp 和stdsoap2.h 也复制到工程中去;设置新加的所有.cpp文件不要使用预编译头。
3、调用Web服务:
#include "stdafx.h"
#include "soapH.h"
#include "soapClientServiceSoapBindingProxy.h"
#include "ClientServiceSoapBinding.nsmp"
int _tmain(int argc,_TCHAR* argv[])
{
const char* server="http://XXXXX:81/Service1.svc";
4、解决415错误:
这个是由于服务端客户端soap协议不统一造成的,我使用的gsoap是2.8.8版本,编译出来的xxx.nsmap中soap协议是1.2版本,连接的wcf服务是1.1版本。
目前这个问题有两个方法可以解决,1,手工修改nsmap文件中的协议,2,使用gsoap编译器“-1”编译选项(此方法未测试,据说有些时候也无效)
//手动修改namespace, 生成soap1.1.
struct
Namespace namespaces[] =
{
{
"SOAP-ENV"
,
"http://schemas.xmlsoap.org/soap/envelope/"
, NULL, NULL},
{
"SOAP-ENC"
,
"http://schemas.xmlsoap.org/soap/encoding/"
, NULL, NULL},
{
"xsi"
,
"http://www.w3.org/2001/XMLSchema-instance"
, NULL, NULL},
{
"xsd"
,
"http://www.w3.org/2001/XMLSchema"
, NULL, NULL},
//{"SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope", "http://www.w3.org/2003/05/soap-envelope", NULL},
//{"SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding", "http://www.w3.org/2003/05/soap-encoding", NULL},
//{"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL},
//{"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL},
{
"ns4"
,
"http://schemas.datacontract.org/2004/07/SnailGame.Toolkits.MessageSendService"
, NULL, NULL},
{
"ns3"
,
"http://schemas.microsoft.com/2003/10/Serialization/"
, NULL, NULL},
{
"ns1"
,
"http://tempuri.org/"
, NULL, NULL},
{NULL, NULL, NULL, NULL}
};
|
5、解决中午乱码的问题:
通过上面的步骤,已经可以成功连接了,但是在遇到中文字符的时候,问题就出现了。
5.1中文字符返回值
这个解决的办法主要就是要把字符转换成utf-8
1>设置GSOAP的编码模式 soap_set_mode(soap, SOAP_C_UTFSTRING);
2>将传输回来的字符流进行字符转换
//ns5__SimpleNetObject是WCF中定义的结构体,返回值是此类型<br> //mapServiceResponse用来接收返回值,参看前面的addResponse就明白了
ns5__SimpleNetObject d = *mapServiceResponse.PublishMapServiceResult;
std::wstring str;
std::string stt = d.Message;
//转换为utf-8编码
bool
fl = WFromUTF8(
reinterpret_cast
<
const
char
* >(stt.c_str()), str);
CString s = str.c_str();
|
3>字符转换函数
#if !defined(TEXTCONVERT_H)
#define TEXTCONVERT_H
//多字节字符串转换为宽字节字符串
// CodePage:[in]编码
// lpcwszText:[in]多字节字符串
// lppszVal:[out]宽字节字符串
//返回值:TRUE表示成功,其它表示失败
inline
BOOL
WcharFromChar(
UINT
CodePage,
const
char
*lpcszText,std::wstring &wstrVal)
{
WCHAR
*lpwszVal( NULL );
int
nLength(0);
size_t
dwSize(0);
nLength = ::MultiByteToWideChar( CodePage, 0, lpcszText, -1 , NULL, 0);
if
( nLength <= 0 )
return
FALSE;dwSize = nLength *
sizeof
(
WCHAR
);
lpwszVal = (
WCHAR
*)
malloc
( dwSize );
if
( NULL == lpwszVal )
return
FALSE;
memset
(lpwszVal,0x0,dwSize);
nLength = ::MultiByteToWideChar( CodePage, 0, lpcszText, -1 , lpwszVal, nLength );
if
( nLength <= 0 )
{
free
(lpwszVal);
lpwszVal = NULL;
return
FALSE;
}
wstrVal.assign( lpwszVal );
free
(lpwszVal);
lpwszVal = NULL;
return
TRUE;
}
//UTF-8字符串转换为宽字节字符串
// lpcwszText:[in]UTF-8字符串
// lppszVal:[out]宽字节字符串
//返回值:TRUE表示成功,其它表示失败
inline
BOOL
WFromUTF8(
const
char
*lpcszText,std::wstring &wstrVal )
{
return
WcharFromChar(CP_UTF8,lpcszText,wstrVal);
}
#endif
|
5.2中文字符参数
中文字符返回值的问题解决了,新的问题又出现了,如果按照上面的设置,如果是有中文字符的参数,这个参数就会乱码了,真是一波刚平一波又起啊。
有人提出了用setloacal的方法来解决,不过这个办法我使用的时候无效。
返回值乱码,我们是将其从转换成utf-8的编码方式,那么,我们传人的参数有问题,在传人参数之前,先将其转换成utf-8是不是就可以呢。(当前是unicode,大概等价于utf-16,未查证)
如果是linux系统,貌似有直接的装换函数,可惜我是在windows平台,那就自己写吧,有了上面的转换方法,照猫画虎,也就是那样。
//字符编码转换
std::string Convert(std::wstring str,
int
targetCodepage)
{
BYTE
* pTargetData = NULL;
size_t
souceLen=WideCharToMultiByte(targetCodepage,0,(
LPWSTR
)str.c_str(),-1,(
char
*)pTargetData,0,NULL,NULL);
pTargetData=
new
BYTE
[souceLen+1];
memset
(pTargetData,0,souceLen+1);
WideCharToMultiByte(targetCodepage,0,(
LPWSTR
)str.c_str(),-1,
(
char
*)pTargetData,souceLen,NULL,NULL);
std::string rt((
char
*)pTargetData);
delete
[] pTargetData;
return
rt;
}
|
调用方法
CString str = _T("测试中文");
std::wstring ss = str;
std::string var =Convert(ss,CP_UTF8);