使用VC++使用开发Web服务(ISAPI extension - mod_gsoap.dll) 2

来源:互联网 发布:css3动画和js动画 编辑:程序博客网 时间:2024/05/01 04:20

在本例中使用gsoap中的ISAPI extension 模块(mod_gsoap.dll).

本例中运行环境为WinXP SP3(32) IIS5.1

本例中的开发工具为Microsoft Visual Studio 2008 SP1(32)中文版

gSoap安装目录为D:/gsoap-2.8

在上一节中我已经已经配置好gsoap运行环境,现在实现一个简单的时间服务.

1.     数据类型

定义数据类型,数据类型定义请参考

gSOAP 2.8.1 User Guide(http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc11.3.3) How to Represent Primitive C/C++ Types as XSD Type,下列为常用的数据类型:

typedef char *                                          xsd__anyURI                                      //表示统一资源标识符

typedef char *                                          xsd__base64Binary;                            //表示Base64编码的二进制数据

typedef bool                                            xsd__boolean;                             //表示BOOL类型

typedef char                                             xsd__byte;                                                //表示有符号字节(范围为-127~127)

typedef time_t                                          xsd__dateTime;                                  //表示日期和时间,ISO 8601扩展格式CCYY-MM-DDThh:mm:ss

                                                                                                                                                   //     CC表示世纪

                                                                                                                                                   //     YY表示年

                                                                                                                                                   //     MM表示月份

                                                                                                                                                   //     DD表示当前月的第几天,天数可以为负数,默认为正数,-03--9

                                                                                                                                                   //     T为日期时间分隔符

                                                                                                                                                   //     hh:mm:ss分别代表小时:分钟:

                                                                                                                                                   //     日期和时间范围为-2037年之间,否则time_t值为-1

typedef char *                                          xsd__dateTime;                                  //表示日期和时间,使用这个类型应由应用和读取和设置日期时间格式

typedef char *                                          xsd__date;                                                //表示日期

typedef double                                         xsd__decimal;                                     //表示任一精度的小数,建议使用double类型

typedef double                                         xsd__double;                                      //表示位双精度浮点数(IEEE )

typedef char *                                          xsd__duration;                                   //表示持续时间,ISO 8601扩展格式PnYn MnDTnH nMnS

                                                                                                                                                   //     nY表示年数

                                                                                                                                                   //     nM表示月数

                                                                                                                                                   //     nD表示天数                                                                                               

                                                                                                                                                   //     T为日期时间分隔符

                                                                                                                                                   //     nH表示小时数

                                                                                                                                                   //     nM表示分钟数

                                                                                                                                                   //     ns表示秒数

typedef float                                            xsd__float;                                                //表示位单精度浮点数(IEEE )

typedef char *                                          xsd__hexBinary;                                  //表示任意进制编码的二进制数据

typedef int                                                      xsd__int;                                                   //表示位整数,需要编译器支持

typedef long                                            xsd__int;                                                   //表示位整数,范围-2147483648

typedef long long                              xsd__integer;                                     //表示位整数

typedef long long                              xsd__long;                                                //表示位整数,范围-9223372036854775808

typedef LONG64                                xsd__long;                                                //表示位整数

typedef long long                              xsd__negativeInteger;                  //表示位整数

typedef unsigned long long  xsd__nonNegativeInteger;    //表示无符号位整数

typedef long long                              xsd__nonPositiveInteger;             //表示位整数

typedef char *                                          xsd__normalizedString;         //表示字符串,但字符串中不包含回车(#xD),换行(#xA)和制表符((#x9)

typedef unsigned long long  xsd__positiveInteger;                   //表示无符号位整数

typedef short                                           xsd__short;                                               //表示位整数,范围-32768

typedef char *                                          xsd__string;                                       //表示字符串

typedef wchar_t *                              xsd__string;                                       //表示字符串

typedef wchar_t *                              xsd__string_;                                      //表示字符串,同时使用char *wchar_t *类型字符串时,加一个下划线表示wchar_t *

