3

我正在考虑将系统的本地时间更改为服务器的时间,然后使用它,但我敢打赌还有其他方法可以做到这一点。我一直试图在 c# 中找到类似时钟的东西,但找不到任何东西。我以 DateTime 格式接收服务器的时间。

编辑:我需要我的应用程序在同一时间服务器工作时使用。我只想获得一次服务器的时间,然后使用从服务器获得的时间让我的应用程序在一个while循环中工作。我的系统时间和服务器时间(甚至 5 秒)之间可能存在差异,这就是我想要这样做的原因。

4

4 回答 4

4

您的意思并不完全清楚,但您当然可以创建自己的IClock接口,您可以在代码中的任何地方使用该接口,然后编写一个与您的服务器(或与 NTP)定期同步的实现。

我的Noda Time项目已经使用了可注入时钟的概念——不是为了同步,而是为了可测试性。(时间服务基本上是一个依赖项。)基本上这个想法是可行的 :) 你很可能找不到任何已经这样做的东西,但写起来应该不会太难。不过,您需要考虑如何调整时间 - 例如,如果服务器时间超过您的“上次服务器时间 + 本地时间测量值”,您可能希望逐渐调整它而不是离散跳跃。

当然,这总是假设您确实希望它在您的应用程序中是本地的。另一种选择(这可能不合适,取决于您的上下文)是要求主机运行时间同步客户端(我相信这些天默认情况下Windows会这样做)并且如果您的服务器和客户端之间的差异变得简单,则开始失败太大了。(无论如何,它永远不会完全同步,或者至少不会很长时间 - 你需要留出一些余地。)

于 2013-02-28T08:11:01.290 回答
2

@JonSkeet 提供的同步时间的答案看起来不错,我只是想指出一些事情。

正如@Alexei 已经说过的那样,用户需要管理员权限才能更改他们的本地时间(至少在 Windows 中),但也可能存在其他可能导致时间不同步的问题(糟糕的互联网连接、黑客攻击等)。 )。这意味着不能保证客户端时间确实与服务器时间相同,因此您至少需要检查服务器端收到请求的时间。另外,这里可能还存在可用性问题,我是否希望应用程序能够更改我自己的本地计算机的时间?一定不行。

总结一下:

  • 至少检查请求服务器端的时间
  • 不要更改客户端机器的时间,而是在您的应用程序中显示某种指示器

如何处理应用程序中的指标可以通过多种方式完成。

  • 在您的应用程序(您最初的想法)中显示一个与服务器定期同步的时钟
  • 显示某种倒计时(“您可以在 x 秒后提交..”),在收到请求时向客户端推送 resetCountdown 请求。
  • 启用“发送按钮”或您拥有的任何东西,这类似于倒计时。

请记住,几乎不可能验证像这个客户端这样的请求。所以你必须在服务器端建立一些检查!

我实际上想写一个评论,但它有点长.. :)

于 2013-02-28T09:37:41.460 回答
1

好吧,有点死灵法,因为这是 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;

这个估计的准确性取决于发送-接收-发送-接收的往返时间,您还应该考虑时钟漂移(您应该多次这样做):

  1. 往返时间。如果它是即时的,那么您将获得准确的 dT。如果来回需要1秒钟,你不知道延迟是在发送还是接收。结果,您的错误为 0 < e < RTT,平均为 RTT/2。如果您知道发送(或接收)比另一个花费更多,请利用它来发挥您的优势 - 您收到的时间不是半场时间,而是相对于每条腿花费的时间
  2. 时钟漂移。CPU 时钟漂移,可能每天 1 秒。因此,一旦潜在的漂移再次轮询可能会发挥重要作用。
于 2019-07-09T16:36:40.347 回答
0

您的服务器应始终以 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;
    }          
于 2013-02-28T08:12:11.987 回答