我将为这个系统绘制一个可能的架构。它不完整,您应该将其视为前半部分的详细伪 C# 代码形式和后半部分的一组提示和建议。
我相信您可能需要两个应用程序:
- AC# WinForms 客户端。
- AC# 服务器端应用程序,可能是 Web 服务。
我不会关注这个答案的安全问题,但它们显然非常重要。我希望可以在更高级别上实现安全性,也许可以使用 SSL。Web 服务将在 IIS 中运行,实现某种形式的安全性应该主要是配置问题。
服务器端部分不是严格要求的,特别是如果您不想要压缩;可能有一种方法可以配置您的服务器,以便在website.com/updater发出 HTTP 请求时返回一个易于解析的文件列表。然而,拥有 Web 服务更灵活,而且可能更容易实现。您可以从查看这篇 MSDN 文章开始。如果您确实想要压缩,您可以将服务器配置为透明地压缩单个文件。我将尝试绘制所有可能的变体。
在单个更新 ZIP 文件的情况下,基本上更新程序 Web 服务应该能够响应两个不同的请求;首先,它可以返回相对于服务器目录website.com/updater的所有游戏文件的列表,以及它们的最后写入时间戳(GetUpdateInfoWeb 服务中的方法)。客户端会将这个列表与本地文件进行比较;服务器上可能不再存在某些文件(可能客户端必须删除本地副本),客户端上可能不存在某些文件(它们是全新的内容),并且客户端和服务器上可能同时存在一些其他文件,在这种情况下,客户端需要检查最后一次写入时间以确定它是否需要更新版本。客户端将构建这些文件相对于游戏内容目录的路径列表。游戏内容目录应镜像服务器website.com/updater目录。
其次,客户端将此列表发送到服务器( Web 服务中的GetUpdateURL)。服务器将创建一个包含更新的 ZIP 并使用其 URL 进行回复。
[ServiceContract]
public interface IUpdater
{
[OperationContract]
public FileModified[] GetUpdateInfo();
[OperationContract]
public string GetUpdateURL();
}
[DataContract]
public class FileModified
{
[DataMember]
public string Path;
[DataMember]
public DateTime Modified;
}
public class Updater : IUpdater
{
public FileModified[] GetUpdateInfo()
{
// Get the physical directory
string updateDir = HostingEnvironment.MapPath("website.com/updater");
IList<FileModified> updateInfo = new List<FileModified>();
foreach (string path in Directory.GetFiles(updateDir))
{
FileModified fm = new FileModified();
fm.Path = // You may need to adjust path so that it is local with respect to updateDir
fm.Modified = new FileInfo(path).LastWriteTime;
updateInfo.Add(fm);
}
return updateInfo.ToArray();
}
[OperationContract]
public string GetUpdateURL(string[] files)
{
// You could use System.IO.Compression.ZipArchive and its
// method CreateEntryFromFile. You create a ZipArchive by
// calling ZipFile.Open. The name of the file should probably
// be unique for the update session, to avoid that two concurrent
// updates from different clients will conflict. You could also
// cache the ZIP packages you create, in a way that if a future
// update requires the same exact file you would return the same
// ZIP.
// You have to return the URL of the ZIP, not its local path on the
// server. There may be several ways to do this, and they tend to
// depend on the server configuration.
return urlOfTheUpdate;
}
}
客户端将使用HttpWebRequest和HttpWebResponse对象下载 ZIP 文件。要更新进度条(在此设置中您只有一个进度条,请查看我对您问题的评论),您需要创建一个BackgroundWorker。本文和另一篇文章涵盖了相关方面(不幸的是,该示例是用 VB.NET 编写的,但看起来与 C# 中的非常相似)。要推进进度条,您需要跟踪收到的字节数:
int nTotalRead = 0;
HttpWebRequest theRequest;
HttpWebResponse theResponse;
...
byte[] readBytes = new byte[1024];
int bytesRead = theResponse.GetResponseStream.Read(readBytes, 0, 4096);
nTotalRead += bytesread;
int percent = (int)((nTotalRead * 100.0) / length);
收到文件后,您可以使用System.IO.Compression.ZipArchive.ExtractToDirectory更新您的游戏。
如果您不想使用 .NET 显式压缩文件,您仍然可以使用 Web 服务的第一种方法来获取更新文件的列表,并使用HttpWebRequest/HttpWebResponse对在客户端复制您需要的文件每个。这样你实际上可以有两个进度条。计算文件的那个将简单地设置为一个百分比,例如:
int filesPercent = (int)((nCurrentFile * 100.0) / nTotalFiles);
如果您有其他方式获取列表,您甚至不需要 Web 服务。
如果你想单独压缩你的文件,但你不能让服务器自动实现这个功能,你应该用这个接口定义一个 web 服务:
[ServiceContract]
public interface IUpdater
{
[OperationContract]
public FileModified[] GetUpdateInfo();
[OperationContract]
public string CompressFileAndGetURL(string path);
}
您可以在其中要求服务器压缩特定文件并返回压缩的单文件存档的 URL。
编辑 - 重要
特别是在你的更新非常频繁的情况下,你需要特别注意时区。
编辑 - 另一种选择
我应该重申,这里的主要问题之一是从服务器获取当前版本中的文件列表;该文件应包括每个文件的最后写入时间。像 Apache 这样的服务器可以免费提供这样的列表,虽然它通常是供人类使用的,但是它很容易被程序解析。我确信必须有一些脚本/扩展名才能以更机器友好的方式格式化该列表。
还有另一种获取该列表的方法;你可以在服务器上有一个文本文件,对于每个游戏内容文件,它会存储它的最后写入时间,或者甚至更好的是,一个渐进的版本号。您将比较版本号而不是日期来检查您需要哪些文件。这将使您免受时区问题的影响。但是,在这种情况下,您需要维护此列表的本地副本,因为文件没有版本号之类的东西,而只有一个名称和一组日期。