.Net remoting方法實現簡單的線上升級(上篇:更新文件)

来源:http://www.cnblogs.com/lovecsharp094/archive/2016/06/25/5616880.html
-Advertisement-
Play Games

一、前言: 最近做一個簡單的線上升級Demo,使用了微軟較早的.Net Remoting技術來練手。 簡單的思路就是在伺服器配置一個Remoting對象,然後在客戶端來執行Remoting對象中的方法。 過程: (1) 讀取本地dll文件的名稱與版本號,與伺服器的進行對比 (2) 確認需要升級的文件 ...


一、前言:

      最近做一個簡單的線上升級Demo,使用了微軟較早的.Net Remoting技術來練手。

        簡單的思路就是在伺服器配置一個Remoting對象,然後在客戶端來執行Remoting對象中的方法。

        過程:

        (1) 讀取本地dll文件的名稱與版本號,與伺服器的進行對比

        (2) 確認需要升級的文件名稱與版本號並告訴伺服器,伺服器將其複製到一個臨時文件夾並壓縮成zip

        (3) 將伺服器的zip下載到本地的臨時文件夾,並解壓。

        定義伺服器端為UpdateServer,其配置文件為:

<configuration>
  <system.runtime.remoting>
    <application>
      <service>
        <wellknown type="UpdateLibrary.RemoteObject, UpdateLibrary" mode="Singleton" objectUri="RemoteObject.rem"/>
      </service>
      <channels>
        <channel ref="http" port="8989">
        </channel>
      </channels>
    </application>
  </system.runtime.remoting>
  <appSettings>
    <!--<add key="Dir" value="E:\server"/>-->
  </appSettings>
</configuration>

        定義客戶端為UpdateClient,其配置文件為:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="ServerUrl" value="127.0.0.1:8989"/>
    <add key="Modules" value="BIMCoreDB"/>
    <add key="BufferLength" value="100"/>
  </appSettings>
</configuration>

       定義兩端共同調用的dll為UpdateLibrary。


二、伺服器端代碼:

     程式主入口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Configuration;
using UpdateLibrary;

namespace UpdateServer
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主入口點。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            LoadConfig();
            Application.Run(new FormServer());
        }

        private static void LoadConfig()
        {
            Config.Dir = System.IO.Path.Combine(Application.StartupPath, "serverfiles"); //更新包所在位置
            Config.TempDir = System.IO.Path.Combine(Application.StartupPath, "temp");  //臨時文件夾,用於放更新文件的地方。
        }
    }
}

          伺服器窗體後臺代碼:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
