zoukankan      html  css  js  c++  java
  • C#.Net版本自动更新程序及3种策略实现

    C#.Net版本自动更新程序及3种策略实现


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


    通过可行性分析可以使用下面3种方案下载

    1. 局域网共享文件夹下载 
    2. Tcp/ip远程下载 
    3. 通过Web方式下载


    1.局域网共享文件夹下载 

    方式1适合局域网络,功能简单,通过File.Copy()方式从服务器直接复制文件。如建立企业级的VPN网络,也可作为局域网络直接复制。共享文件夹下载实现非常简单,我们只需要在服务器上共享一个文件夹并设定访问权限,然后将最新版本存放在这个目录,升级程序直接从这个目录Copy文件即可。


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


    3.通过Web方式下载。

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



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


    主窗体<->下载控制器<->XmlLoader关系图

    贴图图片

    图解:

    frmUpgrader窗体定义一个下载控制器及2个TreeView组件。
    当点[检查更新]按钮,控制器调用当前下载器的DownloadServerXml()方法从服务器下载XmlServerFiles.xml文件。
    下载后将文件交给XmlLoader分析,分析器创建XmlDocument实例。最后将XML分析器作为FileView构造器参数创建FileView实例。

    FileView两个重要方法:
    LoadTreeViewClient()方法创建客户端文件清单的TreeView。
    LoadTreeViewServer()方法创建服务器端文件清单的TreeView。

    TreeView的数据来源是两个Xml文件。

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


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


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

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



    三种下载器实现:


    贴图图片


    UML图说明: 

    frmUpgrader: 主窗体

    DownloadController:下载控制器,如上图所示,它负责控制实现IDownloader接口的实例。

    IDownloader: 下载器接口,下载器需要实现这个接口。

    LAN_Downloader: 局域网复制文件下载器。

    WebClient_Downloader: 广域网下载器,通过WebClient组件下载文件。

    TcpIp_Downloader: Tcp/ip下载器。需要运行Tcp/ip服务器提供下载服务。

    主窗体有[检查更新]及[开始更新]两个按钮。分别调用下载控制器的CheckUpdate()及Download()方法。



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


    Tcp/IP下载器需要有服务器支持,使用tcp/ip传送文件简单设计思路是:在服务器端启动TcpListener监听客户端的Socket连接,当Client发送连接请求,TcpListener捕获当前请求的Socket,并取到请求命令数据(字符串),然后由命令处理程序分析该字符串,如果字符串头部包含GET_FILE标识则为下载文件。


    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()方法时就开始监听,请看代码。

    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.2abc.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 CSFramework.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" ?> " +
             "<Upgrader> " +
             "<description>本机最近更新清单</description> " +
             "<Application> " +
             "<LastUpdateTime value="" /> " +
             "<Version value="" /> " +
             "</Application> " +
             "<Files> " +
             "</Files> " +
             "</Upgrader> ";
             
             //删除旧的更新文件 
             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;
          }
       }
    }
     
     
    refer to : http://www.csframework.com/archive/1/arc-1-20110626-1655.htm
  • 相关阅读:
    Js $.merge() 函数(合并两个数组内容到第一个数组)
    11.联结表---SQL
    函数作用域
    递归特性
    计算递归函数理解
    递归、问路函数
    全局变量用大写,局部变量用小写
    全局变量与局部变量
    函数形参和实参
    函数和过程
  • 原文地址:https://www.cnblogs.com/kinpauln/p/3656022.html
Copyright © 2011-2022 走看看