版本自动更新程序及3种实现策略-分析文档(UML图)

http://www.vjsdn.com/bbs/bbsTopicDetails.aspx?pid=596

版本自动更新程序及3种实现策略

C/S程序是基于客户端和服务器的,在客户机编译新版本后将文件发布在更新服务器上。然后建立一个XML文件,该文件列举最新版本号和所有文件及文件最后修改日期。如文件较多可以通过工具自动建立XML文件。当某客户机运行程序后会自动下载这个XML文件,通过与本地机器上的版本号匹配,如本机上的版本号比服务器上的要旧,通知客户机运行更新程序。如用户更新了版本,将最新版本号写入配置文件,这样方便下一次匹配。

通过可行性分析可以使用下面3种方案下载
1.局域网共享文件夹下载 
2.Tcp/ip远程下载 
3.通过Web方式下载。

方式1适合内部网络,功能简单,编程只需调用File.Copy()就能实现。如建立VPN网络,也可视为远程下载。我们只需在服务器上共享一个文件夹并设定访问权限,然后将最新版本文件存放在这个目录。升级程序直接从这个目录Copy文件即可。

方式2是通过基于tcp/ip 的Socket组件编程来实现,使用这个机制必须有服务器监听程序。其简单设计思路是在服务器端启动TcpListener监听客户端的Socket连接,当Client发送连接请求,TcpListener捕获当前请求的Socket,并获取收到的数据(字符串,称为命令)。然后由命令处理程序分析该字符串,如果字符串头部包含GET_FILE标识则为下载文件请求。
例:客户机向服务器程序发送请求命令:"GET_FILE|D:\PUBLISH\TEST.DLL"。首先TcpListener捕获当前请求的Socket.并接收到字符数据"GET_FILE|D:\PUBLISH\TEST.DLL",通过分析发现"GET_FILE"标识符,表示下载文件请求.然后通过socket.SendFile(file="D:\PUBLISH\TEST.DLL")将文件传送给当前Socket。客户端由NetworkStream.Read()方法接收来自服务器发送的文件。

方式3是通过.NetFramework提供的WebClient组件下载文件。只需指定DownloadData()方法中参数address(url)。

通过上面的介绍,1,3方法最简单。Tcp/ip相对复杂。

 


下面讲解版本更新程序系统框架图

主窗体<->下载控制器<->XmlLoader关系图
贴图图片
图解:
frmUpgrader窗体内定义了一个下载控制器及2个TreeView控件.
当执行[检查更新],控制器调用当前下载器的DownloadServerXml()方法从服务器下载XmlServerFiles.xml文件。下载成功将文件交给XmlLoader分析器,分析器创建XmlDocument对象。最后将XML分析器作为FileView构造器参数创建FileView实例,分别调用FileView的LoadTreeViewClient方法创建客户端文件清单的TreeView及LoadTreeViewServer方法创建服务器端文件清单的TreeView。TreeView的数据来源是两个Xml文件。

DownloadController 下载控制器,它负责建立下载策略及控制当前下载器。

 

FileView对象实际是个TreeView生成器。它跟据Xml结构自动生成TreeView.

XmlLoader分析器主要功能是分析服务器端及本地的XML文件(XmlServerFiles.xml和XmlClientFiles.xml)。XmlLoader类图列举了所有方法,从图中可以看出控制XmlDocument对象。通过XmlDocument.SelectSingleNode方法查找某个指定的文件,然后获取文件最后修改日期文件名等信息用于匹配。

IDownloader接口定义了所有下载器的方法,下面会详细讲解3个下载器的实现策略。

FileInfo是文件的实体类,结构相当简单,只包含文件名,物理路径及最后修改时间。



三种不同的下载器实现方案:
贴图图片

三种不同的下载器实现方案类及接口说明: 
frmUpgrader: 主窗体
DownloadController:下载控制器,如上图所示,它是控制IDownloader接口。
IDownloader: 下载器接口, 三种不同的下载器分别实现了这个接口
LAN_Downloader: 局域网复制文件下载器。
WebClient_Downloader: 广域网下载器,通过WebClient组件下载文件。
TcpIp_Downloader: Tcp/ip下载器。需要运行Tcp/ip服务器提供下载服务。

 