namespace UpdateServer
{
    public partial class FormServer : Form
    {
        public FormServer()
        {
            InitializeComponent();
            try
            {
                //remoting配置
                RemotingConfiguration.Configure(string.Format("{0}\\UpdateServer.exe.config", Application.StartupPath), false);
            }
            catch (Exception e)
            {
                MessageBox.Show(this, e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        private void FormServer_Load(object sender, EventArgs e)
        {
            lbl_time.Text = "當前時間:"+DateTime.Now.ToString("T");
            tm_Server = new Timer();
            tm_Server.Tick += tm_Server_Tick;
            tm_Server.Interval = 1000;
            tm_Server.Enabled = true;
        }
        void tm_Server_Tick(object sender,EventArgs e)
        {
            lbl_time.Text = string.Empty;
            lbl_time.Text = "當前時間:" + DateTime.Now.ToString("T");
        }
    }
}

三、UpdateLibrary:

        UpdateLibrary類庫包含三個類:
        (1)Config類:用於提取配置文件中的信息。

        (2)ZipHelper類:第三方庫,用於文件壓縮與解壓縮。

        (3)RemoteObject類:remoting對象,實現兩端之間所需要的方法。

        Config代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace UpdateLibrary
{
    /// <summary>
    /// 將配置文件中的信息傳給Config對象
    /// </summary>
    public static class Config
    {
        public static string Dir { get; set; }
        public static string TempDir { get; set; }
        public static string[] Modules { get; set; }
        public static int BufferLength { get; set; }
        public static string ServerUrl { get; set; }
    }
}

        ZipHelper代碼:(比較實用的壓縮與解壓縮方法)

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using ICSharpCode.SharpZipLib.Zip;
  6 using ICSharpCode.SharpZipLib.Checksums;
  7 using System.IO;
  8 
  9 namespace UpdateLibrary
 10 {
 11     public class ZipHelper
 12     {
 13         #region  壓縮
 14         /// <summary>
 15         /// 壓縮文件
 16         /// </summary>
 17         /// <param name="sourceFilePath"></param>
 18         /// <param name="destinationZipFilePath"></param>
 19         public static void CreateZip(string sourceFilePath, string destinationZipFilePath)
 20         {
 21             if (sourceFilePath[sourceFilePath.Length - 1] != System.IO.Path.DirectorySeparatorChar)
 22                 sourceFilePath += System.IO.Path.DirectorySeparatorChar;
 23             ZipOutputStream zipStream = new ZipOutputStream(File.Create(destinationZipFilePath));
 24             zipStream.SetLevel(6);  // 壓縮級別 0-9
 25             CreateZipFiles(sourceFilePath, zipStream);
 26             zipStream.Finish();
 27             zipStream.Close();
 28         }
 29         /// <summary>
 30         /// 遞歸壓縮文件
 31         /// </summary>
 32         /// <param name="sourceFilePath">待壓縮的文件或文件夾路徑</param>
 33         /// <param name="zipStream">打包結果的zip文件路徑(類似 D:\WorkSpace\a.zip),全路徑包括文件名和.zip擴展名</param>
 34         /// <param name="staticFile"></param>
 35         private static void CreateZipFiles(string sourceFilePath, ZipOutputStream zipStream)
 36         {
 37             Crc32 crc = new Crc32();
 38             string[] filesArray = Directory.GetFileSystemEntries(sourceFilePath);
 39             foreach (string file in filesArray)
 40             {
 41                 if (Directory.Exists(file))                     //如果當前是文件夾,遞歸
 42                 {
 43                     CreateZipFiles(file, zipStream);
 44                 }
 45                 else                                            //如果是文件,開始壓縮
 46                 {
 47                     FileStream fileStream = File.OpenRead(file);
 48                     byte[] buffer = new byte[fileStream.Length];
 49                     fileStream.Read(buffer, 0, buffer.Length);
 50                     string tempFile = file.Substring(sourceFilePath.LastIndexOf("\\") + 1);
 51                     ZipEntry entry = new ZipEntry(tempFile);
 52                     entry.DateTime = DateTime.Now;
 53                     entry.Size = fileStream.Length;
 54                     fileStream.Close();
 55                     crc.Reset();
 56                     crc.Update(buffer);
 57                     entry.Crc = crc.Value;
 58                     zipStream.PutNextEntry(entry);
 59                     zipStream.Write(buffer, 0, buffer.Length);
 60                 }
 61             }
 62         }
 63         #endregion
 64 
 65         #region 解壓縮
 66 
 67         public static void UnZip(Stream stream, string targetPath)
 68         {
 69             using (ZipInputStream zipInStream = new ZipInputStream(stream))
 70             {
 71                 ZipEntry entry;
 72                 while ((entry = zipInStream.GetNextEntry()) != null)
 73                 {
 74                     string directorName = Path.Combine(targetPath, Path.GetDirectoryName(entry.Name));
 75                     string fileName = Path.Combine(directorName, Path.GetFileName(entry.Name));
 76                     // 創建目錄
 77                     if (directorName.Length > 0)
 78                     {
 79                         Directory.CreateDirectory(directorName);
 80                     }
 81                     if (fileName != string.Empty && !entry.IsDirectory)
 82                     {
 83                         var ext = System.IO.Path.GetExtension(fileName);
 84                         using (FileStream streamWriter = File.Create(fileName))
 85                         {
 86                             int size = 4096;
 87                             byte[] data = new byte[4 * 1024];
 88                             while (true)
 89                             {
 90                                 size = zipInStream.Read(data, 0, data.Length);
 91                                 if (size > 0)
 92                                 {
 93                                     streamWriter.Write(data, 0, size);
 94                                 }
 95                                 else break;
 96                             }
 97                         }
 98                     }
 99                 }
100             }
101         }
102         #endregion
103     }
104 }

        RemoteObject代碼:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Diagnostics;
 5 using System.Text;
 6 using System.IO;
 7 using System.Collections;
 8 using Newtonsoft.Json;
 9 
10 
11 
12 namespace UpdateLibrary
13 {
14     public class RemoteObject : MarshalByRefObject
15     {      
16         public string GetUpdateFileVersion()
17         {           
18             System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(Config.Dir);         
19             System.IO.FileInfo[] files = dir.GetFiles("*.dll");  //獲取伺服器端所有dll文件 
20 
21             List<string> fileinfo = new List<string>();//記錄文件名與文件版本
22             foreach(var file in files)
23             {             
24                 string filename = System.IO.Path.GetFileNameWithoutExtension(file.ToString());//獲取文件名稱
25                 fileinfo.Add(filename);
26                 FileVersionInfo ver = FileVersionInfo.GetVersionInfo(System.IO.Path.Combine(Config.Dir, file.ToString()));
27                 string fileversion = ver.FileVersion; //獲取文件的版本
28                 fileinfo.Add(fileversion);
29             }
30             string SendData = JsonConvert.SerializeObject(fileinfo);//轉Json
31             return SendData;
32         }
33 
34         public IList CreateZipfile(string str_r)
35         {
36             List<string> templist = JsonConvert.DeserializeObject<List<string>>(str_r);//接收到確認更新的文件名
37 
38             foreach (string filename in templist)  //把需要更新的文件都複製到臨時文件夾中
39             {
40                 string updatefile = Path.Combine(Config.Dir, filename + ".dll");
41                 File.Copy(updatefile, Path.Combine(Config.TempDir, filename + ".dll"), true);
42                 System.IO.File.SetAttributes(Path.Combine(Config.TempDir, filename + ".dll"), System.IO.FileAttributes.Normal);//去掉文件只讀屬性
43             }
44 
45             string tempzippath=Path.Combine(Config.Dir,"tempzip");//臨時壓縮包路徑,預設更新文件夾下的tempzip文件夾
46             if(Directory.Exists(tempzippath)==false)  //判斷是否有安放壓縮包的地方
47             {
48                 Directory.CreateDirectory(tempzippath);
49             }
50 
51             ZipHelper.CreateZip(Config.TempDir, Path.Combine(tempzippath, "Update.zip"));//將臨時文件夾內的文件都壓縮到tempzip文件夾下的update.zip
52             System.IO.FileInfo  f = new FileInfo(Path.Combine(tempzippath,"Update.zip"));//獲得該壓縮包的大小
53             IList SendData = new ArrayList();
54             SendData.Add(Path.Combine(tempzippath, "Update.zip"));  //得到壓縮包名稱
55             SendData.Add(f.Length);  //得到壓縮包文件大小
56             return SendData;
57         }
58         public byte[] GetFile(string name, int start, int length)
59         {
60             using (System.IO.FileStream fs = new System.IO.FileStream(System.IO.Path.Combine(Config.TempDir, name), System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite))
61             {
62                 byte[] buffer = new byte[length];
63                 fs.Position = start;
64                 fs.Read(buffer, 0, length);
65                 return buffer;
66             }
67         }
68 
69         public void Finish(string name)
70         {
71             // File.Delete(System.IO.Path.Combine(Config.TempDir, name)); //刪除壓縮包文件夾
72         }
73     }
74 }

四、客戶端端代碼:   

        程式主入口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Configuration;
using UpdateLibrary;

namespace UpdateClient
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主入口點。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            LoadConfig(args);
            Application.Run(new FormClient());
        }
        private static void LoadConfig(string[] args)
        {
            Config.Dir = System.IO.Path.Combine(Application.StartupPath,"localfiles");//本地文件位置
            Config.TempDir = System.IO.Path.Combine(Application.StartupPath, "temp");//本地放更新文件的位置
            Config.ServerUrl = ConfigurationManager.AppSettings["ServerUrl"].ToString();//設置伺服器Url
            Config.Modules =ConfigurationManager.AppSettings["Modules"].ToString().Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);//更新文件的名稱
            Config.BufferLength = int.Parse(ConfigurationManager.AppSettings["BufferLength"].ToString());   //緩存大小
        }
    }
}

         

           第一個窗體FormClient,用於比對文件,如果有更新則提供按鈕進入更新窗體FormUpdate

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Diagnostics;
  6 using System.Drawing;
  7 using System.Linq;
  8 using System.Text;
  9 using System.Windows.Forms;
 10 using UpdateLibrary;
 11 using System.IO;
 12 using Newtonsoft.Json;
 13 
 14 namespace UpdateClient
 15 {
 16     public partial class FormClient : Form
 17     {
 18         Dictionary<string, string> Localupdate = new Dictionary<string, string>();  //確認本地需要文件
 19         Dictionary<string, string> ConfirmUpdate = new Dictionary<string, string>();  //確認需要更新的文件並告訴伺服器的
 20         Dictionary<string, string> ConfirmAdd = new Dictionary<string, string>();  //確認需要新增的文件
 21         public FormClient()
 22         {
 23             InitializeComponent();
 24             btn_update.Enabled = false;
 25             int ScreenWidth = Screen.PrimaryScreen.WorkingArea.Width;
 26             int ScreenHeight = Screen.PrimaryScreen.WorkingArea.Height;
 27             int x = ScreenWidth - this.Width - 5;
 28             int y = ScreenHeight - this.Height - 5;
 29             this.Location = new Point(x, y);
 30         }
 31 
 32         private void btn_update_Click(object sender, EventArgs e)
 33         {
 34             Form updatewindow = new FormUpdate(ConfirmUpdate);
 35             updatewindow.Show();
 36             this.Hide();
 37         }
 38 
 39         private void FormClient_Load(object sender, EventArgs e)
 40         {
 41                Dictionary<string, string> localfileversion = new Dictionary<string, string>();
 42                foreach (string module in Config.Modules)
 43                {
 44                    string filepath = System.IO.Path.Combine(Config.Dir, module + ".dll");
 45                    FileVersionInfo ver = FileVersionInfo.GetVersionInfo(filepath);
 46                    string dllVersion = ver.FileVersion;
 47                    localfileversion.Add(module, dllVersion);   //文件名-版本
 48                }
 49 
 50                //文件對比
 51               try
 52               {
 53                    RemoteObject remoteObject = (RemoteObject)Activator.GetObject(typeof(RemoteObject), string.Format("http://{0}/RemoteObject.rem", Config.ServerUrl));
 54                    //獲取伺服器更新包的版本號
 55 
 56                    string SFVersion = remoteObject.GetUpdateFileVersion();//獲取伺服器端更新文件的名稱與版本號
 57                    List<string> Recieve = new List<string>();
 58                    Recieve = JsonConvert.DeserializeObject<List<string>>(SFVersion);//轉成泛型
 59                    Dictionary<string, string> serverfileversion = new Dictionary<string, string>();//轉成字典型
 60                    for (int i = 0; i < Recieve.Count; i += 2)
 61                    {
 62                         serverfileversion.Add(Recieve[i], Recieve[i + 1]);
 63                    }
 64 
 65                    if (serverfileversion.Count > 0)  //是否有更新文件
 66                    {
 67                        foreach (var serverkey in serverfileversion.Keys)
 68                       {
 69                            if (localfileversion.ContainsKey(serverkey)) //本地是否有更新文件的名稱,沒有說明是新增的
 70                           {
 71                                if (localfileversion[serverkey] == serverfileversion[serverkey]) //版本號相同?
 72                                {
 73                                     serverfileversion.Remove(serverkey);//不需要更新
 74                                }
 75                                else
 76                               {
 77                                    ConfirmUpdate.Add(serverkey, serverfileversion[serverkey]); //確認更新的
 78                                    Localupdate.Add(serverkey, localfileversion[serverkey]); //本地的版本
 79                               }
 80                           }
 81                           else
 82                           {
 83                                 ConfirmAdd.Add(serverkey, serverfileversion[serverkey]);//確認新增文件,用於提示
 84                           }
 85                       }
 86                    }
 87                   else
 88                   {
 89                       lblmessage.Text = "暫無更新文件";
 90                       btn_update.Visible = false;
 91                   }
 92             }
 93             catch(Exception ex)
 94             {
 95                 lblmessage.Text = ex.ToString();
 96                 btn_update.Visible = false;
 97             }
 98          
 99             if(ConfirmAdd.Count==0 && ConfirmUpdate.Count==0)
100             {
101                     lblmessage.Text = "沒有需要更新的模塊";
102                     btn_update.Visible = false;
103             }
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0. 沒有找到一款中意的分頁插件,又不想使用現成的(醜到爆),所以自己動手造一個吧 先看下效果(其實也不咋滴...): 我的小站地址:我的Bootstrap小站; PS:(問博客園:為什麼老是刪我的置頂隨便?上一篇閱讀量都快500了,也分分鐘給我從首頁刪掉...真是無語了<博客園地址:http:// ...
  • 第一次寫,小緊張! 即將畢業了,現在將我畢業設計中用到的小的編程技術以及自己的一些理解分享出來,希望可以做點小貢獻。 首先要感謝網上各路大神無私的分享,沒有你們,就沒有我的收穫。 在大四之前,對於編程只是學習過簡單的C語言,從來沒有接觸過工程實踐。最後的畢業設計肯定要開發程式,於是認真學習了一段時間 ...
  • Async in C# 5.0(C#中的非同步編程Async) 蝸牛翻譯之第一章 ...
  • 交流QQ群 ASP.NET鷹組 460845632 我會傾囊相授 我們要做微信支付當配置好微信微信商戶和支付配置之後我們首先應該看 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1# 這是微信統一下單的參數,我將這個參數做成 ...
  • 1 /// <summary> 2 /// ************************************************* 3 /// 類名:MP3幫助類 4 /// 修改日期:2016/06/25 5 /// 作者:董兆生 6 /// 聯繫方式:QQ490412323 7 // ...
  • 在FormPanel中按回車按鍵,會觸發預設按鈕的click事件。設置方法為在FormPanel中設置DefaultButton屬性,如果沒有設置這個屬性,預設為最後一個按鈕。 1.預設最後一個按鈕為預設按鈕 2.以數字編號指點預設按鈕 3.用ID指定預設按鈕 4.用選擇器指定預設按鈕 視圖的完整代 ...
  • 使用VS2015進行C++開發的6個主要原因 使用Visual Studio 2015進行C++開發 在今天的 Build 大會上,進行了“將你的 C++ 代碼轉移至 VS2015 的 6 個原因”的演講,其中探討了 VS2015 中對於 C++ 開發者們更有用的新功能。自從它在 2015 年七月的 ...
  • C#——類 一、String 類 系統內置的處理字元串類型的函數方法類。方便我們對字元串類型進行一系列的處理。 1、Length:獲取字元串的長度,返回一個int類型的值 string x=Console.ReadLine();//小string是大String的快捷方式 int i = x.Len ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...