版本自动更新程序及3种实现策略程序下载(附源码)

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


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


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

程序界面及系统设置
贴图图片
图1-版本升级程序服务器端设置
1. 服务器端程序发布URL地址,该参数用于Web下载器连接的url地址。
2. 服务器端程序发布目录,用于tcp/ip下载器下载的目录。
3. 服务器端程序共享目录,用于Lan下载器下载的目录.
以前3个参数跟据选择的下载器类型做相应设置。

贴图图片
图2-本机配置
本地程序发布目录:用于任一下载器下载文件后存放文件的目录。

贴图图片
图3-下载器设置(更新参数设置,未改名)
选择三种下载器中任何一种作为当前下载器。

贴图图片
图4-Tcp/Ip下载器服务器程序.
当您选择Tcp/Ip下载器升级程序,请先运行服务器端程序。
 
贴图图片
图5-版本自动升级程序主界面

运行程序前先要配置系统参数,配置文件保存在程序运行目录下的upgrader.dat文件。参考上面的所介绍的系统设置界面。跟据选择的下载器类型作相应配置。

如出现以下提示,说明您还没有配置程序目录下无upgrader.dat文件.
贴图图片
图6-没有配置文件提示窗体

服务器程序发布目录结构如下图:
 贴图图片
图7-服务器程序发布目录结构

D:\Shared\是服务器上的目录,该目录存放最新版本文件及XmlServerFiles.xml 文件清单。
清单文件可以通过枚举目录下包括子目录下所有文件自动生成,下次更新版本时只需调整某几项内容。

XmlServerFiles.xml 文件内容

<?xml version="1.0" encoding="utf-8" ?>
<Upgrader>
<description>服务器端文件清单</description>
<Application>
<!--程序最新版本发布时间-->
<LastUpdateTime value="4/26/2007 10:30:32 AM" />
<!--版本号-->
<Version value="1.2.1" />
</Application>
<Files>
<File fullPath=".\Web3Layers.rar" fileName="Web3Layers.rar" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\ip_setup.txt" fileName="ip_setup.txt" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\a.png" fileName="a.png" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\mdimain2.png" fileName="mdimain2.png" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\KAV8.0-Key.R00" fileName="KAV8.0-Key.R00" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\crossthread.png" fileName="crossthread.png" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\newfolder\kav.rar" fileName="kav.rar" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\a\crossthread.png" fileName="crossthread.png" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\VJSDN.Tech.AutoUpgrader.exe" fileName="VJSDN.Tech.AutoUpgrader.exe" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\VJSDN.Tech.AutoUpgradeServer.exe" fileName="VJSDN.Tech.AutoUpgradeServer.exe" lastModify="4/26/2009 9:53:23 AM" />
<File fullPath=".\VJSDN.Tech.AutoUpgraderLib.dll" fileName="VJSDN.Tech.AutoUpgraderLib.dll" lastModify="4/26/2009 9:53:23 AM" />
</Files>
</Upgrader>


AppSettings.cs 系统参数代码实现(序列化对象保存为文件)

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

namespace VJSDN.Tech.AutoUpgraderLib
{
   /// <summary> 
   /// 系统参数。序列化对象保存为文件. 
   /// </summary> 
   [Serializable]
   public class AppSettings
   {
      private string _ServerSharedFolder = "";
      private string _ServerIP = "";
      private int _ServerPort = 0;
      private string _ServerFilesXml = "";
      private string _ServerPublishFolder = ""; //用于tcp/ip下载.服务器上程序文件的发布目录。 
      private string _ServerPublishUrl = "";
      private string _ClientSaveFolder = "";
      private string _ClientFilesXml = "";
      private int _DownloaderType = 0;
      
      
      private static AppSettings _instance = null;
      
      /// <summary> 
      /// 系统参数实例。 
      /// </summary> 
      public static AppSettings Instance
      {
         get
         {
            if (_instance == null) CreateDefault();
            return _instance;
         }
      }
      
