一种简单的客户端更新方案
在c/s结构的程序中,客户端自动更新是简化部署的一个常见需求.更新实际上做的操作是:
- 与服务器当前的文件做diff,找出所有变动的文件
- 下载变动的文件,替换掉本地的文件
从这两个操作来看,服务器起码要向客户端提供两个功能:
- 当前有那些文件
- 下载文件
其实第一个功能也可以转为下载文件,只要提供描述当前文件列表信息的文件就行了。通常有两种更新模式,增量更新与直接更新。增量更新是指低版本向高版本逐版本更新,这种方式的好处在于,服务器发布新版本时可以直接预生成diff补丁包,客户端直接按照补丁包进行替换,但坏处是:
- 跨版本比较多时,会进行重复操作
- 管理起来麻烦,要保存所有历史版本
直接升级则不会有重复操作,且服务器只要保存一个最新版本即可.本文描述的方案就是直接升级的.
服务端
基于文件服务器工作,FTP或者HTTP.用两个json文件来管理版本,app.json和appflist.json.文件服务器上的结构为:
- 文件服务器根目录
- app1
- app2
app就表示客户端的根目录,每个app里都会有app.json和appflist.json.服务器的开发工作就是生成这两个文件,以及更新app目录.
app.json
{"version":"1.0.0","info":"版本信息"}
用于描述版本号与版本信息,主要给客户端进行快速判断是否要升级.客户端可以简单的判断版本号是否相等来决定要不要升级.
appflist.json
[{"path":"foo.dll","md5":"","ctime":147989119}]
用于描述文件列表信息,包含当前版本所有文件信息(除app.json,appflist.json),用于给客户端做diff.
path
为客户端根目录的相对路径
客户端
客户端通过本地的app.json和appflist.json与服务器上的同名文件来决定更新操作。更新时会用ftp://127.0.0.1/app1/foo.dll
这样的url去下载文件,url的格式为协议://host:port/app目录/文件路径
更新分为五个步骤:
判断是否要更新
通过比对app.json的version来决定是否要更新diff
下载appflist.json,然后通过md5筛选出增删改的文件下载变动的文件
用path和app名下载增改的文件本地替换
进行增删改操作删除下载的文件
在更新时要注意用独立的进程操作,因为要替换主程序的文件.
还有如何处理更新失败的情况,更新失败有两种情况:
- 更新时程序被强制结束
- 文件操作时失败
第一种情况发生时,如果正在替换文件,可能会损坏文件,导致主程序不能正常打开,这个问题不在于文件损坏,而是主程序无法运行,也就不能再次进入更新流程了,如果能进来则把流程重来一次就好了,不能进来时只能让用户手动打开更新程序了.因此这种情况是不需要在客户端写特定代码处理的.
第二种情况发生时,也直接重试流程,如果一直错,那可能是无操作文件的权限,此时并不会损坏旧文件,因此也不需要特殊处理.
最后我写了一个shupd实现了这些功能,放在github上,shupd的server部分为一个用来生成app.json,appflist.json,拷贝变动文件的工具;client部分为c实现的动态库。目前基于ftp工作,有兴趣的朋友可以看看.