主窗体有[检查更新]及[开始更新]两个按钮。分别调用下载控制器的CheckUpdate()及Download()方法。
下载控制器控制IDownloader接口,三种不同的下载器分别实现了这个接口,通过对象多态原理,实际上是
下载控制器间接控制着实现IDownloader接口的所有下载器。我们可以把DownloadController理解为桥接(Bridge)
或适配器(Adpater)。IDownloader接口实现了3种下载策略,这也是策略模式的应用。



Tcp/IP下载器图解:
贴图图片

关于Tcp/IP下载这里有个案例
使用Tcp/Ip下载文件(图) 
http://www.vjsdn.com/bbs/html/090809_561.html

Tcp/IP下载器需要有服务器程序支持,使用tcp/ip传送文件其简单设计思路是在服务器端启动TcpListener监听客户端的Socket连接。当Client发送连接请求,TcpListener捕获当前请求的Socket,并获取收到的数据(字符串,称为命令)。然后由命令处理程序分析字符串,如果字符串头部包含GET_FILE则为下载文件请求。
例:如客户机向服务器程序发送请求:"GET_FILE|D:\PUBLISH\TEST.DLL"。首先TcpListener捕获
当前请求的Socket,收到字符串,如果是下载文件请求就通过socket.SendFile(file="D:\PUBLISH\
TEST.DLL")将文件传送给当前Socket。在客户端由NetworkStream.Read()方法接收来自服务器发送的文件。

参考上图来理解如何实现:
UpgraderServer 是tcp/ip服务器的核心类。他控制TcpListener对象,TcpListener负责监听客户端的Socket连接。
当有下载文件请求时就调用SendFile()方法将文件传送给当前连接的Socket. 
Stop()方法用来关闭服务器.
SendFile()方法用来发送文件
StartListening()方法用户启动监听程序。

TcpListener是监听程序,它负责监听客户端的Socket连接。如有连接请求触发AccecptSocket方法。该方法返回当前请求的Socket对象。

UpgraderClient是tcp/ip客户端的核心类。他控制TcpClient对象, TcpClient对象负责监听来自服务器的请求。
DownloadFile()方法详解:
要明白客户端是如何接收文件,先要明白NetworkStream对象. NetworkStream是提供用于网络访问的基础数据流。客户机监听来自服务器的数据是通过NetworkStream.Read()方法实现的,当程序执行到ns.Read()方法时就开始监听,同时中断下面代码执行,直到接收到数据才会执行Read()下面的代码。请看代码。

byte[] resBytes =  new  byte[256];  //一次接收256字节 
int resSize;  //当前接收到的数据长度 
do
{
    //开始监听,同时中断下面代码执行,直到接收到数据才会执行Read()下面的代码。 
   resSize = ns.Read(resBytes, 0, resBytes.Length);
   
    string msg = Byte2Str(resBytes);
    if (msg.Trim().ToUpper() == "FILE_NOT_FOUND")
   {
       if (_writeOutput !=  null) _writeOutput("找不到文件:" + file);
       break;
   }
    if (resSize == 0)  break;
   
   ms.Write(resBytes, 0, resSize);
   }  while (ns.DataAvailable);
   ns.Close();
   


请注意while (ns.DataAvailable)这段代码,当接受到来自服务器的数据时DataAvailable=True,然后通过NetworkStream.Read方法每次读取256字节,直到读取完所有数据时DataAvailable=false。这时监听工作完成,跳出while循环。最后调用FileStream对象保存文件。

TcpIp_Downloader Tcp/IP下载器方法
Download():下载XmlServerFiles.xml定义的所有文件
DownloadFile(FileInfo file):下载单个文件
DownloadServerXml():下载服务器上的文件清单
Init() //初始化下载器



IDownloader下载器接口定义

 

/// <summary> 
/// 下载器接口 
/// </summary> 
public  interface IDownloader
{
    void Init();  //初始化下载器 
    void Download(); //下载所有文件 
   
   FileInfo DownloadFile(FileInfo file);  //下载单个文件 
   XmlLoader XmlServer {  get;} //服务器上的Xml文件
   XmlLoader XmlLocal {  get;}//客户机上的Xml文件
   
