我正在考虑将系统的本地时间更改为服务器的时间,然后使用它,但我敢打赌还有其他方法可以做到这一点。我一直试图在 c# 中找到类似时钟的东西,但找不到任何东西。我以 DateTime 格式接收服务器的时间。
编辑:我需要我的应用程序在同一时间服务器工作时使用。我只想获得一次服务器的时间,然后使用从服务器获得的时间让我的应用程序在一个while循环中工作。我的系统时间和服务器时间(甚至 5 秒)之间可能存在差异,这就是我想要这样做的原因。
我正在考虑将系统的本地时间更改为服务器的时间,然后使用它,但我敢打赌还有其他方法可以做到这一点。我一直试图在 c# 中找到类似时钟的东西,但找不到任何东西。我以 DateTime 格式接收服务器的时间。
编辑:我需要我的应用程序在同一时间服务器工作时使用。我只想获得一次服务器的时间,然后使用从服务器获得的时间让我的应用程序在一个while循环中工作。我的系统时间和服务器时间(甚至 5 秒)之间可能存在差异,这就是我想要这样做的原因。
您的意思并不完全清楚,但您当然可以创建自己的IClock
接口,您可以在代码中的任何地方使用该接口,然后编写一个与您的服务器(或与 NTP)定期同步的实现。
我的Noda Time项目已经使用了可注入时钟的概念——不是为了同步,而是为了可测试性。(时间服务基本上是一个依赖项。)基本上这个想法是可行的 :) 你很可能找不到任何已经这样做的东西,但写起来应该不会太难。不过,您需要考虑如何调整时间 - 例如,如果服务器时间超过您的“上次服务器时间 + 本地时间测量值”,您可能希望逐渐调整它而不是离散跳跃。
当然,这总是假设您确实希望它在您的应用程序中是本地的。另一种选择(这可能不合适,取决于您的上下文)是要求主机运行时间同步客户端(我相信这些天默认情况下Windows会这样做)并且如果您的服务器和客户端之间的差异变得简单,则开始失败太大了。(无论如何,它永远不会完全同步,或者至少不会很长时间 - 你需要留出一些余地。)
@JonSkeet 提供的同步时间的答案看起来不错,我只是想指出一些事情。
正如@Alexei 已经说过的那样,用户需要管理员权限才能更改他们的本地时间(至少在 Windows 中),但也可能存在其他可能导致时间不同步的问题(糟糕的互联网连接、黑客攻击等)。 )。这意味着不能保证客户端时间确实与服务器时间相同,因此您至少需要检查服务器端收到请求的时间。另外,这里可能还存在可用性问题,我是否希望应用程序能够更改我自己的本地计算机的时间?一定不行。
总结一下:
如何处理应用程序中的指标可以通过多种方式完成。
请记住,几乎不可能验证像这个客户端这样的请求。所以你必须在服务器端建立一些检查!
我实际上想写一个评论,但它有点长.. :)
好吧,有点死灵法,因为这是 6 岁,但不得不为网络游戏处理类似的问题。
采用了一种我称之为“马可波罗”的技术,原因很快就会很明显。它需要两个时钟能够交换消息,其准确性取决于它们能够以多快的速度做到这一点。
免责声明:我很确定我不是第一个这样做的人,这是同步两个时钟的最基本方法。我仍然没有找到这样做的记录方法。
在时钟 B(我们尝试同步的时钟),我们执行以下操作::
// Log the timestamp
localTime_Marco_Send = DateTime.UtcNow;
// Send that to clock A
SendSyncRequest();
// Wait for an answer
Sleep(..);
在时钟 A(参考时钟)处,我们有以下处理程序 ::
// This is triggered by SendSyncRequest
OnReceiveSyncRequest()
{
// We received "Marco" - Send "Polo"
SendSyncReply(DateTime.UtcNow);
}
回到时钟B::
// This is triggered by SendSyncReply
OnReceiveSyncReply(DateTime remoteHalfTime)
{
// Log the time we received it
DateTime localTime_Polo_Receive = DateTime.UtcNow;
// The remote time is somewhere between the two local times
// On average, it will be in the middle of the two
DateTime localHalfTime = localTime_Marco_Send +
(localTime_Polo_Receive - localTime_Marco_Send) / 2;
// As a result, the estimated dT from A to B is
TimeSpan estimatedDT_A_B = localHalfTime - remoteHalfTime;
}
因此,我们现在可以访问一个漂亮的 TimeSpan,我们可以从当前本地时间中减去来估计远程时间
DateTime estimatedRemoteTime = DateTime.UtcNow - estimatedDT_A_B;
这个估计的准确性取决于发送-接收-发送-接收的往返时间,您还应该考虑时钟漂移(您应该多次这样做):
您的服务器应始终以 UTC 模式保存时间。
您可以在服务器中像这样在 UTC 中节省时间:
DateTime utcTime = new DateTime(0, DateTimeKind.Utc);
或者:
DateTime utcTimeNow = DateTime.UtcNow;
在客户端中,当您获得存储在 utc 中的时间时,您可以将其转换为本地时间,如下所示:
public DateTime ToLocalTime(DateTime utcTime)
{
//Assumes that even if utcTime kind is no properly deifned it is indeed UTC time
DateTime serverTime= new DateTime(utcTime.Ticks, DateTimeKind.Utc);
return TimeZoneInfo.ConvertTimeFromUtc(serverTime, m_localTimeZone);
}
如果你想改变你的本地时区,这里有一个关于如何从配置中读取时区的代码示例:
string localTimeZoneId = sysParamsHelper.ReadString(LOCAL_TIME_ZONE_ID_KEY, LOCAL_TIME_ZONE_DEFAULT_ID);
ReadOnlyCollection<TimeZoneInfo> timeZones = TimeZoneInfo.GetSystemTimeZones();
foreach (TimeZoneInfo timeZoneInfo in timeZones)
{
if(timeZoneInfo.Id.Equals(localTimeZoneId))
{
m_localTimeZone = timeZoneInfo;
break;
}
}
if (m_localTimeZone == null)
{
m_logger.Error(LogTopicEnum.AMR, "Could not find time zone with id: " + localTimeZoneId + " . will use default time zone (UTC).");
m_localTimeZone = TimeZoneInfo.Utc;
}