C#.Net版本自动更新程序及3种策略实现
C#.Net版本自动更新程序及3种策略实现
关于自动升级程序
1. 局域网共享文件夹下载 方式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)。
版本更新程序系统框架图 主窗体<->下载控制器<->XmlLoader关系图 图解: frmUpgrader窗体定义一个下载控制器及2个TreeView组件。当点[检查更新]按钮,控制器调用当前下载器的DownloadServerXml()方法从服务器下载XmlServerFiles.xml文件。下载后将文件交给XmlLoader分析,分析器创建XmlDocument实例。最后将XML分析器作为FileView构造器参数创建FileView实例。 FileView两个重要方法: LoadTreeViewClient()方法创建客户端文件清单的TreeView。 LoadTreeViewServer()方法创建服务器端文件清单的TreeView。 TreeView的数据来源是两个Xml文件。 DownloadController 下载控制器,它负责建立下载策略实例及控制当前的下载器。
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标识则为下载文件。
UpgraderClient是tcp/ip客户端的核心类。他控制TcpClient对象, TcpClient对象负责监听来自服务器的请求。 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();
TcpIp_Downloader Tcp/IP下载器方法: 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; } } } 扫一扫加微信
参考文档:
C#.Net C/S快速开发框架V2.2版本介绍 C#.Net局域网版本自动升级解决方案(原创) C#.NET 监视操作系统打开程序(使用Window消息实现全局系统勾子) C#.NET C/S结构版本自动升级解决方案2.0详解 (一) C#.NET C/S结构版本自动升级解决方案之流程图 C#.NET C/S结构版本自动升级解决方案之自我替换 C#.NET C/S结构版本自动升级解决方案之升级策略 C#.NET C/S结构版本自动升级解决方案之升级包实现 C#.NET C/S结构版本自动升级解决方案之TCP服务器 C#.NET C/S结构版本自动升级解决方案开发文档下载 C#程序升级框架-软件版本自动升级系统|C/S框架网 C# Winform软件版本自动升级 | 软件系统自动更新工具 CSFramework版本自动升级程序AutoUpgrader增加日志Log功能 C#源码-[CMS程序]LeadWit CMS.NET 1.0.1_leadwitcms-源码下载 C#源码-[论坛社区]BBSGood论坛程序 ASP.NET版-源码下载
其它资料:
什么是C/S结构? | C/S框架核心组成部分 | C/S框架-WebService部署图 | C/S框架-权限管理 | C/S结构系统框架 - 5.1旗舰版介绍 | C/S结构系统框架 - 功能介绍 | C/S结构系统框架 - 产品列表 | C/S结构系统框架 - 应用展示(图) | 三层体系架构详解 | C/S架构轻量级快速开发框架 | C/S框架网客户案例 | WebApi快速开发框架 | C/S框架代码生成器 | 用户授权注册软件系统 | 版本自动升级软件 | 数据库底层应用框架 | CSFramework.CMS内容管理系统 | |