    int DownloadsCount {  get;} //下载成功的文件总数
    int DownloadFaliedCount {  get;}//下载失败的文件总数
   
    void DownloadServerXml();  //下载服务器上的文件清单(xml文件) 
    void SetProgressBar(ToolStripProgressBar progress);
    void SetTrace(ListBox logList);
}


下载器类型
/// <summary> 
/// 下载器类型 
/// </summary> 
public  enum DownloadType
{
   Intranet = 1,
   TcpIp = 2,
   WebDownload = 3
}

下载控制器,该控件器可以创建3种不同的下载器。
/// <summary> 
/// 下载控制器,该控件器可以创建3种不同的下载器。 
/// 策略模式应用。 
/// </summary> 
public  class DownloadController
{
    private IDownloader _downloader =  null;
    public IDownloader CurrentDownloader {  get {  return _downloader; } }
   
    private TreeView _tvServerFiles;
    private TreeView _tvLocalFiles;
    private ListBox _log;
    private ToolStripProgressBar _progress =  null;
   
    public DownloadController(IDownloader downloader)
   {
      _downloader = downloader;
   }
   
    /// <summary> 
    /// 跟据下载类型创建3种不同的下载策略 
    /// </summary> 
    public  static DownloadController Create(DownloadType type)
   {
       if (DownloadType.Intranet == type)
       return  new DownloadController( new LAN_Downloader());
       if (DownloadType.TcpIp == type)
       return  new DownloadController( new TcpIp_Downloader());
       if (DownloadType.WebDownload == type)
       return  new DownloadController( new WebClient_Downloader());
       return  null;
   }
   
    public  void Download()
   {
      _log.Items.Add("开始下载....");
      _downloader.SetProgressBar(_progress);
      _downloader.Init();
      _downloader.SetTrace(_log);
      _downloader.Download();
      _log.Items.Add("下载完成....");
      
      _log.Items.Add("刷新文件列表....");
       //下载完成更新视图 
       new FileView(_downloader.XmlLocal, _progress).LoadTreeViewClient(_tvLocalFiles,  null);
      _log.Items.Add("完成.");
   }
   
    public  void CheckUpdate()
   {
      _log.Items.Add("开始检查服务器上有用更新....");
      _downloader.SetProgressBar(_progress);
      _downloader.Init();
      _downloader.SetTrace(_log);
      
       //加载Treeview 
       new FileView(_downloader.XmlServer, _progress).LoadTreeViewServer(_tvServerFiles);
       new FileView(_downloader.XmlLocal, _progress).LoadTreeViewClient(_tvLocalFiles, _downloader.XmlServer);
      
       if (_downloader.XmlLocal.HasNewVersion)
      _log.Items.Add("服务器上有最新版本,请更新.");
       else
      _log.Items.Add("检查完成,没有发现新版本.");
   }
   
    public  void BindControls(TreeView tvServerFiles, TreeView tvLocalFiles, ListBox log, ToolStripProgressBar progress)
   {
      _progress = progress;
      _tvLocalFiles = tvLocalFiles;
      _tvServerFiles = tvServerFiles;
      _log = log;
   }
}



文件对象定义

/// <summary> 
/// 文件对象 
/// </summary> 
public  class FileInfo
{
    private  string _name = "";
    private  string _FullPath = "";
    private DateTime _ModifyTime = DateTime.MinValue;
   
    public FileInfo() { }
   
    public FileInfo( string fileName,  string fullPath, DateTime lastEditDate)
   {
       this.Name = fileName;
       this.FullPath = fullPath;
       this.ModifyTime = lastEditDate;
   }
   
    /// <summary> 
    /// 纯文件名,不包含路径。:如upgrader.exe 
    /// </summary> 
    public  string Name {  get {  return _name; }  set { _name = value; } }
   
    /// <summary> 
    /// 文件完整路径。如:[.\8.8.8.2\abc.dll] 
    /// </summary> 
    public  string FullPath {  get {  return _FullPath; }  set { _FullPath = value; } }
   
    /// <summary> 
    /// 最后更新时间。 
    /// </summary> 
    public DateTime ModifyTime {  get {  return _ModifyTime; }  set { _ModifyTime = value; } }
   
