gSoap使用上的一些问题汇总(当前共7个问题,最后更新2013-05-10)
2011-06-29 14:05:31| 分类: gSoap | 标签: |举报 |字号大中小 订阅
相关下载:
下载: time_ce.rar
1、中文乱码问题,可以使用宽字符。具体办法,在wsdl2h转换时使用t参数指定一个映射文件,或者不指定t参数而直接使用默认的映射文件typemap.dat。
例如内容为xsd__string = | std::wstring
2、时间问题1,在CE平台上,有些函数是不支持的,例如gmtime,localtime,mktime等。而对于描述文件中的时间处理,要用到这些函数,在编译时就会碰到 error C3861: “soap_outdateTime”: 找不到标识符等错误,因为这些函数没有包含进来,而修改soapC.cpp文件将这些函数引入后,又会碰到gmtime等函数不支持。解决办法是使用网上免费提供了一套为CE定制的时间函数,网址为:http://www.opennetcf.com,由于此网站现在有所改变,因此将原来下载的时间函数提供在上面,供博友们使用
3、时间问题2,当接收的报文中的时间类似为2010-11-01T00:00:00+08:00这样的格式时
由于东八区会将时间减8小时,而从天借一天变成24小时再减,天正好是1号,减了后变成了0,然后调用其他时间处理函数时就会出问题。
出现的代码为:
stdsoap2.cpp 12302行
soap_s2dateTime函数中
if (*s)
{
#ifndef WITH_NOZONE
if (*s == ‘+’ || s == ‘-’)
{ int h = 0, m = 0;
if (s[3] == ‘:’)
{ / +hh:mm /
sscanf(s, “%d:%d”, &h, &m);
if (h < 0)
m = -m;
}
else / +hhmm /
{ m = (int)soap_strtol(s, NULL, 10);
h = m / 100;
m = m % 100;
}
T.tm_min -= m;
T.tm_hour -= h;
/ put hour and min in range /
T.tm_hour += T.tm_min / 60;
T.tm_min %= 60;
if (T.tm_min < 0)
{ T.tm_min += 60;
T.tm_hour–;
}
T.tm_mday += T.tm_hour / 24;
T.tm_hour %= 24;
if (T.tm_hour < 0)
{ T.tm_hour += 24;
T.tm_mday–;
}
/ note: day of the month may be out of range, timegm() handles it */
}
#endif
*p = soap_timegm(&T);
}
解决办法:
1、定义宏WITH_NOZONE,跳过对于时区的处理
2、若一定要使用时区可以增加对于day为0的处理代码
参考代码(来自gsoap中的一个补丁文件)
bool leapYear = ((T.tm_year % 4 == 0 && T.tm_year % 100 != 0) || T.tm_year % 400 == 0);
if (T.tm_mday == 0)
{
T.tm_mon–;
if (T.tm_mon < 0)
{
T.tm_mon += 12;
T.tm_year–;
}
if (T.tm_mon == 0 || T.tm_mon == 2 || T.tm_mon == 4 || T.tm_mon == 6
|| T.tm_mon == 7 || T.tm_mon == 9 || T.tm_mon == 11)
{
T.tm_mday = 31;
}
else if (T.tm_mon == 3 || T.tm_mon == 5 || T.tm_mon == 8 || T.tm_mon == 10)
{
T.tm_mday = 30;
}
else if (T.tm_mon == 1 && leapYear)
{
T.tm_mday = 29;
}
else
T.tm_mday = 28;
}
if (T.tm_mday == 32 &&
(T.tm_mon == 0 || T.tm_mon == 2 || T.tm_mon == 4 || T.tm_mon == 6
|| T.tm_mon == 7 || T.tm_mon == 9 || T.tm_mon == 11))
{
T.tm_mday = 1;
T.tm_mon++;
}
else if (T.tm_mday == 31 &&
(T.tm_mon == 3 || T.tm_mon == 5 || T.tm_mon == 8 || T.tm_mon == 10))
{
T.tm_mday = 1;
T.tm_mon++;
}
else if (T.tm_mon == 1 && T.tm_mday == 30 && leapYear)
{
T.tm_mday = 1;
T.tm_mon++;
}
else if (T.tm_mon == 1 && T.tm_mday == 29 && !leapYear)
{
T.tm_mday = 1;
T.tm_mon++;
}
if (T.tm_mon == 12)
{
T.tm_mon = 0;
T.tm_year++;
}
参考:在gsoap的版本历史说明中,有如下一段:
gSOAP 2.7.12: dateTime numeric timezone normalization issue when converting to XSD dateTime with numeric timezone (+hh:mm) to time_t (e.g. 2000-03-04T02:00:00+03:00). Patch in stdsoap2.cpp line 10886:
/* put hour and min in range /
T.tm_hour += T.tm_min / 60;
T.tm_min %= 60;
if (T.tm_min < 0)
{ T.tm_min += 60;
T.tm_hour–;
}
T.tm_mday += T.tm_hour / 24;
T.tm_hour %= 24;
if (T.tm_hour < 0)
{ T.tm_hour += 24;
T.tm_mday–;
}
/ note: day of the month may be out of range, timegm() handles it */
似乎是在版本2.7.12中就解决了这个问题,就是由timegm来处理这个问题,但我实在是没有找到timegm是怎么处理这个问题的。
4、实现简单调试
How do I debug a SOAP client?
Compile stdsoap2.cpp (or stdsoap2.c for C) with compiler option -DDEBUG (/DDEBUG in MSVC++) to enable the DEBUG macro. You can also uncomment “#define DEBUG” in stdsoap2.h and recompile stdsoap2.cpp/stdsoap2.c. When you run your application, three files are created in the current directory: SENT.log, RECV.log, and TEST.log. All output messages are appended to SENT.log, all input messages are appended to RECV.log, and TEST.log contains various logging messages generated by the gSOAP runtime environment. Alternatively, you can use the example plugin code provided in the distribution package in extras/plugin.h to develop an application that can selectively log input and output messages. In addition, you can use a simple trick to inspect a client request message by leaving the endpoint URL empty (“” string). This will send the request message to stdout. The client application expect the response to be available from stdin. So, unless you provide a response message using redirect e.g. from a file or a service application using Unix pipes, you will have to terminate the client application
经过测试,取消注释掉stdsoap2.h 中的#define DEBUG
重新编译,然后运行确实在运行目录下生成了3个文件:SENT.log,RECV.log,TEST.log
在PDA等上使用时,需要直接定义#define DEBUG
因为此DEBUG宏定义在其他宏中间,如下:
#ifndef WITH_LEAN
ifdef DEBUG
ifndef SOAP_DEBUG
define SOAP_DEBUG
endif
ifndef SOAP_MEM_DEBUG
define SOAP_MEM_DEBUG
endif
endif
#endif
但生成的日志文件中没有时标,修改如下代码可以生成带时标的日志:
以下在stdsoap2.h(2.7.15版本)文件1222行,其他版本可以搜索DBGLOG
#ifdef SOAP_DEBUG
ifndef SOAP_MESSAGE
define SOAP_MESSAGE fprintf
endif
ifndef DBGLOG
define DBGLOG(DBGFILE, CMD) \
{ if (soap)
{ if (!soap->fdebug[SOAP_INDEX_##DBGFILE])
soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);
if (soap->fdebug[SOAP_INDEX_##DBGFILE])
{ FILE *fdebug = soap->fdebug[SOAP_INDEX_##DBGFILE];
DWORD dwCount = GetTickCount();
SYSTEMTIME tTime;
GetLocalTime(&tTime);
fprintf(fdebug,“%02d:%02d:%02d.%03d”,tTime.wHour,tTime.wMinute,tTime.wSecond,dwCount%1000);
CMD;
fflush(fdebug);
}
}
}
继续修改可以增加源代码和行数
{ if (soap)
{ if (!soap->fdebug[SOAP_INDEX_##DBGFILE])
soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);
if (soap->fdebug[SOAP_INDEX_##DBGFILE])
{ FILE fdebug = soap->fdebug[SOAP_INDEX_##DBGFILE];
DWORD dwCount = GetTickCount();
SYSTEMTIME tTime;
GetLocalTime(&tTime);
fprintf(fdebug,“%02d:%02d:%02d.%03d”,tTime.wHour,tTime.wMinute,tTime.wSecond,dwCount%1000);
fprintf(fdebug,“–%s:%d–”,FILE,LINE);\
CMD;
fflush(fdebug);
}
}
}
4、例子mtom-stream和dime在linux上无法传输文件的问题
在win下,这两个例子都能成功,而在linux下,则无法将实现文件传输。经过仔细跟踪源代码,发现了问题如下:
stdsoap2.c,cpp文件中
soap_copy函数调用
soap_copy_context函数的
soap_set_namespaces(copy, soap->local_namespaces);调用应该修改为
soap_set_namespaces(copy, soap->namespaces);
因为默认情况下soap->local_namespaces为空,而soap->namespaces是有值的
此处调用显然是需要将copy的namespaces置上值,如果用soap->local_namespaces实际就将
copy->namespaces的置为空了,后面的判断就会出错.
当然soap_set_namespaces函数中调用soap_set_local_namespaces(soap);才是真正为local_namespace赋值的
由于在win下没有使用线程调用,因此此问题没有暴露出来,在LINUX下,由于使用线程处理每个连接,因此首先对soap进行了拷贝
原代码如下:
tsoap = soap_copy(&soap);
pthread_create(&tid, NULL, (void()(void))process_request, (void*)tsoap);
线程中使用的tsoap和soap内容不一致,导致出问题。
当前gsoap版本为2.8.3,此bug尚未修正。
另:为了实现在不同机器上调试客户端和服务端的通讯,可以修改mtom-stream-test.c的
const char *endpoint = “http://localhost:8085”;
为需要的ip地址和端口号
5、base64解码bug
使用gSaop的soap_base642s将base64编码的字符串转换为一般字符串时,其中bug导致解码出的字符串会多出一个乱码。
主要问题就是没有考虑字符串最后结尾0的问题,但不是每个解码都会出问题,因为是4个字符转换为3个,因此算出的长度有时是够用的,这个bug在字符串为某些长度时暴露出来,例如160长。
解决办法:将l = (strlen(s) + 3) / 4 * 3;修改为l = (strlen(s) + 3) / 4 * 3+1;即可以解决问题。
特地上官网看了一下,下载了2.8.8,这个问题已经解决,就是按以上修改的,但2.8.3还未改,后检查了2.8.4,2.8.5,2.8.6,2.8.7,在2.8.4就修改了
6、SOAPACTION的问题
调试发送接收时不成功,检查发送回的报文,错误为版本不匹配,用soapui发送一样的报文就是成功的。
后来发现是namespace和soapui不同。
soapui发的报文中:soap:Envelope xmlns:soap=“http://www.w3.org/2003/05/soap-envelope”
gsoap发的报文中:SOAP-ENV:Envelope xmlns:SOAP-ENV=“http://schemas.xmlsoap.org/soap/envelope/”
这是经过查找,原来是soap1.2规范和1.1的区别,gsoap发的是1.1的,soapui发的是1.2的
原来是服务端不向下兼容,只能收1.2规范的报文。
经过查看gsoap的文档,在9.2有描述,原来默认情况下gsoap用的是1.1方式发送
9.2 SOAP 1.1 Versus SOAP 1.2 and Dynamic Switching
但可以接收1.1和1.2的,要修改为默认用1.2发送
需要在wsdl2h产生的.h文件中增加
#import “soap12.h”
因为调用soapcpp2已经指定了路径,因此这里不用指出路径
然后在调用soapcpp2加一个-2参数,重新生成需要的文件,即默认以1.2的方式命名空间发送报文,同时可以接收1.1或1.2的报文
7、收发文件问题
用MTOM收发文件时,刚开始碰到无法收发,最后发现是命名空间问题,有两个命名空间必须添加到文件中,如下:
{“xop”, “http://www.w3.org/2004/08/xop/include”, NULL, NULL},
{“xmime5”, “http://www.w3.org/2005/05/xmlmime”, “http://www.w3.org/2004/11/xmlmime”, NULL},
加的位置为生成的代理文件soapXXXXServicePortBindingProxy.cpp中
void soapXXXXServicePortBindingProxy::soapXXXXServicePortBindingProxy_init(soap_mode imode, soap_mode omode)
{ soap_imode(this, imode);
soap_omode(this, omode);
soap_endpoint = NULL;
static const struct Namespace namespaces[] =
{
{“SOAP-ENV”, “http://schemas.xmlsoap.org/soap/envelope/”, “http://www.w3.org//soap-envelope", NULL},
{“SOAP-ENC”, “http://schemas.xmlsoap.org/soap/encoding/”, "http://www.w3.org//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},
{“xop”, “http://www.w3.org/2004/08/xop/include”, NULL, NULL},
{“xmime5”, “http://www.w3.org/2005/05/xmlmime”, “http://www.w3.org/2004/11/xmlmime”, NULL},
{NULL, NULL, NULL, NULL}
};
soap_set_namespaces(this, namespaces);
}