      private static void CreateDefault()
      {
         _instance = new AppSettings();
         _instance.ServerFilesXml = "XmlServerFiles.xml";
         _instance.ServerSharedFolder = @"\\192.168.1.2\Shared";
         _instance.ServerPublishUrl = "http://www.vjsdn.com/UserUpload";
         _instance.ServerPublishFolder = @"d:\mypublish\autoupgrader";
         _instance.ServerIP = "192.168.1.1";
         _instance.ServerPort = 12345;
         _instance.DownloaderType = (int)DownloadType.Intranet;
         _instance.ClientFilesXml = "XmlClientFiles.xml";
         _instance.ClientSaveFolder = @"c:\yourfolder\newversion";
         
      }
      
      public static bool HasSetting()
      {
         string file = Application.StartupPath + @"\upgrader.dat";
         return File.Exists(file);
      }
      
      /// <summary> 
      /// 读取参数 
      /// </summary> 
      public static void Read()
      {
         string file = Application.StartupPath + @"\upgrader.dat";
         if (File.Exists(file))
         {
            FileStream fs = File.Open(file, FileMode.OpenOrCreate, FileAccess.Read);
            byte[] bs = new byte[fs.Length];
            int len = fs.Read(bs, 0, bs.Length);
            if (len > 0)
            {
               object o = ZipObject.DecompressionObject(bs);
               _instance = (AppSettings)o;
            }
            fs.Close();
         }
      }
      
      /// <summary> 
      /// 保存参数 
      /// </summary> 
      public static void Write()
      {
         string file = Application.StartupPath + @"\upgrader.dat";
         FileStream fs = File.Open(file, FileMode.OpenOrCreate, FileAccess.Write);
         byte[] bs = ZipObject.CompressionObject(_instance);
         fs.Write(bs, 0, bs.Length);
         fs.Flush();
         fs.Close();
      }
      
      /// <summary> 
      /// 用于内部网络直接Copy的共享目录。 
      /// </summary> 
      public string ServerSharedFolder { get { return _ServerSharedFolder; } set { _ServerSharedFolder = value; } }
      
      /// <summary> 
      /// 用于Tcp/Ip下载的服务器IP。 
      /// </summary> 
      public string ServerIP { get { return _ServerIP; } set { _ServerIP = value; } }
      
      /// <summary> 
      /// 用于Tcp/Ip下载的服务器端口 
      /// </summary> 
      public int ServerPort { get { return _ServerPort; } set { _ServerPort = value; } }
      
      /// <summary> 
      /// 下载类型(策略) 
      /// </summary> 
      public int DownloaderType { get { return _DownloaderType; } set { _DownloaderType = value; } }
      
      /// <summary> 
      /// 发布在服务器上的程序Url地址。如:http://www.vjsdn.com/publish 
      /// </summary> 
      public string ServerPublishUrl { get { return _ServerPublishUrl; } set { _ServerPublishUrl = value; } }
      
      /// <summary> 
      /// 用于tcp/ip下载.服务器上程序文件的发布目录 
      /// </summary> 
      public string ServerPublishFolder { get { return _ServerPublishFolder; } set { _ServerPublishFolder = value; } }
      
      /// <summary> 
      /// 客户端文件目录,下载文件保存的目录。如:c:\myapp 
      /// </summary> 
      public string ClientSaveFolder { get { return _ClientSaveFolder; } set { _ClientSaveFolder = value; } }
      
      /// <summary> 
      /// 客户机器上的文件列表 
      /// </summary> 
      public string ClientFilesXml { get { return _ClientFilesXml; } set { _ClientFilesXml = value; } }
      
      /// <summary> 
      /// 服务器上的文件列表 
      /// </summary> 
      public string ServerFilesXml { get { return _ServerFilesXml; } set { _ServerFilesXml = value; } }
      
   }
}

Common.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.IO.Compression;

namespace VJSDN.Tech.AutoUpgraderLib
{
   public class Common
   {
      //字符转日期 
      public static DateTime StrToDate(string date)
      {
         DateTime ret = DateTime.MinValue;
         if (DateTime.TryParse(date, out ret))
         return ret;
         else
         return DateTime.MinValue;
      }
   }
   
