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:\PUBLISH\TEST.DLL"。首先TcpListener捕获当前Socket.并接收到命令数据"GET_FILE|D:\PUBLISH\TEST.DLL",通过分析程序发现"GET_FILE"是特殊命令,表示下载文件请求。然后通过socket.SendFile(file="D:\PUBLISH\TEST.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.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 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\" ?> \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 == null) return true; //文件没找到,为不相同,返回True; 
         
         XmlNode node = this.GetFileNode(file.FullPath);
         if (node == null) return 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 == null) return 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

C#.Net版本自动更新程序及3种策略实现的更多相关文章

  1. ASP.NET网站版本自动更新程序及代码[转]

    1.自动更新程序主要负责从服务器中获取相应的更新文件,并且把这些文件下载到本地,替换现有的文件.达到修复Bug,更新功能的目的.用户手工点击更新按钮启动更新程序.已测试.2.环境VS2008,采用C# ...

  2. C#之tcp自动更新程序

    .NETTCP自动更新程序有如下几步骤: 第一步:服务端开启监听 ServiceHost host; private void button1_Click(object sender, EventAr ...

  3. winform自动更新程序实现

    一.问题背景 本地程序在实际项目使用过程中,因为可以操作电脑本地的一些信息,并且对于串口.OPC.并口等数据可以方便的进行收发,虽然现在软件行业看着动不动都是互联网啊啥的,大有Web服务就是高大上的感 ...

  4. C# WINFORM的自动更新程序

    自动更新程序AutoUpdate.exe https://git.oschina.net/victor596jm/AutoUpdate.git 1.获取源码 http://git.oschina.ne ...

  5. winform 通用自动更新程序

    通用自动更新程序 主要功能: 1. 可用于 C/S 程序的更新,集成到宿主主程序非常简单和配置非常简单,或不集成到主程序独立运行. 2. 支持 HTTP.FTP.WebService等多种更新下载方式 ...

  6. .Net自动更新程序GeneralUpdate,适用于wpf,winfrom,控制台应用

    什么是GeneralUpdate: GeneralUpdate是基于.net framwork4.5.2开发的一款(c/s应用)自动升级程序. 第一个版本叫Autoupdate(原博客: WPF自动更 ...

  7. WPF自动更新程序

    WPF AutoUpdater 描述: WPF+MVVM实现的自动更新程序 支持更新包文件验证(比较文件MD5码) 支持区分x86与x64程序的更新 支持更新程序的版本号 支持执行更新策略 截图: 使 ...

  8. android自动更新程序,安装完以后就什么都没有了,没有出现安装成功的界面的问题

    转载自: http://blog.csdn.net/lovexieyuan520/article/details/9250099 在android软件开发中,总是需要更新版本,所以当有新版本开发的时候 ...

  9. Android App版本自动更新

    App在开发过程中,随着业务场景的不断增多,功能的不断完善,早期下载App的用户便无法体验最新的功能,为了能让用户更及时的体验App最新版本,在App开发过程加入App自动更新功能便显得尤为重要.更新 ...

随机推荐

  1. Memched——C#操作

    Memched还是比较简单的,这里把C#的相关操作整理了一下,Mark~ /// <summary> /// 缓存操作类. /// </summary> /// <rem ...

  2. Ubuntu14.04下沙盒数据导入到 Neo4j 数据库(图文详解)

    不多说,直接上干货! 参考博客 http://blog.csdn.net/u012318074/article/details/72793914   (表示感谢) 前期博客 Neo4j沙盒实验申请过程 ...

  3. 以SqlHelper为例论面向对象中封装的使用

    引言: 在使用面向对象方法编写的程序中,会有一些工具类,如Utility,xxHelper等. 比如1)操作数据库的过程,一般步骤都是:1.准备数据库地址.表名等信息:2.建立连接:3.准备要执行sq ...

  4. Windows 10 的功能更新,版本 1809 - 错误 0x80070002

    一般是双硬盘导致的问题,请打开电脑拆掉系统盘以外的硬盘,一般为固态硬盘和物理硬盘同时使用的电脑会出现此错误.

  5. 手把手教你写带登录的NodeJS爬虫+数据展示

    其实在早之前,就做过立马理财的销售额统计,只不过是用前端js写的,需要在首页的console调试面板里粘贴一段代码执行,点击这里.主要是通过定时爬取https://www.lmlc.com/s/web ...

  6. Win10 UI入门 pivot multiable DataTemplate

    this is a dynamic pivot with sliderable navigation and multiableDatatemplate Control 看了 alexis 大哥的pi ...

  7. CorelDRAW X8制作金属质感3D立体按钮

    本教程教您使用CorelDRAW X8制作金属质感3D立体按钮.绘图中主要应用渐变填充技巧为立体按钮表现物体质感和丰富的色彩变化,最后实现的效果也是不错的,是很实用的案例,教程难度一般,完成图如下: ...

  8. 3ds Max制作欧式风格的墙壁路灯效果

    在这篇文章中,我将解释我创建我的形象元宵节的步骤.我只是在寻找一个很好的参考图像在互联网上的东西,我觉得我想要的模型,这个形象.我发现了一个巨大的灯笼形象,但在白天的图片拍摄.我想改变我的形象和显示的 ...

  9. Linux中删除特殊符号文件名文件

    Linux 系统下的文件名长度最多可到256个字符.通常情况下,文件名的字符包括:字母.数字.“.”(点).“_”(下划线)和“-”(连字符). Linux 允许在文件名中使用除上述符号之外的其它符号 ...

  10. java的算法实现冒泡

    package xutao3;public class test1 { public static void main(String[] args) { int[] arr={12,88,66,55, ...