    public  override  string ToString()
   {
       return  this.Name;
   }
}

XML文件解释器,分析服务器/客户端的xml文件

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Collections;
using System.Windows.Forms;

namespace VJSDN.Tech.AutoUpgraderLib
{
    /// <summary> 
    /// XML文件解释器,分析服务器/客户端文件清单。 
    /// </summary> 
    public  class XmlLoader
   {
       private XmlDocument _xml;
       public XmlDocument XML {  get {  return _xml; } }
      
       private  string _xmlFilePath;
      
       public XmlLoader( string xmlFile)
      {
         _xml =  new XmlDocument();
         _xmlFilePath = xmlFile;
          if (System.IO.File.Exists(xmlFile)) _xml.Load(xmlFile);
      }
      
       private  bool _HasNewVersion =  false;
      
       //本机文件清单与服务器文件清单比较后是否发现新版本。 
       public  bool HasNewVersion {  get {  return _HasNewVersion; }  set { _HasNewVersion = value; } }
      
       /// <summary> 
       /// 创建空的Xml文件 
       /// </summary> 
       /// <param name="xmlFilePath"></param> 
       public  static XmlLoader CreateEmpty( string xmlFilePath)
      {
          string xml =
         "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \r\n" +
         "<Upgrader> \r\n" +
         "<description>本机最近更新清单</description> \r\n" +
         "<Application> \r\n" +
         "<LastUpdateTime value=\"\" /> \r\n" +
         "<Version value=\"\" /> \r\n" +
         "</Application> \r\n" +
         "<Files> \r\n" +
         "</Files> \r\n" +
         "</Upgrader> \r\n";
         
          //删除旧的更新文件 
          if (File.Exists(xmlFilePath)) File.Delete(xmlFilePath);
         
          string dir = Path.GetDirectoryName(xmlFilePath);
          if (!Directory.Exists(dir))  throw  new Exception("不存在目录:" + dir);
         
         StreamWriter sw = File.CreateText(xmlFilePath);
         sw.Write(xml);
         sw.Flush();
         sw.Close();
         
          return  new XmlLoader(xmlFilePath);
      }
      
       //保存最后更新信息 
       public  void SetLastUpdateInfo( string version, DateTime lastUpdateTime)
      {
         XmlNode nodeVersion = _xml.SelectSingleNode("Upgrader/Application/Version");
         XmlNode nodeTime = _xml.SelectSingleNode("Upgrader/Application/LastUpdateTime");
         
         nodeVersion.Attributes["value"].Value = version;
         nodeTime.Attributes["value"].Value = lastUpdateTime.ToString();
      }
      
       //获取xml文件版本信息 
       public  string GetVersion()
      {
         XmlNode ver = _xml.SelectSingleNode("Upgrader/Application/Version");
          if (ver !=  null)
          return ver.Attributes["value"].Value;
          else
          return "";
      }
      