typedef char *                                          xsd__time;                                                //表示时间hh:mm:ss.sss

typedef char *                                          xsd__token;                                       //表示字符串,但字符串中不包含回车(#xA),制表符((#x9)和空格符(#x20)

typedef unsigned char                xsd__unsignedByte;                            //表示位无符号整数,范围至

typedef unsigned int                          xsd__unsignedInt;                        //表示位无符号整数,范围至(需要编译器支持)

typedef unsigned long                xsd__unsignedInt;                        //表示位无符号整数,范围至

typedef unsigned long long  xsd__unsignedLong;                    //表示位无符号整数,范围至

typedef ULONG64                             xsd__unsignedLong;                    //表示位无符号整数,Visual C++支持

typedef unsigned short               xsd__unsignedShort;                    //表示位无符号整数,范围至

2.     编写头文件

//gsoap ns service namespace: urn: query

//gsoap ns service location: http://localhost/gsoap/mod_gsoap.dll?currentTime

 

typedef time_t       xsd__dateTime;

typedef char *       xsd__string;

 

int ns__currentTime(xsd__dateTime& rsp);

 

int ns__helloworld(xsd__string req,xsd__string &rsp);

 

注意事项:头文件最上面的不是注释,而是用于配置命名空间和访问地址,而非单纯的注释;详细配置说明可以参考gSOAP 2.8.1 User Guide.

gSoap对“_”和“__”(下划线、双下划线)有特殊用法,接口定义时函数名前要加上命名空间名和双下划线.

举例:加法运算

        int add(int num1, int num2, int &num3);

        在接口头文件定义时要写成如下格式

        int ns__add(int num1, int nmu2, int &num3);

其中

        "ns"是命名空间名称,此名称可以自定义成其它名字.

        "__"是编译时识别符号,如果没有的话xml文件是编译不出来的.       

 "_"(单下划线)定义名称中要慎用,自己定义的变量或函数名称中使用下划线的地方要在下划线后加上"USCORE"

        例 定义 ns__add_sum(int num_1, int num2, int &num_3);

        要写成如下形式

         ns__add_USCOREsum(int num_USCORE1, int num2, int &num_USCORE3);

       保存编写的头文件(D:/Temp/项目/gSoap/Query.h)

3.     使用soapcpp2编译头文件

在上一讲中我们已经把gSoap安装在D:/gsoap-2.8,但是没有设置运行环境,复制D:/gsoap-2.8/gsoap/bin/win32/soapcpp2.exeD:/gsoap-2.8/gsoap/stdsoap2.hD:/gsoap-2.8/gsoap/ stdsoap2.cppD:/Temp/项目/gSoap文件夹中.为了使用方便,我们采用批处理方法编译头文件.D:/Temp/项目/gSoap中新建Server.batClear.bat.

Server.bat,内容为

soapcpp2 -S Query.h

 

Clear.bat内容为

del  *.xml

del  ns.nsmap

del  ns.wsdl

del  ns.xsd

del  soapC.cpp

del  soapH.h

del  soapObject.h

del  soapServer.cpp

del  soapServerLib.cpp

del  soapStub.h

 

执行Server.bat,后生成ns.currentTime.req.xmlns.currentTime.res.xmlns.helloworld.req.xmlns.helloworld.res.xmlns.nsmapns.wsdlns.xsdsoapC.cppsoapH.hsoapObject.hsoapServer.cppsoapServerLib.cppsoapStub.h

注意生城的文件数量和接口方法相关,如果新增了接口方法必须修改Clear.bat中的内容.

4.     创建项目

打开Microsoft Visual Studio 2008.选择文件-新建-项目,在新建项目对话框中项目类型选择Visual C++ - Win32,在模板中选择Win32项目,项目名称为Query,位置为D:/Temp/项目/gSoap/Query.不要勾选创建解决方案的目录,如图

