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

本文介绍了一种C/S架构下的版本自动更新程序设计方案,包括通过XML文件管理文件版本、三种不同下载策略(局域网共享、TCP/IP远程及Web下载)的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:http://blog.chinaunix.net/uid-20729421-id-765215.html
版本自动更新程序及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下载器接口定义

///
/// 下载器接口 
///

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);
}


下载器类型
///
/// 下载器类型 
///

public  enum DownloadType
{
   Intranet = 1,
   TcpIp = 2,
   WebDownload = 3
}

下载控制器,该控件器可以创建3种不同的下载器。
///
/// 下载控制器,该控件器可以创建3种不同的下载器。 
/// 策略模式应用。 
///

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;
   }
   
    ///
   /// 跟据下载类型创建3种不同的下载策略 
   ///

    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;
   }
}



文件对象定义

///
/// 文件对象 
///

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;
   }
   
    ///
   /// 纯文件名,不包含路径。:如upgrader.exe 
   ///

    public  string Name {  get {  return _name; }  set { _name = value; } }
   
    ///
   /// 文件完整路径。如:[.\8.8.8.2\abc.dll] 
   ///

    public  string FullPath {  get {  return _FullPath; }  set { _FullPath = value; } }
   
    ///
   /// 最后更新时间。 
   ///

    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
{
    ///
   /// XML文件解释器,分析服务器/客户端文件清单。 
   ///

    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; } }
      
       ///
      /// 创建空的Xml文件 
      ///

       /// 
       public  static XmlLoader CreateEmpty( string xmlFilePath)
      {
          string xml =
         " \r\n" +
         " \r\n" +
         "本机最近更新清单 \r\n" +
         " \r\n" +
         " \r\n" +
         " \r\n" +
         " \r\n" +
         " \r\n" +
         " \r\n" +
         " \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 "";
      }
      
       ///
      /// 比较服务器与本机文件的最后更新时间。 
      /// 返回True:表示可以更新。False:服务器与本机文件版本一致。 
      ///

       /// 服务器上的文件 
       /// 
       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;
      }
      
       ///
      /// 比较两个XmlNode的日期大小 
      ///

       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;
      }
      
       ///
      /// 在客户端的Xml记录文件加入更新记录 
      ///

       /// 
       /// 
       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);
      }
      
       ///
      /// 获取文件数量 
      ///

       public  int FilesCount
      {
          get
         {
            XmlNode node = _xml.SelectSingleNode("Upgrader/Files");
             return node.ChildNodes.Count;
         }
      }
      
       ///
      /// 获取文件列表 
      ///

       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;
      }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值