What would be the best way to synchronize my appli

2019-07-14 07:36发布

问题:

I was thinking of changing system's local time to server's time and then use it but I bet there are other ways to do this. I've been trying to find something like a clock in c#, but couldnt find anything. I'm receiving server's time in a DateTime format.

edit: I need my application to use while working same time server does. I just want to get server's time once and after that, make my application work in a while loop using the time I've obtained from the server. There might be a difference between my system's time and server's time (even 5 seconds) and that's why I want to do this.

回答1:

The answer @JonSkeet's provided to synch the times looks good, I just wanted to point out some things.

As @Alexei already said, users require admin privileges to be able to change their local time (in Windows as least), but there may also be other issues that can cause the time to be out of synch (bad internet connection, hacks etc.). This means there is no guarantee that the client time is indeed the same as the server time, so you will at least need to check the time the request was received serverside anyway. Plus there might also be a usability issue at hand here, would I want an application to be able change the time of my own local machine? Hell no.

To sum things up:

  • Check the time of the request serverside at least
  • Don't change the time of the client machine but show some kind of indicator in your application

How to handle the indicator in your application can be done in various ways.

  • Show a clock in your application (your initial idea) that is periodically synched with the server
  • Show some kind of countdown ("you can submit after x seconds.."), push a resetCountdown request to the clients when a request is received.
  • Enable a 'send button' or what ever you have, this would work kind of similar to the countdown.

Just remember, it's nearly impossible validate a request such as this clientside. So you have to build in some checks serverside!

I actually wanted to write a comment but it got kind of long.. :)



回答2:

It's not entirely clear what you mean, but you could certainly create your own IClock interface which you'd use everywhere in code, and then write an implementation of that which is regularly synchronized with your server (or with NTP).

My Noda Time project already uses the idea of an injectable clock - not for synchronization purposes, but for testability. (A time service is basically a dependency.) Basically the idea is workable :) You may well not find anything which already does this, but it shouldn't be too hard to write. You'll want to think about how to adjust time though - for example, if the server time gets ahead of your "last server time + local time measurements" you may want to slew it gradually rather than having a discrete jump.

This is always assuming you do want it to be local to your application, of course. Another alternative (which may well not be appropriate, depending on your context) is to require that the host runs a time synchronization client (I believe Windows does by default these days) and simply start failing if the difference between your server and the client gets too large. (It's never going to be exactly in sync anyway, or at least not for long - you'll need to allow for some leeway.)



回答3:

Okay a bit of necromancy as this is 6 years old, but had to deal with a similar problem for a network game.

Employed a technique I referred to as "marco-polo" for reasons that will be obvious soon. It requires the two clocks to be able to exchange messages, and its accuracy is dependent on how fast they can do that.

Disclaimer: I am fairly certain I am not the first to do this, and that this is the most rudimentary way to synchronize two clocks. Still I didn't find a documented way of doing so.

At Clock B (The clock we're trying to synchronize) we do the following ::

// Log the timestamp
localTime_Marco_Send = DateTime.UtcNow;

// Send that to clock A
SendSyncRequest();

// Wait for an answer
Sleep(..);

At Clock A (the reference clock) we have the following handler ::

// This is triggered by SendSyncRequest
OnReceiveSyncRequest()
{
    // We received "Marco" - Send "Polo"
    SendSyncReply(DateTime.UtcNow);
}

And back at Clock 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;
}

As a result we now have access to a nifty TimeSpan we can subtract from our current local time to estimate the remote time

DateTime estimatedRemoteTime = DateTime.UtcNow - estimatedDT_A_B;

The accuracy of this estimate is subject to the Round Trip Time of send-receive-send-receive, and you should also account for Clock drift (you should be doing this more than once):

  1. Round-trip-time. If it were instant, you'd have the exact dT. If it takes 1 second to come and return, you don't know if the delay was on the sending or the receiving. As a result, your error is 0 < e < RTT, and on average will be RTT/2. If you know send (or receive) takes more than the other, use that to your advantage - the time you received is not the half-time, but is shifted relatively to how long each leg takes
  2. Clock drift. CPU clocks drift, maybe 1s per day. So poll again once potential drift may play an important role.


回答4:

Your server should always save the time in UTC mode.
You save time in UTC like this in the server:

  DateTime utcTime = new DateTime(0, DateTimeKind.Utc);

or:

    DateTime utcTimeNow = DateTime.UtcNow;

In the client, when you get the time which is stored in utc you can sonvert it to local time like this:

    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);            
    }

If You want to change your local time zone , here is a code example on how to read time zone to use from config:

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;
    }