确定后打开Win32应用程序向导,这里采用默认设置单击下一步

在应用程序设置这里选择应用程序类型为DLL,附加选项选择空项目,单击完成

为使用方便我们在VS解决方案资源管理器中创建三个筛选器gSoapgSoapExTools,创建方法在解决方案资源管理器中点击项目Query,然后鼠标右键弹出快捷菜单-添加-新建筛选器.

在筛选器gSoapEx中添加现有文件D:/Temp/项目/gSoap/stdsoap2.hD:/Temp/项目/gSoap/stdsoap2.cpp.

在筛选器gSoapEx中新建模块定义文件stdsoap2.def,内容为

; stdsoap2.def : Declares the module parameters for a gsoap server DLL.

 

;DESCRIPTION  'gosap server dll'

 

EXPORTS

    ; we must export what the gsoap isapi extension needs for loading us and working with us.

       soap_init PRIVATE

       soap_serve  PRIVATE

       soap_delete PRIVATE

       soap_done     PRIVATE

       soap_end       PRIVATE

       soap_register_plugin_arg PRIVATE

       soap_lookup_plugin PRIVATE

                                                                                                       

       mod_gsoap_init PRIVATE      ; initializer called before loading

       mod_gsoap_terminate PRIVATE ; called before uploading

 

在解决方案资源管理器中右键点击项目Query选择属性,打开属性页对话框,选择配置属性-连接器-输入,找到模块定义文件输入stdsoap2.def.如下图

 

在筛选器gSoap中添加现有文件

ns.nsmap

ns.wsdl

soapC.cpp

soapH.h

soapObject.h

soapServer.cpp

soapStub.h

添加完成后开打ns.wsdl,找到<SOAP:address>节点,修改<SOAP:address

<SOAP:address location="http://localhost/gsoap/mod_gsoap.dll?Query"/>,节点属性location 说明了WebServic的地图,客户端引用时访问的地址就是http://localhost/gsoap/mod_gsoap.dll?Query

 

在筛选器Tools中添加现有文件Server.batClear.bat       

在头文件中添加现有项Query.h(也可以不添加)

在源文件中添加新项Query.cpp,添加完成后如下图:

编译项目Query,出现以下错误

错误 3     fatal error LNK1120: 2 个无法解析的外部命令      D:/Temp/项目/gSoap/Query/Debug/Query.lib

错误 1     error LNK2001: 无法解析的外部符号 mod_gsoap_init  d:/Temp/项目/gSoap/Query/stdsoap2.def  1

错误 2     error LNK2001: 无法解析的外部符号 mod_gsoap_terminate      d:/Temp/项目/gSoap/Query/stdsoap2.def  1

错误原因是因为还没有实现Query.hstdsoap2.def中定义的方法.

5.     方法定义

打开Query.cpp添加以下内容,然后编译Query,项目生成成功.

#include "soapH.h"

#include "ns.nsmap"

 

extern "C"

{

 

       /** This function is called by mod_gsoap after the dll was successfully loaded and before processing begins.

       You can do any one-time initialization here.

       */

       int mod_gsoap_init()

       {

              // todo: add your initialization code here

              return SOAP_OK;

       }

 

       /** This function is called after all processing was done before dll is unloaded.

       You can do any cleanup here.

       */

       int mod_gsoap_terminate()

       {

              // todo: add your termination code here          

              return SOAP_OK;

       }

 

}

 

int ns__currentTime(struct soap *soap,xsd__dateTime& rsp)

{

       return SOAP_OK;

}

 

int ns__helloworld(struct soap *soap,xsd__string req,xsd__string &rsp)

{

       return SOAP_OK;

}

说明:

int mod_gsoap_init() 当前模块加载到IIS时执行的方法,例如在这里创建数据库连接池等.

int mod_gsoap_terminate() 当模块从IIS卸载时执行的方法,例如关闭并释放数据数据库连接池等.

