0 前言 |
>>[前言]、[第1节]、[第2节]、[第3节]、[第4节]、[第5节] |
众所周知,诸如ASP.NET等的Internet应用系统易于发布与更新版本——只需要修改或增加服务器端程序。比较而言,通过光盘或安装程序发布的窗 体应用程序或客户端系统,版本升级则要困难得多。为此,Microsoft在其.NET平台上提供了ClickOnce技术,该技术具有启动前更新或启动 后更新(下次运行时安装)两种模式,但使用时客户端需要证书,也不能在下载前做文件压缩处理,不可指定客户端文件安装路径,主要针对.NET程序集,不能 发布部署其他类型的文件(如数据库文件、非程序集文件)。显然,ClickOnce技术缺乏应用灵活性。
本文介绍的基于.NET WebService的可扩展客户端自升级框架WebSAUF(Web Smart Auto Upgrade Framework),具有如下功能和特性:
- 可指定多种升级方式:版本比较(程序集版本或文件版本)、文件覆盖、新加文件;
- 可指定文件压缩形式:可使用GZipStream/DeflateStream压缩文件,也能定制自己的文件压缩算法;
- 可指定客户端文件夹:在升级文件配置清单中可以指定客户端文件的安装文件夹;
- 可扩展读写文件方法:通过重写(override)服务器端GetUpgradeFiles与客户端SaveUpgradeFiles方法,可以扩展升级文件的读写行为;
- 可扩展压缩处理方法:压缩方法类TCompressMethod的读写文件方法全部是protected virtual,均可以重写;
- 独立性应用框架结构:独立的基于WebService升级文件服务器,独立的登录认证方式,独立的客户端应用类型,可以应用于各种类型的客户端应用系统。
本文内容如下:
1 技术思路与架构 |
>>[前言]、[第1节]、[第2节]、[第3节]、[第4节]、[第5节] |
1.1 总体技术思路
- 基于.NET WebService技术平台,采用多种文件压缩方法和程序升级形式,使用命令行程序自动升级指定文件夹的客户端文件;
- 框架应用环境为Web服务器、Internet与客户端应用程序(见下图的应用模型图);
- 系统设计与开发平台为.NET 2.0、VC# 2005;
- WebSAUF框架具有独立性和可扩展性。
1.2 系统处理流程
客户端通过WebService请求Web服务器升级文件清单,然后在客户端比较本地的指定文件夹的同名文件,确定需要升级的文件名,接着下载这些升级文件并改名存储到同文件夹中,最后启动命令行bat进程、终止当前进程完成最后的删除原文件与改名升级文件操作。
1.3 框架的类层次结构
从应用或功能划分,WebSAUF框架有四组类:服务器端类(含一个升级文件配置清单文件)、客户端类、压缩方法类与辅助类,它们之间的关系见下图:
- 服务器端
- TSmartAutoUpgradeWebService泛型类:是服务器端WebService网页文件(asmx文件)程序代码的基类,它有TCompressMethod类型约束的泛型参数,定义了4个WebMethod:
- GetRASPublicKey:获取服务器端RSA加密共钥;
- Login:登录Web服务;
- GetUpgradeFileList:获取服务器上的升级文件配置清单;
- DownloadUpgradeFiles:下载服务器上的升级文件。
- SmartAutoUpgradeFileConfig配置文件: 服务器端的升级文件配置清单,该xml文件包含一个<FileList>根节点元素与若干<File>元素,详细介绍参见第3节;
- TSmartAutoUpgradeWebService泛型类:是服务器端WebService网页文件(asmx文件)程序代码的基类,它有TCompressMethod类型约束的泛型参数,定义了4个WebMethod:
- 客户端
- TSmartAutoUpgradeClient泛型类: 是WebSAUF在客户端应用系统的基类,它具有TCompressMethod类型的泛型参数,定义了3个public方法、2个public属性与3个事件,具体使用见第3节;
- WebService代理类TSmartAutoUpgradeWebServiceProxy:是服务器端类型TSmartAutoUpgradeWebService的客户端代理,VS开发工具的Web Reference引用时可创建该类,TSmartAutoUpgradeClient使用该代理类调用Web服务器的asmx网页。
- 压缩方法类TCompressMethod:WebSAUF允许传输压缩文件,也允许定制自己的文件压缩算 法。该类型提供了2个public方法ReadFile/SaveFile,它们可以调用4对protected virtual的文件Read/Save方法中的指定方法(无压缩、GZip、Deflate和Custom):
- ReadFileWithoutCompress/SaveFileWithoutCompress:直接读写文件字节信息;
- ReadFileByGZipCompress/SaveByGZipCompress:使用GZipStream组件读写文件字节信息;
- ReadFileByDeflateCompress/SaveByDeflateCompress:使用DeflateStream组件读写文件字节信息;
- ReadFileByCustomCompress/SaveByCustomCompress:使用自定义算法读写文件字节信息。
- 辅助类:WebSAUF框架有若干辅助类,用来读取配置清单的文件信息、传递升级文件信息、定义事件参数等。
- TRPCResult类:远程或本地操作结果的基类,具有属性RpcSuccess与Message,分别表示操作是否成功与处理消息;
- TUpgradeFileConfig类:升级文件配置信息,具有属性FileName、UpgradeKind、CompressKind与Subdirectory,它们对应了配置文件的同名属性;
- TUpgradeFileConfigCollection类:泛型集合类型Collection<TUpgradeFileConfig>,是升级文件配置信息集合;
- TUpgradeFileInfo类:升级文件信息,主要内容来自TUpgradeFileConfig,增 加了属性AssemblyVersion、FileVersion、Version、Content等。其中,Content为(可能压缩了的)文件字节 数组,Version则是综合版本:如果是程序集,则表示AssemblyVersion;如果有文件版本,则表示FileVersion;否则为空;
- TUpgradeFileInfoCollection类:泛型集合类型Collection<TUpgradeFileInfo>,是升级文件信息集合;
- TUpgradeKind、TCompressKind枚举型:分别表示升级方式和压缩形式;
- TFileUpgradeEventArgs类:客户端TSmartAutoUpgradeClient类型中事件的参数类型。
2 关键实现技术 |
>>[前言]、[第1节]、[第2节]、[第3节]、[第4节]、[第5节] |
WebSAUF关键实现技术包括:框架可扩展性设计、GZipStream压缩处理技巧、WebService代理类重构、命令行自升级方法、程序集或文件版本获取等。
2.1 框架可扩展性设计
WebSAUF可扩展性主要体现在文件压缩处理方法上,即文件下载前是否做压缩/解压缩操作,关键是定制TCompressMethod类型,通过如下两种设计达到目的:
- 泛型类设计:WebService端TSmartAutoUpgradeWebService与客户端 TSmartAutoUpgradeClient类型均是泛型结构,它们的泛型参数约束为TCompressMethod。如果定制了压缩方法(即派生了 TCompressMethod),在编程时用派生类代替泛型类参数即可;
- protected virtual方法:TCompressMethod、 TSmartAutoUpgradeWebService和TSmartAutoUpgradeClient类型中与读写文件相关的方法均设计为 protected virtual,可以在派生类中重写(override),以满足定制的文件压缩或处理算法。
- TSmartAutoUpgradeWebService类型的GetUpgradeFiles方法:获取全 部服务器端的升级文件内容(字节数组),并调用泛型参数TCompressMethod或其派生类的相应方法处理文件字节数组。显然,可以不使用 TCompressMethod而直接在GetUpgradeFiles方法中进行文件处理算法,或做一些优化工作。例如,如果是GZip压缩方式,那么 在首次读该文件时,先压缩最新版本文件到服务器上,随后的升级请求均可以直接读取该文件,避免每个客户端升级请求都做压缩处理。
- TSmartAutoUpgradeWebService类型的SaveUpgradeFiles方法:用别名保存下载的升级文件,为自动升级做准备。显然,如果重写了服务器端的GetUpgradeFiles,那么也必须重写该方法。
- TCompressMethod类型的Read/Save方法:针对4种压缩处理方式,具有4对protected virtual方法,可以重写这些方法,满足定制算法需求。
2.2 GZipStream压缩处理技巧
.NET提供了GZipStream/DeflateStream压缩组件,与直接压缩文件到磁盘上的方法不同,把文件压缩到一个字节数组的处理技巧是:必须关闭GZipStream/DeflateStream压缩流,才能获得正确的压缩后文件字节数组:
上述代码的关键点为:第一,GZipStream构造函数的第三个参数leaveOpen必须为true(默认为false);第二,必须先关闭 GZipStream,再读关联的内存流的数据。事实上,因为leaveOpen=true,此时的压缩流仍然没有关闭,Close只做了一个结束压缩的 操作而已。
下面代码段将获得错误的文件字节数组,因为GZipStream没有关闭,却通过MemoryStream读压缩流字节数据:
下面代码段将抛出异常“无法访问已关闭的流”,因为leaveOpen为false:
2.3 WebService代理类重构
WebService客户端调用的是SoapHttpClientProtocol派生的、与服务器端WebService网页asmx文件相关的一个代 理类。VS2005开发工具提供了一个便利的获取该代理类的方法,但产生代理类时包含了相关的辅助类型、方法及异步方法、方法事件委托等。根据需要,可以 删除辅助类,简化代理类代码,更改代理类名和文件名,等等:
- 修改VS2005自动产生的代理类文件Reference.cs为适当的文件名,或直接把其中的内容拷贝到另一个cs类文件中(例如:WebSAUF.cs文件);
- 修改代理类名称空间localhost.TestClient为统一的框架名称空间CSUST.NET,此时必须做如下代码修改:
- 删除全部的辅助类定义代码;
- 删除构造函数中的this.Url=...的代码。
经过上述修改,TSmartAutoUpgradeWebServiceProxy是一个独立的类型,可以直接在客户端程序中使用。但必须指出如下几点:
- 如果服务器端TSmartAutoUpgradeWebService类增加或修改了WebMethod(Web调用方法)方法名和签名,那么客户端代理类也必须修改相关方法,或重引用后做适当修改;
- 具体应用时,只需要给出正确的代理类Url属性即可;
- WebSAUF(1.0).cs框架文件中的代理类包含了许多无用的代码,例如:事件及委托类型等,为保持Web Reference创建时的原状,没有清理这些代码。
2.4 命令行自升级方法
显然,进程未终结前是不能删除或替换当前可执行文件(exe程序文件)的。WebSAUF采用Windows的命令行处理方式,构建一个命令行bat文 件,退出当前进程前执行bat,完成文件升级工作。主要步骤包括:别名保存下载的升级文件为,创建bat文件,终止当前进程前调用bat,该bat做循环 操作——删除客户端升级原文件、更改别名为原文件名。下面代码是一个实际的bat文件,随后给出的是TSmartAutoUpgradeClient类中 产生bat文件的c#代码。
上述命令行处理代码的关键有四点:1)建立一个命令行处理进程,注意需要处理汉字目录名;2)启动批处理进程前终止当前进程,即 Process.Start(info)、Environment.Exit(0);3)在文件所在的文件夹中做检测、删除与改名操作;4)循环删除文件 并更改别名为原名。
2.5 获取程序集或文件版本
Windowns应用程序(exe/dll)均有文件版本,此外.NET程序集还有所谓程序集版本,它们均可以用来识别更新的程序。WebSAUF首先判断配置清单文件是否有程序集版本,否则判断是否有文件版本,见如下代码:
3 使用WebSAUF框架 |
>>[前言]、[第1节]、[第2节]、[第3节]、[第4节]、[第5节] |
3.1 一般步骤
- 构建Web服务器并定制配置文件SmartAutoUpgradeFileConfig:按客户端文件升级需求,编制一个升级文件清单xml文件。
- 定制满足需求的派生类,主要是定制压缩处理方法和文件处理方法:
- 派生TSmartAutoUpgradeWebService类,重写protected virtual方法GetUpgradeFiles;
- 派生TSmartAutoUpgradeClient类,重写protected virtual方法SaveUpgradeFiles;
- 派生TCompressMethod类,重写相关压缩处理方法。
- 编写asmx文件:修改其中的名称空间和类名称。下面是本示例的WebService网页asmx文件的代码:
<%@ WebService Language="c#" Codebehind = "TestWebSAUF_WebService.asmx.cs" Class = "CSUST.NET.TestWebSAUF_WebService" %>
- 其中,Codebehind属性值为asmx网页代码cs文件,Class属性值为TSmartAutoUpgradeWebService类或其派生类的类型全名(名称空间+类型名)。
- 编写客户端应用程序:在应用程序中使用TSmartAutoUpgradeClient类,创建TSmartAutoUpgradeWebServiceProxy对象,实现客户端自升级功能。具体使用可以参考示例代码中的窗体程序部分。
需要指出,如果没有特定的压缩算法等定制行为,只需要做上面的第1步和第4步工作。
3.2 构建升级文件配置清单TSmartAutoUpgradeFileConfig
1) TSmartAutoUpgradeFileConfig文件结构
配置文件具有根元素<FileList>,包含若干<File>子元素,该子元素有四个属性:Name、UpgradeKind、CompressKind与Subdirectory。除Name外,其它三个属性可以省略,此时取其默认配置值。
- Name:定义文件在服务器存放的路径及名称,其中的路径可以是绝对路径,也可以是当前WebService网页的相对路径(建议方式);
- UpgradeKind:指定升级方式,默认为Version,即先比较程序集版本,如果非程序集文件则比较 文件版本;Override表示无条件覆盖客户端同名文件,NewFile则表示客户端不存在该文件时下载该文件。需要指出,为Version升级方式但 文件没有版本(程序集版本或文件版本),则认为该配置项无效,将不会出现在下载文件中;
- CompressKind:指定文件传输前的压缩处理方式,默认是None,可以选择GZip/Deflate、Custom三种方式;
- Subdirectory:表示相对客户端应用程序(exe程序)启动路径(即AppDomain.CurrentDomain.BaseDirectory)的子文件夹,空或/时表示启动文件夹(这是默认配置)。注意,保存到客户端时如果该文件夹不存在则创建之。
下面是示例代码中的一个配置文件内容:
<?xml version="1.0" encoding="utf-8"?> <FileList> <File Name = "/UpgradeFiles/TestClient.exe" CompressKind = "GZip" Subdirectory = ""/> <File Name = "/UpgradeFiles/ClassDllByVS2005.dll" UpgradeKind = "Version" CompressKind = "None"/> <File Name = "/UpgradeFiles/WinExeByDelphi.exe" UpgradeKind = "Override" CompressKind = "Custom" Subdirectory = "sub2/" /> <File Name = "/UpgradeFiles/WinDllByDelphi.dll" UpgradeKind = "NewFile" CompressKind = "None" Subdirectory = "/sub3/sub33" /> <File Name = "/UpgradeFiles/UpgradeKind_NewFile.xls" UpgradeKind = "NewFile" CompressKind = "Deflate" /> <File Name = "/UpgradeFiles/UpgradeKind_Override.mdb" UpgradeKind = "Override" CompressKind = "Custom" /> </FileList>
2) Web.Config中制定配置文件和路径
配置文件SmartAutoUpgradeFileConfig默认保存在WebService同文件夹中,可以在Web.Config中的AppSettings节指定存放文件夹和文件名,见如下示例代码:
<appSettings> <add key = "SmartAutoUpgradeFileConfig" value = "/Upgrade/SmartAutoUpgradeFileConfig.xml" /> </appSettings>
- key:固定为SmartAutoUpgradeFileConfig;
- value:是配置文件的全路径或当前WebService的相对路径文件名;
- 查找顺序:首先搜索Web.Config,如果不存在指定节或配置文件,则在WebService当前路径下找。
3.3 TSmartAutoUpgradeClient类的属性、方法事件
泛型类TSmartAutoUpgradeClient可以应用于窗体或控制台等应用系统中,它提供了WebSAUF客户端全部的对外接口:属性、方法和事件。
1) TSmartAutoUpgradeClient构造函数
有两个重载版本,一个是默认构造函数,一个包含TSmartAutoUpgradeWebServiceProxy对象参数,见如下代码:
构造函数中的私有方法CreateUpgradeHistoryListFile()用于检测或创建一个升级文件历史清单AutoUpgradeFileHistory.xml,该文件保存在客户端程序文件夹中。下面给出一个升级历史清单文件的内容:
<?xml version="1.0" encoding="utf-8"?> <FileList CreateDate="2009-1-23 11:45:50"> <File UpgradeDate="2009-01-23 11:50:13" Name="TestClient.exe" UpgradeKind="Version" Subdirectory="" /> <File UpgradeDate="2009-01-23 11:50:13" Name="ClassDllByVS2005.dll" UpgradeKind="Version" Subdirectory="" /> <File UpgradeDate="2009-01-23 11:50:13" Name="WinExeByDelphi.exe" UpgradeKind="Override" Subdirectory="sub2" /> <File UpgradeDate="2009-01-23 11:50:13" Name="WinDllByDelphi.dll" UpgradeKind="NewFile" Subdirectory="/sub3/sub33" /> <File UpgradeDate="2009-01-23 11:50:13" Name="UpgradeKind_NewFile.xls" UpgradeKind="NewFile" Subdirectory="" /> <File UpgradeDate="2009-01-23 11:50:13" Name="UpgradeKind_Override.mdb" UpgradeKind="Override" Subdirectory="" /> </FileList>
2) TSmartAutoUpgradeClient公共属性
- UpgradeWebServiceProxy:客户端调用的WebService代理类;
- Ticket:客户端访问WebService的票据。该属性是为需要登录帐号预留的;
- TempFileSuffix:保存升级文件时的文件别名的后缀,是只读属性,固定值".AutoUpgrade";
3) TSmartAutoUpgrdeClient公共方法
- CheckUpgradeFileList:检测服务器端升级文件清单,比较本地的同名文件,确定哪些文件需要下载更新。升级方式为Version时,则比较程序集版本或文件版本;
- CheckUpgradeFileListAsync:异步执行上述操作。注意,不是异步WebService调用;
- StopUpgrade:终止异步操作。
4) TSmartAutoUpgrdeClient事件参数类型
类型TSmartAutoUpgradeClient的事件都使用EventHandler<TFileUpgradeEventArgs>作为事件处理委托,事件参数类型TFileUpgradeEventArgs具有如下属性:
- Success:标记操作是否成功;
- Message:操作结果消息,包括正常的进程信息和异常信息;
- NewFileList:Collection<string>泛型集合,表示需要更新的文件名集。该参数仅在CheckUpgradeFileListColmpleted事件中可用;
- StopUpgrade:可修改属性,表示是否终止下个执行步骤。该属性提供了在处理流程(即事件)中终止下个操作步骤的途径。
5) TSmartAutoUpgrdeClient事件
- CheckUpgradeFileListCompleted:客户端调用WebService代理类检测服务器端升级文件清单完成后的事件;
- DownloadUpgradeFilesCompleted:下载升级文件后的事件;
- SaveUpgradeFilesCompleted:别名保存下载的升级文件到客户端后的事件。正常情况下,激发该事件表明将调用bat命令行进程、终止当前进程(退出客端系统),完成最后的文件升级工作。
3.4 下载包源码与示例程序简介
1) 下载包zip文件
下载zip文件中包含WebSAUF框架源码和WebService及客户端Demo,解决方案文件为WebSAUF.sln,包含两个项目:TestWebServcie项目和TestClient项目。解压缩zip文件后,将产生如下三个文件夹:
- TestClient:文件夹包含客户端窗体程序代码、WebSAUF(1.0).cs框架源码等;
- TestWebService:文件夹包含WebService代码(asmx文件与cs文件等)、 WebSAUF(1.0).cs框架源码、配置文件SmartAutoUpgradeFileConfig.xml。并且,该文件夹中含有一个子文件夹 UpgradeFiles,它包含全部的升级文件:4个版本为11.22.33.44的程序文件、一个Access文件和一个Excel文件;
- OldVersionFiles:文件夹包含旧版本1.2.3.4的4个文件:两个.NET程序集文件、两个Delhpi程序文件。
2) 示例运行测试
安装一个Web服务器(笔者使用XP自带的IIS)以及.NET Framework 2.0及以后版本,运行下载包示例程序的步骤如下:
- 建立Web虚拟路径WebSAUF:虚拟路径必须指向TestWebService文件夹,可以在浏览器中测试该虚拟路径是否正常,即打开网址:http://localhost/WebSAUF/TestWebSAUF_WebService.asmx,正常时将显示4个WebMethod;
- 测试运行客户端:下面是示例客户端程序TestClient.exe(/TestClient/bin/文件夹中)的运行图。可以选择同步或异步执行方式,也可以选择在升级过程中跟踪执行步骤——在三个事件中给出相关信息并提示是否进行下一个步操作。
3) 示例代码的几点说明
- 作为扩展性应用的具体举例,示例代码中给出了TCompressMethod、TSmartAutoUpgradeWebService、 TSmartAutoUpgradeClient三个类的派生类,并重写了TCompressMethod的 ReadByCustomCompress/SaveByCustomCompress文件压缩算法(仅仅用于举例的简单代码);
- 服务器与客户端不在同一台机器时,WebService代理的Url需要修改localhost为实际IP地址或机器域名:
CSUST.NET.TSmartAutoUpgradeWebServiceProxy webService = new CSUST.NET.TSmartAutoUpgradeWebServiceProxy(); webService.Url = "http://172.30.141.16/WebSAUF/TestWebSAUF_WebService.asmx";
- 升级后,当前的TestClient更新为版本11.22.33.44,可以拷贝文件夹/OldVersionFiles/中的旧版程序继续测试。
4 总结与展望 |
>>[前言]、[第1节]、[第2节]、[第3节]、[第4节]、[第5节] |
本文介绍的WebSAUF框架的思路来源于笔者研制的”公路交通流量调查数据综合处理系统“项目,该项目应用了WebService技术,但只使用了 WebSAUF框架中的命令行程序代码,且仅更新客户端exe程序本身。全省数十家机构应用表明,采用自升级客户端技术可以提高开发效率、节省发布与维护 成本。除了因网络影响到升级速度外,可以与ASP.NeT媲美,当然具有ASP.NET无法拥有的丰富客户端体验。
在2008年,同教研室的两位老师各自研制了一个WebService应用系统,大家在一起经常探讨WebService开发与应用技术,一致认 为,如果能像ASP.NET那样方便发布与维护程序集或文件,那么WebService的解决方案将有更大的应用需求。因此,笔者强烈地意思到有必要构建 一个通用的客户端程序升级框架,满足客户端自升级需求,突破客户端应用系统自升级技术障碍。
牛年的头一个月,在笔者紧张的“交通综合历史数据库“项目的设计与实现过程中,停顿两周时间,构思、设计与完成了WebSAUF1.0版,框架代码 约1500行(日均200行),使用了StarUML建模工具和VS2005开发工具。控制台和窗体客户端程序的测试表明,WebSAUF框架基本达到设 计要求:自升级、可配置、独立性与可扩展性。但有一些技术问题尚待解决,例如:WebService登录安全性、WebService交互Ticket票 据应用方式、代理类的简化程度等等,因时间关系,只有留待以后的实际应用中进一步修改完善。欢迎感兴趣者测试、使用、评论或建议WebSAUF框架。
5 版本与源码 |
>>[前言]、[第1节]、[第2节]、[第3节]、[第4节]、[第5节] |
- WebSAUF 1.0, 2009年1月23日, 首次发布。 下载WebSAUF 1.0源码与示例。
- http://blog.csdn.net/hulihui/article/details/3851991