       /// <summary> 
       /// 比较服务器与本机文件的最后更新时间。 
       /// 返回True:表示可以更新。False:服务器与本机文件版本一致。 
       /// </summary> 
       /// <param name="file">服务器上的文件</param> 
       /// <returns></returns> 
       public  bool CompareFile(FileInfo file)
      {
          if (file ==  nullreturn  true//文件没找到,为不相同,返回True; 
         
         XmlNode node =  this.GetFileNode(file.FullPath);
          if (node ==  nullreturn  true//文件没找到,为不相同,返回True; 
         
         DateTime date;
          if (DateTime.TryParse(node.Attributes["lastModify"].Value,  out date))
         {
             return file.ModifyTime.CompareTo(date) > 0;
         }
         
          return  false;
      }
      
       /// <summary> 
       /// 比较两个XmlNode的日期大小 
       /// </summary> 
       public  bool CompareNode(XmlNode node1, XmlNode node2)
      {
          if (node1 ==  null || node2 ==  nullreturn  false;
         
         DateTime date1 = Common.StrToDate(node1.Attributes["lastModify"].Value);
         DateTime date2 = Common.StrToDate(node2.Attributes["lastModify"].Value);
          return date1.CompareTo(date2) > 0;
      }
      
       //获取在xml文件的结点 
       public XmlNode GetFileNode( string fullPath)
      {
          string xPath = @"Upgrader/Files/File[@fullPath=''" + fullPath + "'']";
         XmlNode node = _xml.SelectSingleNode(xPath);
          return node;
      }
      
       //获取在xml文件的结点,转换为FileInfo对象。 
       public FileInfo GetFileInfo( string fullPath)
      {
         XmlNode node =  this.GetFileNode(fullPath);
          if (node !=  null)
         {
            FileInfo fi =  new FileInfo();
            fi.FullPath = node.Attributes["fullPath"].Value;
            fi.ModifyTime = Common.StrToDate(node.Attributes["lastModify"].Value);
            fi.Name = node.Attributes["fileName"].Value;
             return fi;
         }
          return  null;
      }
      
       /// <summary> 
       /// 在客户端的Xml记录文件加入更新记录 
       /// </summary> 
       /// <param name="FileInfo"></param> 
       /// <param name="serverFile"></param> 
       public  void AddOrUpdateHistory(FileInfo serverFile, FileInfo clientFile)
      {
         XmlNode node = GetFileNode(serverFile.FullPath);
          if (node ==  null)
         {
            XmlNode fileRoot = _xml.SelectSingleNode("Upgrader/Files");
            node = _xml.CreateNode(XmlNodeType.Element, "File", "");
            fileRoot.AppendChild(node);
         }
         node.RemoveAll(); //先删除结点内的数据 
         node.Attributes.Append(CreateAttribute("fileName", clientFile.Name));
         node.Attributes.Append(CreateAttribute("fullPath", serverFile.FullPath));
         node.Attributes.Append(CreateAttribute("lastModify", serverFile.ModifyTime.ToString()));
      }
      
       private XmlAttribute CreateAttribute( string name,  string value)
      {
         XmlAttribute attr = _xml.CreateAttribute(name);
         attr.Value = value;
          return attr;
      }
      
       //保存历史记录 
       public  void Save()
      {
         _xml.Save(_xmlFilePath);
      }
      
       /// <summary> 
       /// 获取文件数量 
       /// </summary> 
       public  int FilesCount
      {
          get
         {
            XmlNode node = _xml.SelectSingleNode("Upgrader/Files");
             return node.ChildNodes.Count;
         }
      }
      
       /// <summary> 
       /// 获取文件列表 
       /// </summary> 
       public IList GetFiles()
      {
         IList files =  new ArrayList();
         XmlNode node = _xml.SelectSingleNode("Upgrader/Files");
          foreach (XmlNode n  in node.ChildNodes)
         {
            FileInfo sf =  new FileInfo();
            sf.FullPath = n.Attributes["fullPath"].Value;
            sf.ModifyTime = Common.StrToDate(n.Attributes["lastModify"].Value);
            sf.Name = n.Attributes["fileName"].Value;
            files.Add(sf);
         }
          return files;
      }
   }
}

版本自动更新程序及3种实现策略(二)下载器实现 

版本自动更新程序及3种实现策略程序下载(附源码) 
自動更新插件。 AutoUpgrader is a component for building software updates. Delphi programmers can use this component to create their own software packages without writing a single line of code, which includes ready-made wizards that can be activated for different purposes, as well as multiple languages ​​for the application interface. It uses updates and the interesting feature is that when the end user runs the update software, the application will recognize the user’s current operating system language and display the application’s interface in the same language. This component currently translates all wizards into 13 different languages, including English, Spanish, German, French, Russian, Portuguese, Italian, Chinese and more. Of course, if you are not interested in using the program’s pre-installed wizards, you can easily design an ambient wizard with your own texts and images for the entire process. If you have installed a new version of your application in the encrypted web directory, you can do so by configuring the username and password of access files, or allow this component to be displayed to the user when the login window needs it. And users can access the app by entering the appropriate username and password. With AutoUpgrader end users of your app will always use the latest version of your products. This package consists of two components, one HTTP web / HTTP based HTTP protocol (which supports upload and cache IE and …) and another acThread, a threaded caponity for use on ActiveFirms. Delphi programmers, with this component no longer need a basic design of update programs, and can focus on the logic of their program, such as updating the program to components such as AutoUpgrader.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值