C# 自动更新 在线更新 自动升级 版本控制设计及其实现方式 差异化的文件升级 适用winform,wpf等基于CS架构项目

在很多实际的项目开发中,我们使用了CS架构的开发方式,为什么需要使用CS架构设计?我们在开发企业级的应用软件 的时候,通常是伴随着网络通信服务的。大多数都是应用软件+数据库(或是上位机软件+实际PLC设备)

设计如下所示:

 

 CS架构设计程序最大的好处,就是很多个客户端,可以提供一致的访问体验,可以部署到局域网的任意的电脑,只要网络保持畅通即可。比如说,登录之后,显示数据库的一些信息,或是显示PLC数据的信息。各个客户端都是显示服务器的数据的,因为想看数据的,大概率不是一个人而已。

但是上面的设计还有一定的问题:

1. 直接怼数据库,或是PLC,会导致安全性不高,通过反编译客户端的代码,可能可以轻松还原出数据库的连接字符串,或是PLC的IP地址,端口等信息,进而危害整个系统的安全性。

2. 客户端不容易实现自动更新的功能,如果已经分发了几十个几百个客户端,那么统一的更新升级,将是非常不容易的事。

 

那么怎么设计比较好呢?

 

 正确的设计如上图所以,服务器程序独立出来,服务器承上启下,提供各种各样的接口给客户端,并提供客户端自动更新的实现支持。

好了,接下来的重点是如何实现你的自动更新的功能。

这时候需要使用一个组件和一个exe的更新程序来实现:

 

HslCommunication 软件,详细可以参考下面的地址:

github地址:https://github.com/dathlin/HslCommunication

官网:http://www.hslcommunication.cn

加群咨询学习信息:http://www.hslcommunication.cn/

在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装如果需要教程:Nuget安装教程:http://www.cnblogs.com/dathlin/p/7705014.html

Install-Package HslCommunication

组件的api地址:http://api.hslcommunication.cn

 

Upgrade.exe 软件,这是一个用于更新的软件
下载地址:http://www.hsltechnology.cn/Files/Upgrade.exe

简单的界面截图:

 

 

接下来说明如何使用自动更新的软件,这个 Upgrade 软件执行的操作及原理是什么呢?本质上就是连接远程服务器,将服务器指定目录下的所有文件(通常是客户端软件,一个软件通常是有个主exe程序,还有很多的dll,甚至可能包括很多数据文件)下载到本地的Upgrade程序所在的目录。

我们假设开发好的客户端软件长这个样子:

 

我们看到客户端有个exe,主启动程序名称为  HslCommunicationDemo.exe

好了,我们可以来写服务器的程序了,新建一个控制台的项目,并且从nuget安装hslcommunication组件

 

 

 

 

库安装好了,我们来写服务器支持更新的代码部分

	internal class Program
	{
		static void Main( string[] args )
		{
			// 实例化exe
			HslCommunication.Enthernet.NetSoftUpdateServer netSoftUpdate = new HslCommunication.Enthernet.NetSoftUpdateServer( "Upgrade.exe" );
			// 客户端程序放在服务器当前目录的Client里面
			netSoftUpdate.FileUpdatePath = System.IO.Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Client" );
			netSoftUpdate.ServerStart( 12345 );            // 绑定的更新端口号信息


			Console.ReadLine( );
		}
	}

其实就是三行代码的事,服务器核心支持更新的代码已经写完成了,意思就是客户端的程序放到 目录 Client 下面。启动的端口为 12345。  

 

 

 

 

我们把写好的客户端的程序放到了这个目录里,我们现在来初步验证下,当前的更新程序是否好使。怎么验证呢?

第一步我们先启动服务器端的程序。

 

 

启动之后,我们使用cmd来启动这个更新程序,因为还需要传入参数,所以不能直接双击打开这个exe

 

 

然后就可以看到,正在下载文件了。

 

 

下面的截图就是下载好了文件之后启动demo程序的例子。

 

 

 

好了,到这一步我们基本测试成功了上面的功能。这个更新程序是把服务器的目前下载到当前的目录里去。那么怎么样真正的实现更新呢?可以参考下面的流程信息

 

 

 

 

 

那么现在就是实现上述流程即可的,客户端启动时需要和服务器通信,并拿到版本号ID信息。版本号一般都是如下的方法