ns__currentTimens__helloworld就是我们在Query.h中声明的WebService接口方法.

mod_gsoap_init()mod_gsoap_terminate()方法是在模块定义文件stdsoap2.def中定义的,如果你不需要主这两个方法,首先删除stdsoap2.def客户的定义,然后删除下列内容

extern "C"

{

 

       /** This function is called by mod_gsoap after the dll was successfully loaded and before processing begins.

       You can do any one-time initialization here.

       */

       int mod_gsoap_init()

       {

              // todo: add your initialization code here

              return SOAP_OK;

       }

 

       /** This function is called after all processing was done before dll is unloaded.

       You can do any cleanup here.

       */

       int mod_gsoap_terminate()

       {

              // todo: add your termination code here          

              return SOAP_OK;

       }

 

}

6.     实现头文件中定义的方法

int ns__currentTime(struct soap *soap,xsd__dateTime& rsp)

{

       //获取当前日期和时间     

       rsp = time(0);

       return SOAP_OK;

}

 

//注意req只接收英文字符串和符号,如果是中文则输出为乱码

int ns__helloworld(struct soap *soap,xsd__string req,xsd__string &rsp)

{

       std::string::size_type dwMemSize(0);

       std::string strVal;

 

       //设置输出的消息

       strVal.assign(req);

       strVal.append(" Hello world!");   

       //为输出参数分配内存

       //soap_malloc分配的内存在soap_end中释放

       //使用时只需要分配,并不需要释放资源

       dwMemSize = ( strVal.size() + 1 ) * sizeof(char);

       rsp = (char*)soap_malloc( soap,dwMemSize );

       memset( rsp,0x0,dwMemSize);

       memcpy_s( rsp,dwMemSize,strVal.c_str(),dwMemSize);

       return SOAP_OK;

}

编译项目Query,编译成功后将"D:/Temp/项目/gSoap/Query/Debug/Query.dll复制到D:/WEB/gsoap.

如里出现复制文件或文件夹出错对话框则必须重启IIS后复制. 重启IIS命令 iisrese

 

7.      自动部署到WEB

由于每次编译项目成功后我们都必须把模块Query.dll复制Web目录中,手工复制容易忘记并且比较麻烦,可以在VS中项目设置为自动部署,在解决方案资源管理器中右键点击项目Query选择属性,打开属性页对话框,选择配置属性-生成事件-生成后事件,在命令行中输入

iisreset /STOP

copy "D:/Temp/项目/gSoap/Query/Debug/Query.dll" "D:/WEB/gsoap"

iisreset /START

说明:

首先停止IIS,这是因为模块加载到IIS后当前模块被IIS占有用,无法删除.

       复制代码到Web目录

       重新启动IIS

每次生成成功后就自动更新Web目录中的文件.

8.     测试

打开VS2008,新建一个C#控制台应用程序,添另一个服务引用D:/Temp/项目/gSoap/Query/ ns.wsdl,找到服务引用后单击确定完成.

修改Main函数为

static void Main(string[] args)

        {

            try

            {

                ServiceReference1.ServicePortType s = new ServiceReference1.ServicePortTypeClient();

                ServiceReference1.currentTimeResponse response1 = s.currentTime(new ServiceReference1.currentTimeRequest() );

                ServiceReference1.helloworldRequest request2 =  new ServiceReference1.helloworldRequest(new ServiceReference1.helloworldRequestBody("lxg"));

                ServiceReference1.helloworldResponse response2 = s.helloworld(request2);

 

                System.Console.WriteLine(response1.rsp);

                System.Console.WriteLine(response2.Body.rsp);                        

            }

            catch (Exception exp)

            {

                System.Console.WriteLine(exp.Message);

            }

        }

编译并运行程序,打印出当前服务器时间和xxx Hello world!,至此一个简单的gSoap服务器开发完成,下一讲中谈谈中文乱码和ISAPI extension - mod_gsoap.dll”调试技术.

原创粉丝点击