   /// <summary> 
   /// 压缩解压object 
   /// </summary> 
   public class ZipObject
   {
      public static byte[] CompressionObject(object DataOriginal)
      {
         if (DataOriginal == nullreturn null;
         BinaryFormatter bFormatter = new BinaryFormatter();
         MemoryStream mStream = new MemoryStream();
         bFormatter.Serialize(mStream, DataOriginal);
         byte[] bytes = mStream.ToArray();
         MemoryStream oStream = new MemoryStream();
         DeflateStream zipStream = new DeflateStream(oStream, CompressionMode.Compress);
         zipStream.Write(bytes, 0, bytes.Length);
         zipStream.Flush();
         zipStream.Close();
         return oStream.ToArray();
      }
      
      public static object DecompressionObject(byte[] bytes)
      {
         if (bytes == nullreturn null;
         MemoryStream mStream = new MemoryStream(bytes);
         mStream.Seek(0, SeekOrigin.Begin);
         DeflateStream unZipStream = new DeflateStream(mStream, CompressionMode.Decompress, true);
         object dsResult = null;
         BinaryFormatter bFormatter = new BinaryFormatter();
         dsResult = (object)bFormatter.Deserialize(unZipStream);
         return dsResult;
      }
   }
}

FileView.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Threading;

namespace VJSDN.Tech.AutoUpgraderLib
{
   /// <summary> 
   /// 文件视图,文件结构以树形展示。 
   /// </summary> 
   public class FileView
   {
      private XmlLoader _xml = null;
      private ToolStripProgressBar _progress = null;
      
      public FileView(XmlLoader xml, ToolStripProgressBar progress)
      {
         _xml = xml;
         _progress = progress;
      }
      