1、整数类型,版本 1,2,3,4,5,6,。。。。。。。 100000

2.  实数类型,1.0   ,   1.1,    2.0,     10.0

3.  版本号信息 例如 1.0.0   1.0.1   2.0.0

 

这里关于如何取版本号,可以使用任何网络通信实现,哪怕是写socket也行。不过直接使用hslcommunication来实现,会更加的便捷。我们继续改造服务器的代码

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp4
{
	internal class Program
	{
		static void Main( string[] args )
		{
			// 实例化exe
			HslCommunication.Enthernet.NetSoftUpdateServer netSoftUpdate = new HslCommunication.Enthernet.NetSoftUpdateServer( "Upgrade.exe" );
			// 客户端程序放在服务器当前目录的Client里面
			netSoftUpdate.FileUpdatePath = System.IO.Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Client" );
			netSoftUpdate.ServerStart( 12345 );            // 绑定的更新端口号信息

			// 实例化服务
			Services services = new Services( );
			HslCommunication.MQTT.MqttServer mqttServer = new HslCommunication.MQTT.MqttServer( );
			// 注册服务
			mqttServer.RegisterMqttRpcApi( services );
			// 启动服务
			mqttServer.ServerStart( 1883 );


			Console.WriteLine( "启动成功!" );
			Console.ReadLine( );
		}

		/// <summary>
		/// 服务类,提供各种接口
		/// </summary>
		public class Services
		{
			/// <summary>
			/// 获取客户端的版本号信息
			/// </summary>
			/// <returns>版本号</returns>
			[HslCommunication.Reflection.HslMqttApi()]
			public string GetClientApi( )
			{
				return Version;
			}

			public string Version { get; set; } = "1.0.0";   // 服务器上当前最新版本的客户端的版本号,这个值在你更新客户端版本后,就需要手动更新

		}
	}
}

 

这样就写好了服务器端的代码了,已经可以给到客户端的版本号了。那么我们能不能初步测试下接口到底行不行呢?当然可以的,使用 HslCommunicationDemo程序,可以进行便捷的测试,下载地址:

http://www.hsltechnology.cn/Home/Download

 

 

 

点击这个就可以下载到最新的demo版本(这个demo程序的版本更新就是使用上面的机制实现的),我们现在来验证一下,先打开服务器的程序,再打开demo测试界面:

 

 

 

 

那么我们就看到了结果

 

 

 

当然了,这个注册服务接口的功能,不仅仅用于传递版本号,实际上你可以从服务器获取任何的数据,都可以使用服务接口来实现的。对客户端是很简单的,那么我们回到客户端写代码!

 

按照上面的流程来说,客户端在启动的时候,就需要连接服务器,读取服务器的版本号,然后和当前的版本号做对比,如果发现有新版本更新,就启动当前目录的 Upgrade.exe 软件,好了,现在思路已经非常清晰了,来写代码

 

 

 

 

客户端启动就执行的话,我们一般会放到主窗体的load事件(或是主窗体实例化的事件里)里,当然如果你的客户端是初始界面一个支持登录操作的界面,可以在按钮点击登录时,连接服务器校验版本号。我们就以load窗体为例子。

		private string currentVersion = "1.0.0";
		private void FormLoad_Load( object sender, EventArgs e )
		{
			HslCommunication.MQTT.MqttSyncClient client = new HslCommunication.MQTT.MqttSyncClient( new HslCommunication.MQTT.MqttConnectionOptions( )
			{
				IpAddress = "127.0.0.1", // 服务器程序所在电脑的ip地址,这里是本机测试,就写 127.0.0.1
				Port = 1883,  // 端口,和服务器程序的配置一致
							  //Credentials = new HslCommunication.MQTT.MqttCredential("admin", "123456"), // 如果配置了用户名及密码的话,就是这么设置
			} );

			HslCommunication.OperateResult<string> readVersion = client.ReadRpc<string>( "Services/GetClientApi", new { } );
			if (readVersion.IsSuccess)
			{
				// 获取成功,和当前的版本号对比
				if( new HslCommunication.BasicFramework.SystemVersion(readVersion.Content) > new HslCommunication.BasicFramework.SystemVersion( currentVersion ))
				{
					// 有了新版本,当然你这里可以跳过确认,强制更新,此处演示 需要手动确认
					if (MessageBox.Show("新版本:" + readVersion.Content + Environment.NewLine + "是否确认更新?", "新版", MessageBoxButtons.YesNo ) == DialogResult.Yes)
					{
						// 确认更新,启动当前目录的更新程序
						try
						{
							System.Diagnostics.Process.Start( Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Upgrade.exe" ), "127.0.0.1 12345 \"HslCommunicationDemo.exe\"" );
							System.Threading.Thread.Sleep( 20 );
							Close( );
						}
						catch (Exception ex)
						{
							MessageBox.Show( "启动更新失败:" + ex.Message );
							Close( );   // 退出或是不退出
						}
					}
				}

				// 如果是int类型的版本号
				//if (int.Parse( readVersion.Content ) > int.Parse( currentVersion ))
				//{

				//}
			}
			else
			{
				// 版本号获取失败,这里可以自行决定是否需要退出系统,如果需要退出,执行下面的Close
				// Close( );
			}

              }

  

