原创翻译 用gSOAP在Windows Embedded Compact (Windows CE)上架构webservice

简介: 本文转载自微软嵌入式中文社区 www.msembed.com  在本文中我将讨论怎样在Windows Embedded Compact设备上架构webservice。

本文转载自微软嵌入式中文社区 www.msembed.com

 

 

在本文中我将讨论怎样在Windows Embedded Compact设备上架构webservice。

你当然可以在一个智能设备上用托管代码来访问webservice,但是不幸的是用托管代码在设备上来架构一个webservice是不可能的。所以让我们再回到古老但却好用的本机代码上来吧!

 


 我将会使用gSOAP,一个第三方框架,它将为我们做大多数无聊麻烦的工作。听上去简单是么?让我们现在就开始吧!
 

 

最重要的部分是WSDL(Web Services Description Language)文件。WSDL是一个XML文档,该文档提供了我们将要在设备上提供的服务的接口描述。这里我将不会花太多时间来解释WSDL的语法,在WSDL在线指南上可以找到很多关于WSDL语法的消息。

 


 让我们先看一个非常简单的WSDL文件,它只描述了一个简单的webservice方法:string HelloWorld(string name)

 


wsdl:
<?xml version="1.0" encoding="utf-8"?><definitions name="HelloService"  targetNamespace="http://www.YourServer.com/wsdl/HelloService.wsdl"  xmlns="http://schemas.xmlsoap.org/wsdl/"  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"  xmlns:tns="http://www.YourServer.com/wsdl/HelloService.wsdl"  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <message name="HelloWorldRequest">    <part name="name" type="xsd:string"/>  </message>  <message name="HelloWorldResponse">    <part name="answer" type="xsd:string"/>  </message>
  <portType name="HelloWorld_PortType">    <operation name="HelloWorldOperation">      <input message="tns:HelloWorldRequest"/>      <output message="tns:HelloWorldResponse"/>    </operation>  </portType>
  <binding name="HelloWorld_Binding" type="tns:HelloWorld_PortType">    <soap:binding style="rpc"      transport="http://schemas.xmlsoap.org/soap/http"/>    <operation name="HelloWorldOperation">      <soap:operation soapAction="HelloWorldAction"/>      <input>        <soap:body          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"          namespace="urn:examples:helloservice"          use="encoded"/>      </input>      <output>        <soap:body          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"          namespace="urn:examples:helloservice"          use="encoded"/>      </output>    </operation>  </binding>
  <service name="HelloWorld_Service">    <documentation>WSDL File for HelloService</documentation>    <port binding="tns:HelloWorld_Binding" name="HelloWorld_Port">      <soap:address        location="http://Topaz:8080"/>    </port>  </service></definitions>
  

 

你可以看到在此WSDL文件中的<definition></definition>标签对(此标签对是必需的)中有六个主要的标签:
1)  types
描述方法的参数和返回值的数据类型。
 

 

2)  message
被传输数据的抽象定义。一个message由若干逻辑部分组成,每一部分都和某个类型系统的定义相关联。
 

 

3)   portType
一组抽象操作。每个操作都引用了一个输入消息和输出消息。
 

 

4)binding
为由portType定义的操作和消息指定具体的规约和数据格式。
 

 

5)  port
为一个binding确定一个地址,这样就确定了单个通信端点。
 

 

6)  service
用来聚合一组相关的ports,来设置实际服务所在的网络位置。

 

 

基本上Type、Message和portType 描述了webservice的方法,binding、port和service描述了怎样在socket上传输数据。
 

 

位置信息中包含了提供webservice服务的设备的server的ip地址或主机名。在这个例子中我用了一个Topaz设备(你要根据你的设备的IP地址或主机名进行相应改动)。
 

 

在对WSDL进行简单描述后,让我们开始创建相应的Visual Studio 2008 project。这是工作量最大的部分。
 在生成实际的VS2008 project之前,我们还须做以下几步工作:
 

  1. 将WSDL文件导入一个空project
  2. 用gSOAP通过wsdl2header.exe生成一个头文件
  3. 用gSOAP生成对应的.cpp实现文件
  4. 实现我们的方法 

 

由于gSOAP会自动生成很多文件,我将会把自动生成文件和实际的实现文件分开处理,这样我们就可以把精力集中在实现文件上。让我们现在开始生成一个Visual Studio的空project并加入WSDL文件。
 