      //将xml转换为TreeView 
      public void LoadTreeViewServer(TreeView treeView)
      {
         treeView.Nodes.Clear();
         XmlNode node = _xml.XML.SelectSingleNode("Upgrader/Files");
         if (node == nullthrow new Exception("找不到xml文件的Upgrader/Files结点!");
         _progress.Maximum = node.ChildNodes.Count + 1;
         _progress.Minimum = 1;
         _progress.Value = 1;
         
         foreach (XmlNode n in node.ChildNodes)
         {
            CreateNode(treeView, n, null);
            _progress.Value++;
         }
      }
      
      //将xml转换为TreeView.同时与服务器匹配 
      public void LoadTreeViewClient(TreeView treeView, XmlLoader server)
      {
         treeView.Nodes.Clear();
         XmlNode node = _xml.XML.SelectSingleNode("Upgrader/Files");
         if (node == nullthrow new Exception("找不到xml文件的Upgrader/Files结点!");
         _progress.Maximum = node.ChildNodes.Count + 1;
         _progress.Minimum = 1;
         _progress.Value = 1;
         
         foreach (XmlNode n in node.ChildNodes)
         {
            CreateNode(treeView, n, server);
            _progress.Value++;
         }
         
         //如果服务器的版本文件数量大于本地文件数量,视为有更新。 
         if (_xml.HasNewVersion == false && server != null && _xml.FilesCount < server.FilesCount)
         _xml.HasNewVersion = true;
      }
      
      private void CreateNode(TreeView treeView, XmlNode n, XmlLoader comparer)
      {
         //取参数n的Attributies["fullPath"].Value 
         //如:Value=".\b\b\b.txt" 多级目录 
         string fullPath = n.Attributes["fullPath"].Value;
         string[] part = fullPath.Split(new char[] { char.Parse(@"\") });
         TreeNode currNode = null;
         foreach (string p in part)
         {
            if (p == ".") continue;//当前目录,不处理 
            if (p.Length > 3 && p.IndexOf(".") > 0) //文件
            {
               TreeNode node = new TreeNode(p);
               
               if (comparer == null)
               node.ImageIndex = 0; //文件 
               else
               {
                  XmlNode node2 = comparer.GetFileNode(fullPath);
                  
                  if (_xml.CompareNode(node2, n))
                  {
                     node.ImageIndex = 2;
                     node.SelectedImageIndex = 2;
                     _xml.HasNewVersion = true;
                  }
                  else
                  node.ImageIndex = 0;
               }
               
               if (currNode == null)
               treeView.Nodes.Add(node); //加入根结点 
               else
               currNode.Nodes.Add(node);//加入子结点 
               
               return;
            }
            else //子目录
            {
               TreeNode tempNode = FindNode(treeView, currNode, p); //查找当前目录 
               
               if (tempNode == null) //未找到结点.则创建结点
               {
                  TreeNode tmp = new TreeNode(p);
                  tmp.ImageIndex = 1;//子目录 
                  if (currNode == null)
                  treeView.Nodes.Add(tmp); //创建根目录 
                  else
                  currNode.Nodes.Add(tmp); //在当前目录创建子目录 
                  
                  currNode = tmp;
               }
               else
               currNode = tempNode; //保存当前目录,方便查找下一级 
            }
         }
      }
      
      //查找结点,此方法仅在CreateNode方法有效.为CreateNode方法的子方法。 
      private TreeNode FindNode(TreeView treeView, TreeNode currNode, string text)
      {
         if (currNode == null) //在根目录搜索
         {
            foreach (TreeNode node in treeView.Nodes)
            if (node.Text.ToUpper() == text.ToUpper()) return node;//找到 
         }
         else //在当前目录搜索
         {
            foreach (TreeNode node in currNode.Nodes)
            if (node.Text.ToUpper() == text.ToUpper()) return node;//找到 
         }
         return null//未找到 
      }
   }
}

UpgraderClient.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;

namespace VJSDN.Tech.AutoUpgraderLib
{
   /// <summary> 
   /// Tcp/ip下载器客户端 
   /// </summary> 
   public class UpgraderClient
   {
      private OutputInvoke _writeOutput = null;
      
      public UpgraderClient(OutputInvoke writeOutput)
      {
         _writeOutput = writeOutput;
      }
      
      private TcpClient _Client = null;
      private TcpClient Client
      {
         get
         {
            if (_Client == null)
            {
               _Client = new TcpClient();
            }
            return _Client;
         }
      }
      
      public void CloseClient()
      {
         if (_Client != null && _Client.Connected) _Client.Close();
      }
      
      /// <summary> 
      /// 下载文件 
      /// </summary> 
      public bool DownloadFile(string file, string savePath)
      {
         try
         {
            TcpClient client = new TcpClient();
            //client.ReceiveTimeout = 1000 * 60; 
            
            if (client == null || file.Trim() == "") return false;
            
            TryConnect(client, AppSettings.Instance.ServerIP, AppSettings.Instance.ServerPort); //连接服务器 
            if (!client.Connected) return false;//连线线失败,退出 
            
            byte[] bs = Encoding.Unicode.GetBytes("GET_FILE|" + file);
            client.Client.Send(bs); //发送请求 
            
            //开始接受数据.... 
            NetworkStream ns = client.GetStream();
            MemoryStream ms = new System.IO.MemoryStream();
            
            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(); //关闭网络数据流 
               
               if (ms.Length > 0)
               {
                  FileStream fs = File.Open(savePath, FileMode.OpenOrCreate, FileAccess.Write);
                  fs.Write(ms.ToArray(), 0, (int)ms.Length);
                  fs.Flush();
                  fs.Close(); //关闭文件流 
                  if (_writeOutput != null) _writeOutput("文件已下载:" + file);
               }
               
               ms.Close(); //关闭内存流 
               client.Close();//关闭网络套接 
               return true;
            }
            catch
            {
               return false;
            }
         }
         
         private string Byte2Str(byte[] buffer)
         {
            string msg = Encoding.Unicode.GetString(buffer).Replace("\0", "");
            return msg.Trim();
         }
         
         private void TryConnect(TcpClient client, string hostIP, int hostPort)
         {
            try
            {
               client.Connect(IPAddress.Parse(hostIP), hostPort); //连接服务器 
            }
            catch
            {
               throw new Exception("主机已关闭或网络存在问题,不能建立连线!");
            }
         }
      }
   }

UpgraderServer.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Windows.Forms;
using System.IO;

namespace VJSDN.Tech.AutoUpgraderLib
{
   
   //跨线程调用控件显示消息 
   public delegate void OutputInvoke(string msg);
   
   /// <summary> 
   /// tcp/ip下载器服务器 
   /// </summary> 
   public class UpgraderServer
   {
      private TcpListener _ServerListener = null//本地消息监听器 
      private Thread _ServerListenerThread = null//监听器使用的线程 
      private bool _ServerRunning = false;
      
      private Form _owner = null;
      private OutputInvoke _writeOutput = null;
      
      public UpgraderServer(Form owner, OutputInvoke method)
      {
         _owner = owner;
         _writeOutput = method;
      }
      
      //启动聊天服务器程序. 参数说明 ip:本地消息服务器IP地址; port:端口 
      public void StartListening(string ip, int port)
      {
         if (_ServerRunning) return;
         
         //构建监听器 
         _ServerListener = new TcpListener(IPAddress.Parse(ip), port);
         _ServerListener.Start(255);
         _ServerRunning = true;
         
         //启动线程 
         _ServerListenerThread = new Thread(new ThreadStart(DoStartServerListener));
         _ServerListenerThread.IsBackground = true;
         _ServerListenerThread.Start();
      }
      
      //关闭聊天服务器程序. 
      public void Stop()
      {
         _ServerRunning = false;
         _ServerListenerThread.Abort(101);
         _ServerListenerThread = null;
         _ServerListener.Stop();
      }
      
      /// <summary> 
      ///启动服务器程序. 
      /// </summary> 
      private void DoStartServerListener()
      {
         //监听客户连线请求 
         while (_ServerRunning)
         {
            try
            {
               if (_ServerListener == nullreturn//防止其它地方关闭监听器 
               Application.DoEvents();
               Socket socket = _ServerListener.AcceptSocket(); //有客户请求连接 
               if (socket == nullcontinue;
               
               byte[] buffer = new Byte[socket.ReceiveBufferSize];
               int i = socket.Receive(buffer); //接收请求数据. 
               if (i <= 0) continue;
               
               string msg = Encoding.Unicode.GetString(buffer).Replace("\0", "");
               msg = msg.Trim();
               if (msg.ToUpper().StartsWith("GET_FILE")) //命令字符串:GET_FILE|.\a.dll
               {
                  string[] sps = msg.Split(new char[] { char.Parse("|") });
                  string file = sps[1]; //取出文件a.dll 
                  this.SendFile(socket, file);
               }
               socket.Close();// 
            }
            catch (Exception ex)
            {
               if (ex is ThreadAbortException)
               {
                  if ((ex as ThreadAbortException).ExceptionState.ToString() == "101")
                  {
                     _ServerRunning = false;
                     ShowLog("用户关闭服务器.");
                  }
                  else
                  ShowLog(ex.Message);
               }
               else
               {
                  ShowLog(ex.Message);
               }
            }
         }
      }
      
      private void SendFile(Socket socket, string file)
      {
         if (File.Exists(file))
         {
            ShowLog("获取文件[" + file + "]....");
            socket.SendFile(file);
            ShowLog("[" + file + "]已发送.");
         }
         else
         {
            socket.Send(Encoding.Unicode.GetBytes("FILE_NOT_FOUND")); //通知客户程序 
            ShowLog("[" + file + "]不存在.");
         }
      }
      
      //在_ServerListener线程内操作主线程上的控件。 
      private void ShowLog(string log)
      {
         //访问其它线程上的控件,需要用Invoke方法 
         _owner.Invoke(_writeOutput, log);
      }
   }
}


解决方案视图:
贴图图片
附件已打包下面的源代码:
VJSDN.Tech.AutoUpgrader.exe 自动升级程序主程序
VJSDN.Tech.AutoUpgradeServer.exe tcp/ip下载器服务器程序

VJSDN.Tech.AutoUpgraderLib.dll 内的文件已分散在3个帖内

******* 请在运行程序前先配置系统参数 *******
该帖子最后修改:2009-8-13 15:25:16
posted @ 2009-09-25 09:57  海浪~~  阅读(1276)  评论(0编辑  收藏  举报