笔记本备份






4.45/5 (28投票s)
2004年3月10日
17分钟阅读

121159

4644
使用远程处理的文件备份系统。
引言
笔记本电脑备份这个想法在我脑子里已经放了一段时间了。我本来打算写出来,但一直没抽出时间,直到我经历了一个糟糕的月份,因为各种原因不得不重建我所有的电脑。我不仅丢失了工作,还丢失了电子邮件和我购买的下载软件,以及一些我再也无法阅读的电子书。更不用说在《无冬之夜》里失去了一个十二级的双手技能小偷。我必须采取行动,这就是我的行动。
最初的设想是在主计算机和笔记本电脑之间维护数据(因此得名),但我的笔记本电脑实在受不了这种没完没了的计算,决定实现它作为大而昂贵的镇纸的真正价值。从那时起,我一直使用这个程序将所有我想备份的文件收集到一个文件夹中,这样,当需要每月备份时,我就不必在硬盘上到处搜索,回忆它们都在哪里。这确实意味着,如果有人在远程处理设置方面遇到问题,你们就只有源代码,一切得靠自己了。
除了本文档之外,Zip文件中还包含一个用户指南。其中包括基本设置以及几个关于如何使用该程序的简短教程。
设计
该程序设计为两个部分。第一部分是GUI,第二部分是服务。程序设计为协同工作;也就是说,你不能在一台机器上安装它,然后期望它将文件备份到另一台机器。我觉得这个想法太简单了,无法实现我想要的程序操作。我觉得那种方式不对,而且,如果你想备份到的计算机关机了怎么办?那样就没有备份了,所以我必须允许选项,以便在不不断地全天备份相同文件,或者在文件所在的计算机关闭时根本不备份文件的情况下,可以重试直到完成。我马上会回到协同工作部分,解释我这句话的意思。
设计理念是,我想要建立一个备份系统,让人们可以使用它而无需动脑筋。这意味着不能麻烦TCP连接和套接字,也不能让用户自己去设置。它必须直接从安装程序运行。通过重启或手动启动服务,它应该能运行。检查连接是否正常也应该很简单,在GUI的情况下,只需单击“验证”按钮,程序就会允许你访问远程计算机。这只是为了检查它是否能连接到另一台计算机。
那么,我说的协同工作程序是什么意思?是这样的。正如我之前提到的,该程序的工作方式不像你期望的那样。第一个也是最重要的一个需要理解的区别是,程序不会将文件从它正在运行的计算机复制到另一台计算机。它会获取它们。当你在服务中设置要复制的文件时,你会将文件的详细信息存储在注册表中,这样在需要的时候,服务会检查该计算机是否在网络上运行,然后它会从另一台计算机获取文件。服务在任何情况下都不会将文件从其自己的硬盘复制到另一台计算机。当然,除非你重写源代码来实现。
这样工作的原因是,笔记本通信DLL作为代理对象运行;也就是说,如果你从你的主计算机运行代码来复制你笔记本电脑上的文件,那么笔记本通信DLL实际上将运行在笔记本电脑上,即使所有对它的调用都是通过主计算机上的代码运行的。这是通过.NET Framework内置的远程处理技术实现的,稍后我会提供更多细节。
我做了一个让步,那就是端口号。虽然我设置了一个默认值,它在我系统上工作得很好,但我怀疑会有很多人有防火墙,他们会想要阻止该端口。我添加了更改端口值的功能,以迁就那些有防火墙的人。
另外一个让我烦恼的主要问题是文件夹共享。如果我想编写一个可以轻松在计算机之间移动的程序,而不会让计算机知识不多的用户感到困惑,我必须找到一种方法来绕过文件夹共享。事实上,我必须绕过程序在执行任何技术性或特殊操作的想法。对最终用户来说,它应该看起来就像他们正在选择他们想要备份的文件或文件夹。就最终用户而言,就这么复杂。考虑到这一点,用户界面设计得让最终用户看起来好像在使用一个类似Explorer的对话框来浏览硬盘。他们当前所在的计算机与文件所在的计算机不同,这应该无关紧要。事实上,我唯一一次让最终用户意识到整个过程中涉及另一台计算机,是在设置新计算机到程序时,以及当用户界面启动时,它会要求他们验证远程计算机。验证功能只是检查与远程计算机的通信通道是否已建立。
意图
所以,有一个备份文件的程序固然好,但为什么还要写一个呢?它和市面上所有其他程序有什么区别?好吧,一开始,这个程序是为个人使用而设计的,而不是为商业使用。这意味着它用于在计算机之间复制我的保存游戏目录、编程文件夹和个人文档,也就是说,如果我丢失了我所有的工作,所有使用备份程序的计算机都必须同时爆炸。
该程序完成了它的预期工作,因此它不会写入磁带驱动器或刻录CD,尽管没有什么能阻止人们将所有文件复制到同一个子目录并自己刻录CD。它也不旨在备份整个计算机。在其当前版本中,该程序将无法成功备份整个笔记本硬盘驱动器并在以后将其复制回笔记本电脑,因为会遇到访问操作系统当前正在使用的文件的问题。我无意添加这个功能,因为它不是我需要程序做的。如果有人想要程序能够做到这一点,那么他们就有提供的源代码。目前,我太忙于假装完成《无冬之夜》比完成备份程序的事实并不能困扰我。
接口
Laptop Backup 的界面设计可以让你使用任意数量的计算机来组织你的文件。当然,由于我不知道会有多少台计算机,我不得不设计一种方法来设置界面,使其能够应对任何数量的添加。有两种方法可以做到这一点。一种是编写一个函数来为每个选项卡重绘布局。另一种是继承选项卡页面类,然后重复实例化该类。这允许我使用设计器来开发界面,然后只需将设置代码复制到BackUpTab
类的构造函数中。这意味着没有代码定义的限制,你可以运行备份程序的计算机数量。
上面图片展示了 Laptop Backup 程序的主屏幕。主显示面板包含添加和删除计算机的功能。值得注意的是,在选项卡页面上进行的任何编辑都会自动保存,因此“保存并退出”按钮实际上只保存计算机更改。选项卡页面包含所有特定于计算机的信息,例如你为计算机选择的名称,以及IP地址,在该显示中,本地计算机的IP地址为localhost。验证按钮会检查计算机连接是否正常工作,而“添加新”、“编辑”和“删除”按钮用于控制单个列表的详细信息。所有这些都在包含在Zip文件中的《Laptop Backup 用户指南》中有讨论。因此,我只会在本节中简要介绍界面的基本知识。
基本上,程序的所有详细信息都存储在注册表中,然后由服务读取,因此用户界面的目的仅仅是收集所需的数据。这主要是通过“添加新”按钮完成的,而“编辑”按钮允许你在设置好设置后进行微调。例如,在测试过程中,我发现《神鬼寓言》的保存游戏大约有300兆。直到那时,我以为编程文件将是最大的数据量,我会复制。300兆不是小数据量,也需要一段时间,所以一旦我知道了这一点,我就更改了这个目录的设置,使其每七天复制一次。
“添加新”按钮是主要的设置,如上所述,在Zip文件中包含的用户指南中有更详细的讨论。
“添加新”对话框实现了上述设计,它以标准的Explorer类型界面显示驱动器数据。总的来说,使用起来感觉有点笨拙,对鼠标的响应速度不如我理想中那么快。按钮的焦点向下移动,这样你就不能在设置文件之前设置文件的目的地,也不能在设置添加和目的地之后保存数据。这比试图跟踪人们随机选择按钮查看发生了什么要干净得多。
服务
名为BackUpService
的服务是该应用程序的主要工作引擎,它负责在计时器每次触发时计算需要复制哪些文件。它通过读取用户界面程序创建的注册表项来实现这一点。在那里,它还为它用于自身账簿的注册表中的每个条目添加了几个注册表值。它添加的主要键是“Done”值。添加此值是为了当用户界面在注册表中设置“尝试直到完成”变量时,服务可以快速轻松地确定它是否已经完成了当前项的复制。 “Done”值阻止服务在计时器每次触发时都要遍历所有文件,因为这不切实际且耗时。
服务本身的一个主要功能是CheckRegistryKeys
函数。该函数在服务运行的各个时间点被调用,并负责程序的控制。在程序开始时,它会将程序“Done”值的所有注册表项设置为false,并将当前检查日期更新为当前日期。在结束时,它会维护DaysTilCheck
值,并在必要时减少它。
服务提供的核心功能在OnBackupTimerTick
函数中,该函数负责实际的文件复制。应该明白的是,你复制的文件大小没有限制,因此该函数实现了一个工作变量,这意味着如果它忙碌,它将一次完成一项任务,然后再进行下一项。我认为这是合理的,因为该程序设计用于纯粹备份个人数据,而不是面向大型企业系统,因此复制速度不如正确性重要。
程序如何使用远程处理
用户界面和服务都依赖于Microsoft .NET Framework提供的远程处理服务。这是一个允许计算机程序与另一台远程计算机上的其他计算机程序通信的系统,因此得名远程处理。与DCOM等早期尝试相比,.NET Framework提供了一个大大简化的机制,DCOM需要对正在发生的事情有大量的专业知识,而许多 otherwise-intelligent 的程序员却完全无法理解。
用户界面和服务都通过设置一个称为“监听器”的组件来启动。这是一种监听来自远程计算机的传入调用的软件。需要注意的是,程序中设置的监听器并不是专门监听来自任何特定计算机的输入,而是监听来自某个特定位置的输入。用户界面用于设置监听器的代码是
listenChannel = new TcpChannel( 0 );
ChannelServices.RegisterChannel( listenChannel );
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(CommunicationChannel), "LaptopBackup",
WellKnownObjectMode.SingleCall);
listenChannel
被声明为TcpChannel
,它使用0参数创建,这一点很重要,因为当你创建TcpChannel
时,你可以指定通道监听的端口,但理论是,如果你在TcpChannel
构造函数中指定零,那么通道可以监听任何可用端口。然而,这不适用于服务。如果你实验性地将TcpChannel
设置为0,它会不断抱怨连接被拒绝。只有显式添加端口号才能解决这个问题。这就引出了远程处理框架的一个小缺点,我认为是否算严重缺点取决于个人看法,但是如果你有一个服务监听在例如8086端口,这对笔记本电脑备份有效,那么你不能指定控制服务的用户界面也监听在同一端口。给出的错误信息是,每个端口只能监听一个项目。如果你尝试将用户界面设置为监听8086端口,则会报告一个错误,说它已被使用。解决方法是为用户界面指定TcpChannel
的0,为服务指定正确的端口号。
一旦确定了TcpChannel
要监听的端口,就使用ChannelServices.RegisterChannel
函数将通道注册到.NET Framework,然后注册服务类型。服务类型是一个可远程处理的对象,在这种情况下,它是CommunicationChannel
对象,它继承自System.MarshalByRefObject
。通信通道处理用户界面和服务的远程处理引用,并通过调用RemotingConfiguration.RegisterWellKnownServiceType
向系统注册。这不会在远程计算机上实例化对象的代理,它只是在当前计算机上注册它。注册的对象被赋予一个URI或一个名称,系统可以使用该名称找到对象,以及一个可以设置为Singleton或SingleCall的调用类型。Singleton对象将在程序的作用域或生命周期内保持有效,而SingleCall类型将在每次实例化对象调用时创建一个新的代理实例。
我在测试应用程序时遇到的一件事是,似乎可以建立的连接数量是有限的。每次备份大量文件时,代码都会标记一个错误,即没有注册接收器。这部分是我的错,因为我有一个类对象反复调用激活器函数,而且没有允许对象超出范围,这意味着(我怀疑)垃圾收集器(可以理解地)没有释放资源,因为它没有被告知它们应该被释放。通过将通信通道对象局部化到需要它的函数,这个错误得以修复,这给了垃圾收集器一个清理我的机会。
当代码调用时,对象的一个实例才会被创建
communicationsChannel = (CommunicationChannel)
Activator.GetObject(typeof(CommunicationChannel),
buffer.ToString() );
这会在远程计算机上实例化到对象的连接。嗯,差不多,此时你仍然会发现对象无效,似乎发生的是,这会在本地计算机上实例化代理。因此,第一次使用你认为已经实例化过的对象时要小心,因为第一次使用时可能会收到一个SocketException
,说明代码不可用,因为远程计算机已主动拒绝。
Activator的GetObject
函数接受两个参数,它们是你正在创建的代理对象的类型和URL字符串。对于Laptop Backup程序,它会读取类似“tcp://:8086/LaptopBackup”的内容,分解为“tcp”(使用的协议)、“localhost”(要连接的计算机的IP地址,在本例中是Laptop Backup正在运行的本地主机或当前计算机)。这通常会是一个数字,如111.111.111.111。“8086”表示代码运行的端口号,而“LaptopBackup”是程序的名称或URI。
快速指南
- 在所有目标计算机上安装。
- 重启计算机或直接启动服务。
- 将您想从中获取文件的计算机添加到主程序中。
- 如果需要,在命令提示符下键入ipconfig以获取IP地址。
- 如果连接仍然不起作用,并且在命令提示符下键入ping有效,那么请将程序的执行文件添加到您的防火墙例外列表中。
- 设置您希望备份的文件。如果不确定,请参阅源代码中提供的用户指南教程。
测试环境
该程序目前在两台计算机和三个硬盘驱动器上进行了测试,运行Windows XP Home和Professional,复制约一GB的数据和约7000个文件,并在其中一台计算机上以调试模式运行,在另一台计算机上以发布模式运行。
历史
- 2004年3月10日:初始发布。
- 2004年3月14日:修复了1级目录错误,修复了刷新错误。
- 2004年7月27日:正确处理了对话框的取消选项。
- 2005年6月10日
- 重写了文件处理。
- 修复了单个文件失败时退出循环的问题。
- 修复了已检查目录复制在目录树末端时出现混淆的问题。
- 清理了教程,使图像正确显示。
参考文献
- Tom Archer (2001) Inside C#, Microsoft Press。
- Charles Peltzold (2002) Programming Microsoft Windows With C#, Microsoft Press。
- Robinson et al (2001) Professional C#, Wrox。