步骤1:将WSDL导入一个空project
 打开Visual Studio并生成一个新的C++ smart device project(File菜单 | new | Project),输入HelloWorldWebService 做为solution的名字。
 

 选择OK,并在接下来的"Welcome to the Win32 Smart Device Project Wizard" 对话框中点击next。在下一个"platforms"对话框中选择你的目标设备的SDK。正如你看到的我选择的是Topaz设备(http://guruce.com/topaz)。 选择next:
 
 
 在"project setting"对话框中选择 "console application"和"empty project"然后点击finish。
 
 

生成此solution后,在solution explorer(通常这是缺省可见的,如果没有,选择View | Solution Explorer)中的"header"区添加一个新的"gsoap"过滤器(右击 "Header Files", 选择Add->New Filter),在"source"区中添加同样的过滤器。我们将让gSOAP在此处生成文件。现在在你的solution的文件夹中新建一个后缀名为.wsdl的文件。将上面的示例WSDL拷贝到文件里,然后将此wsdl文件加入solution中(右击solution然后选择"add existing item")。在此处我的文件名为 HelloWsdl.wsdl。如下图所示:

 步骤2:用gSOAP从我们的wsdl中生成头文件。
 首先下载gSOAP来获得需要的工具。你可以从此处下载:
  http://sourceforge.net/projects/gsoap2/files/
 将解包的文件保存在一个适合记忆的文件夹中,因为在后面我们会在visual studio project中引用此文件夹路径。
 现在我们回到你的solution中,右击wsdl文件然后选择properties。在"Custom build step"中的"Command Line"区中加入如下命令(注意我将gSOAP工具放在$(SolutionDir)中,请确认你的路径并确保你的solution目录不包含任何空格):
 $(SolutionDir)/gsoap-2.8/gsoap/bin/win32/wsdl2h.exe -s $(InputPath) -o $(ProjectDir)$(InputName).h
 这将使用wsdl2h.Exe来生成我们的头文件。参数中指定了输入和输出文件。在property页的"Outputs"项中输入:
 $(InputName).h 
 
 你需要填入"outputs"项来让wsdl"可编译"。注意此处路径中包含了版本号(gsoap-2.8)。这个当然是可以改变的。还要注意我的这个"setting"对应"All Configurations",并不仅是对当前的这一个。 
 

 

 点击"Apply"和"Ok",然后再一次右击wsdl文件并选择"Compile"。
 

 

 现在我们的头文件 (HelloWsdl.h)已经在project目录中生成好了,我们需要将此文件加入到project中。在solution explorer中的header区右击"gsoap"目录,然后选择"add existing item"。选择HelloWsdl.H并加入。
 让我们为生成cpp文件做同样的事情:
 

 

步骤3:用gSOAP生成cpp源文件。 
 在solution explorer中右击我们刚才在上一个步骤中加入的HelloWsdl.h,选择"properties"。在"Custom build step"中加入如下的命令:
 $(SolutionDir)/gsoap-2.8/gsoap/bin/win32/soapcpp2.exe -S $(InputPath) 
 

 

在"Outputs"项中填入:
 $(InputName).cpp 
 
 

 右击HelloWsdl.H文件并选择"Compile"。这将也会生成一些文件,不过现在还不能编译整个solution。如果现在你要编译这个solution,像我刚开始用gSOAP时做的那样,你将会遇到很多编译错误。所以我们还要多做几步工作。
 

 在solution explorer中的header区右击"gsoap"目录并加入如下头文件:
 - soapH.h    - soapStub.h  
 

 

 在solution explorer中的source区右击"gsoap"目录并加入如下cpp文件:
 - soapC.cpp    - soapServer.cpp 
 

 

下一步是在solution中加入stdsoap2.h和stdsoap2.cpp。你可以在"gsoap-2.8/gsoap"目录中找到这两个文件。在solution explorer中右击project选择"Add existing item"。它们会自动出现在对应的"header"和"source"区中。
 

 

我们已经将stdsoap2.h加入到了solution中,不过我们还须将stdsoap2.h所在的目录路径加入到project的 "include directory list" 中。在solution explorer中右击project选择"Properties",在"Configuration Properties" | C/C++ | General中选择"Additional Include Directories ",将"$(SolutionDir)/gsoap-2.8/gsoap"加入到头文件目录列表中。
 

 

现在我们已经做完了生成文件的工作,让我们开始动手写代码吧!
 

 

 步骤4:实现我们的方法
 首先我们要创建包含主定义和具体方法的cpp源文件。在这里我用了两个分开的文件。第一个文件包含服务器代码用于侦听接入请求/消息。第二个文件将实现我们的webservice的方法。
 

 

 在Solution Explorer中右击"Source Files"并选择"New Item"。选择C/C++文件并将文件命名为HelloWsdl.Cpp。用同样的方法创建HelloWsdlMethods.Cpp文件。 
 

 

 你的整个solution应该看起来像这样:
 
 让我们从容易的开始。打开HelloWsdlMethods.cpp,将以下代码片断拷贝并粘贴到文件里:
 

 

#include "soapH.h"
int ns1__HelloWorldOperation(struct soap*,                             char*  name,       /// 请求参数                             char*  &answer     /// 响应参数){    printf("Hello my method/r/n");
 char* myName = {"Erwin"};    answer = myName;    return SOAP_OK;}
 

 

如果你在加入以上代码之前编译project,你将会在错误信息列表中发现关于这个方法的unresolved external错误。以上的方法由gSOAP自动生成声明,并在上边的代码片断中实现。你可以在自动生成的soapStub.h文件中找到该方法的声明:
/************************************************************************/* 服务器端操作                                                    /************************************************************************/
SOAP_FMAC5 int SOAP_FMAC6 ns1__HelloWorld(struct soap*, char *name, char *&answer);
   

 

这里就是在WSDL中加入的方法的声明部分的所在位置。
 

 

我们已经离完成整个工作不远了!最后我们需要做的是加入服务器代码。此代码用于等待来自任意客户端的请求。下面就是代码。它可能起初看上去有点复杂,不过别被它吓倒了。这些代码取自gSOAP网站(文档部分中的章节7.2.3 怎样生成一个单机服务器),只做了一些小改动,我会在下面加以说明:
 

 

/** 包含命名空间结构 */#include "HelloWorld_USCOREBinding.nsmap"
int _tmain(int argc, char* argv[]){   struct soap soap;    int m, s; // master and slave sockets    soap_init(&soap);   soap_set_namespaces(&soap, namespaces);      //** 设置命名空间 **/   m = soap_bind(&soap, "", 8080, 100);         //** 让字符串为空,gSOAP会找出我们的"localhost"具体是什么 **/   if (m < 0)       soap_print_fault(&soap, stderr);    else   {       fprintf(stderr, "Socket connection successful: master socket = %d/n", m);       for (int i = 1; ; i++)       {         s = soap_accept(&soap);          if (s < 0)           {             soap_print_fault(&soap, stderr);             break;          }       fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,             (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);       if (soap_serve(&soap) != SOAP_OK) // process RPC request       soap_print_fault(&soap, stderr); // print error       fprintf(stderr, "request served/n");       soap_destroy(&soap);      // clean up class instances       soap_end(&soap);  // clean up everything and close socket       }    }    soap_done(&soap); // close master socket and detach context
   return 0;}
  

 

拷贝并粘贴以上代码到HelloWsld.cpp中。
  

 

改动部分用(/** */)加以注释。加入的部分用于包含一个命名空间结构来显式地设置正确的命名空间。gSOAP (soapcpp2.exe)不仅自动生成代码文件,而且还生成一个*.nsmap文件,该文件生成了一个包含有用到的正确的命名空间的静态结构体。soap_set_namespaces()方法用于将该结构体投入使用。就是这样了!
  

 

看上去好像有很多工作,不过当你的project生成完毕后,你的接口文件(wsdl)中的任意改动都会自动反映在实现部分中。project生成完毕后,大多数情况下你只需要改动wsdl文件和HelloWsdlMethods.cpp来加入你自己的方法。     
 

 

现在我们已经在Windows CE上生成了服务器,接下来我们要生成一个客户端来使用我们的服务:
 C#托管客户端
 

 

生成一个只有一个窗体的C#.NET桌面应用程序(这里我不会详细介绍如何生成一个C#.NET应用程序,因为这超出了本文的范围,我假定你已经知道怎么做这件事)。给该窗体加一个按钮并给予命名。然后我们需要将webservice的引用(wsdl文件)加入到solution中。在solution explorer中右击"References"并选择"Add Service Reference"。在Address输入框中输入wsdl文件的本机路径。在我这里是:
 

 

C:/Data/Development/HelloWorldWebService/HelloWorldWebService/HelloWsdl.wsdl.我将此引用命名为HelloWsdlMethods,它将出现在solution explorer中。然后点击"Go":

在主窗体中双击按钮并将以下代码粘贴到按钮点击事件处理函数中:
 

 

private void btnTest_Click(object sender, EventArgs e){    try    {        HelloWsdlMethods.HelloWorld_PortTypeClient server = new HelloWsdlMethods.HelloWorld_PortTypeClient();        string name = server.HelloWorldOperation("Dummy");        MessageBox.Show(name);    }    catch (Exception error)    {        MessageBox.Show("Request failed: " + error.Message);    }}  
 

 

 这些代码将访问我们的服务器并调用webservice的方法。如你所见命名可能不是很好,但对于这个例子来说够用了。
 

 

在目标设备上运行服务器程序(HelloWorldWebService.exe),在桌面上运行客户端程序。如果一切都没错的话,在点击"Test"按钮后你将看到如下:
 

在桌面上运行的C#客户端:
 
 

在Windows Embedded Compact设备上的应用程序控制台:
  
 

需要特别注意的事情
 正如你已经看到的,将gSOAP和Visual Studio 2008完美地结合在一起需要做一些工作。这个过程比较容易出错,gSOAP的事件记录在大多数情况下并不能帮助太多。以我的经验,基本上有三个地方比较容易出错:
 位置
 这个wsdl文件中的标签指定了webservice运行的网络地址。请确认它被正确设置。
 

 

 命名空间
 请确保wsdl文件中使用的命名空间和源代码文件中的命名空间(使用nsmap文件)相一致,并且别忘了调用soap_set_namespaces()。
 

 

主机名 
 请确认主机名为空:soap_bind(&soap, "", 8080, 100)。在CE上指定主机名没用。还有wsdl2h.exe和soapcpp2.exe这两个工具有很多选项,在本文中没有涉及到。但是花时间来了解这些选项是绝对值得的。gSOAP网站上有很多信息和详细的文档。
 

 

 当然,本文用了一个很简单的例子来介绍如何用gSOAP在Windows CE上使用webservices, 但是这应该足够能让你开始在智能设备上架构更加复杂的webservice。
 

如果你想看到一些更复杂的例子(比如通过webservices来传输文件),请和我们联系。
 祝

 

好运! 
  
 
 

相关文章
|
2月前
|
XML 前端开发 算法
计算机毕业设计|外文文献翻译之MVC架构详细介绍
计算机毕业设计|外文文献翻译之MVC架构详细介绍
|
存储 缓存 安全
[笔记]深入解析Windows操作系统《二》系统架构
深入解析Windows操作系统《二》系统架构
905 0
[笔记]深入解析Windows操作系统《二》系统架构
|
9月前
|
缓存 Unix 调度
[笔记]深入解析Windows操作系统《二》系统架构(一)
[笔记]深入解析Windows操作系统《二》系统架构
222 0
|
9月前
|
存储 安全 Unix
[笔记]深入解析Windows操作系统《二》系统架构(五)
[笔记]深入解析Windows操作系统《二》系统架构(五)
|
9月前
|
存储 安全 API
[笔记]深入解析Windows操作系统《二》系统架构(四)
[笔记]深入解析Windows操作系统《二》系统架构(四)
|
9月前
|
存储 缓存 安全
[笔记]深入解析Windows操作系统《二》系统架构(三)
[笔记]深入解析Windows操作系统《二》系统架构(三)
|
9月前
|
缓存 安全 Unix
[笔记]深入解析Windows操作系统《二》系统架构(二)
[笔记]深入解析Windows操作系统《二》系统架构(二)
|
存储 前端开发 Unix
软考 系统架构设计师 2009-2018年英语翻译及重点词汇(上)
软考 系统架构设计师 2009-2018年英语翻译及重点词汇
|
存储 SQL 安全
软考 系统架构设计师 2009-2018年英语翻译及重点词汇(下)
软考 系统架构设计师 2009-2018年英语翻译及重点词汇
|
存储 前端开发 算法
React系列——React Fiber 架构介绍资料汇总(翻译+中文资料)
React系列——React Fiber 架构介绍资料汇总(翻译+中文资料)
248 0