嗯,代码基本是写完了,逻辑也都弄好了。现在还差点什么东西呢,还差点新版本更新的流程。比如你得客户端界面调整优化了,重新弄了个版本,v1.0.1  首先这个地方就需要更改

 

 

 

将编译好的新的客户端程序,重新拷贝到服务器的Client目录里面。

 

 

然后修改服务器里的最新的版本号

 

 

 

这样就更新操作完成了,客户端在启动的时候,就会提供更新,点击了更新之后,就会自动启动更新程序了。但是这里还有个小细节,服务器每次更新版本号,岂不是要重启?可以控制台修改版本号,然后记录本地,服务器启动时加载本地的版本号即可。

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp4
{
	internal class Program
	{
		static void Main( string[] args )
		{
			// 实例化exe
			HslCommunication.Enthernet.NetSoftUpdateServer netSoftUpdate = new HslCommunication.Enthernet.NetSoftUpdateServer( "Upgrade.exe" );
			// 客户端程序放在服务器当前目录的Client里面
			netSoftUpdate.FileUpdatePath = System.IO.Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Client" );
			netSoftUpdate.ServerStart( 12345 );            // 绑定的更新端口号信息

			// 实例化服务
			Services services = new Services( );
			string versionFile = System.IO.Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "version.txt" );
			// 检查本地版本号文件
			if (System.IO.File.Exists( versionFile ))
			{
				services.Version = System.IO.File.ReadAllText( versionFile, Encoding.UTF8 );
			}

			HslCommunication.MQTT.MqttServer mqttServer = new HslCommunication.MQTT.MqttServer( );
			// 注册服务
			mqttServer.RegisterMqttRpcApi( services );
			// 启动服务
			mqttServer.ServerStart( 1883 );


			Console.WriteLine( "启动成功!" );
			while (true)
			{
				// 手动输入更新,然后保存本地
				string command = Console.ReadLine( );
				if (command.StartsWith( "v:" ))
				{
					services.Version = command.Substring( 2 );
					System.IO.File.WriteAllText( versionFile, services.Version, Encoding.UTF8 );
				}
			}
		}

		/// <summary>
		/// 服务类,提供各种接口
		/// </summary>
		public class Services
		{
			/// <summary>
			/// 获取客户端的版本号信息
			/// </summary>
			/// <returns>版本号</returns>
			[HslCommunication.Reflection.HslMqttApi()]
			public string GetClientApi( )
			{
				return Version;
			}

			public string Version { get; set; } = "1.0.1";   // 服务器上当前最新版本的客户端的版本号,这个值在你更新客户端版本后,就需要手动更新

		}
	}
}

  当然如果你的是winform程序,还可以从界面上更新这个版本号信息。

 

还有几个小细节,

1. 你上传的客户端程序不应该包含一些敏感的数据,比如你的程序有登录账户的功能,会记录账户名,密码的配置文件,这个文件上传客户端目录的话,所有人就可以自动登录了。

2. 用于更新的 Upgrade.exe 会校验文件的md5,所以文件有修改才会下载到客户端的本地,所以不怕客户端的文件数量多。

3. 用于更新的 Upgrade.exe 会自动检测客户端电脑的语言环境,如果不是中文环境,界面会使用英文。

 

如果还有什么问题,可以通过最上方的连接找到作者。

 

posted @ 2022-07-10 15:12  dathlin  阅读(15146)  评论(4编辑  收藏  举报