Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.89/55: Рейтинг темы: голосов - 55, средняя оценка - 4.89
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70

Получение текущего значения мирового времени через службы NTP

08.01.2017, 19:20. Показов 11040. Ответов 8
Метки ntp (Все метки)

Студворк — интернет-сервис помощи студентам
Привет!

Я использую приведенный ниже программный код для получения текущего значения мирового времени через службы NTP.

В данной реализации у меня осуществляется доступ через сокеты по UDP-протоколу к серверу "time.windows.com".

Иногда из-за перегрузки сервера возникают значительные задержки, и мне необходимо переключиться на получение времени с других серверов "pool.ntp.org" или "time-a.nist.gov".

Любой из этих серверов в различное время суток дает различный тайм-аут, поэтому я не могу отдать какому-нибудь из них предпочтение.

Код работает нормально, но мне нужно предусмотреть, чтобы в случае истечения таймаута socket.ReceiveTimeout = 3000 по одному из серверов, алгоритм начинал "стучаться" к другому серверу, третьему серверу.

В случае всех неудачных 3-х попыток необходимо, чтобы в соответствующем методе возвращалось значение

DateTime networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)),

которое будет рассматриваться как индикатор неудачи в попытках получить текущее значение мирового времени через службы NTP.

У меня возник вопрос, заключающийся в том, как необходимо видоизменить существующий код, чтобы осуществить данную процедуру?

Заранее благодарна за помощь в решении данного вопроса.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System;
using System.Net;
using System.Net.Sockets;
 
namespace NtpTimeService
{
    /// <summary>
    /// Class for acquiring time via Ntp. Useful for applications in which correct world time must be used and the 
    /// clock on the device isn't "trusted."
    /// </summary>
    public static class NtpClient
    {
        static string[] NtpServers = { "time.windows.com", "pool.ntp.org", "time-a.nist.gov" };
 
        public static DateTime GetNetworkTime()
        {
            //**UTC** time
            //var 
            DateTime networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc));
 
            // NTP message size - 16 bytes of the digest (RFC 2030)
            var ntpData = new byte[48];
 
            //Setting the Leap Indicator, Version Number and Mode values
            ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
 
            //Select time server
            string ntpServer = NtpServers[0];
 
            //Setting adresses list
            var addresses = Dns.GetHostEntry(ntpServer).AddressList;
 
            //The UDP port number assigned to NTP is 123
            var ipEndPoint = new IPEndPoint(addresses[0], 123);
 
            //NTP uses UDP
            using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
            {
                //Stops code hang if NTP is blocked
                socket.ReceiveTimeout = 3000;
                //Connecting, sending, recieving data
                socket.Connect(ipEndPoint);
                socket.Send(ntpData);
                socket.Receive(ntpData);
            }
 
            //Offset to get to the "Transmit Timestamp" field (time at which the reply 
            //departed the server for the client, in 64-bit timestamp format."
            const byte serverReplyTime = 40;
 
            //Get the seconds part
            ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
 
            //Get the seconds fraction
            ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
 
            //Convert From big-endian to little-endian
            intPart = SwapEndianness(intPart);
            fractPart = SwapEndianness(fractPart);
 
            var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
 
            return networkDateTime = networkDateTime.AddMilliseconds((long)milliseconds);
        }
 
        // stackoverflow.com/a/3294698/162671
        static uint SwapEndianness(ulong x)
        {
            return (uint)(((x & 0x000000ff) << 24) +
                          ((x & 0x0000ff00) << 8) +
                          ((x & 0x00ff0000) >> 8) +
                          ((x & 0xff000000) >> 24));
        }
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
08.01.2017, 19:20
Ответы с готовыми решениями:

(мобильное приложение) Определение координат GPS и получение текущего времени с сервера времени интернет
Стоит задача на мобильном клиенте получить текущее время из интернет, что бы пользователи не баловали временем на устройстве при...

Получение текущего времени + 2 минуты
Получаю время: $time = date(&quot;H:i:s&quot;); Мне нужно к этому времени прибавить 2 минуты, как это можно сделать, чтобы не сломать...

Получение текущего времени с объекта GregorianCalendar
Не могу понять как можно получить текущее время из объекта GregorianCalendar не создавая его заново. Может что то провтыкал в API. ...

8
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
10.01.2017, 10:46  [ТС]
Я попытаюсь переформулировать вопрос.

Каким образом можно обработать событие, которое наступает в результате выполнения следующего оператора из приведенного выше программного кода?

C#
1
2
 //Stops code hang if NTP is blocked
socket.ReceiveTimeout = 3000;
0
19 / 19 / 9
Регистрация: 14.09.2016
Сообщений: 94
Записей в блоге: 1
10.01.2017, 10:57
InessaSuper, у сокета есть свойство
C#
1
Blocking
true Если Socket заблокирован; в противном случае — false. Значение по умолчанию — true.
1
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18298 / 14222 / 5368
Регистрация: 17.03.2014
Сообщений: 28,900
Записей в блоге: 1
18.01.2017, 22:12
InessaSuper, если модифицировать метод GetNetworkTime чтобы он принимал имя сервера, то можно сделать так:
C#
1
2
3
4
5
6
7
8
Task<DateTime>[] tasks = new Task<DateTime>[]
{
    Task.Run(() => NtpClient.GetNetworkTime("time-a.nist.gov")),
    Task.Run(() => NtpClient.GetNetworkTime("time.windows.com")),
    Task.Run(() => NtpClient.GetNetworkTime("pool.ntp.org")),
};
int idx = Task.WaitAny(tasks);
DateTime d = tasks[idx].Result;
В итоге получим ответ от самого быстрого сервера.
0
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
05.02.2017, 11:35  [ТС]
Скажите, каким образом можно модифицировать работу, приведенного выше кода, чтобы сделать его совместимым с фреймворком .NET 2.0?

Мне очень важна эта совместимость.

Дело в том, что во фреймворке .NET 2.0 нет возможности использовать класс System.Threading.Task.

Также нет возможности использовать лямбда-выражения.

Каким образом будет выглядеть асинхронный вызов метода GetNetworkTime для получения ответа от самого быстрого сервера службы точного времени в этом случае?

Как нужно будет изменить сигнатуру метода GetNetworkTime и само его содержимое, чтобы сделать его доступным для корректного вызова?
1
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18298 / 14222 / 5368
Регистрация: 17.03.2014
Сообщений: 28,900
Записей в блоге: 1
31.03.2017, 16:38
InessaSuper, наверное ответ уже не актуален, но вот решение для .NET 2
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
void Main()
{
    string[] ntpServers = {"time-a.nist.gov", "time.windows.com", "pool.ntp.org" };
    WaitHandle[] waitHandles = new WaitHandle[ntpServers.Length];
    Func<string,DateTime>[] funcs = new Func<string,DateTime>[ntpServers.Length];
    IAsyncResult[] asyncResults = new IAsyncResult[ntpServers.Length];
    for (int i = 0; i < ntpServers.Length; i++)
    {
        funcs[i] = NtpClient.GetNetworkTime;
        asyncResults[i] = funcs[i].BeginInvoke(ntpServers[i], null, null);
        waitHandles[i] = asyncResults[i].AsyncWaitHandle;
    }
    int idx = WaitHandle.WaitAny(waitHandles).Dump();
    DateTime d = funcs[idx].EndInvoke(asyncResults[idx]);
}
 
 
/// <summary>
/// Class for acquiring time via Ntp. Useful for applications in which correct world time must be used and the 
/// clock on the device isn't "trusted."
/// </summary>
public static class NtpClient
{
   public static DateTime GetNetworkTime(string ntpServer)
   {
       //**UTC** time
       //var 
       DateTime networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc));
 
       // NTP message size - 16 bytes of the digest (RFC 2030)
       var ntpData = new byte[48];
 
       //Setting the Leap Indicator, Version Number and Mode values
       ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
 
       //Setting adresses list
       var addresses = Dns.GetHostEntry(ntpServer).AddressList;
 
       //The UDP port number assigned to NTP is 123
       var ipEndPoint = new IPEndPoint(addresses[0], 123);
 
       //NTP uses UDP
       using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
       {
           //Stops code hang if NTP is blocked
           socket.ReceiveTimeout = 3000;
           //Connecting, sending, recieving data
           socket.Connect(ipEndPoint);
           socket.Send(ntpData);
           socket.Receive(ntpData);
       }
 
       //Offset to get to the "Transmit Timestamp" field (time at which the reply 
       //departed the server for the client, in 64-bit timestamp format."
       const byte serverReplyTime = 40;
 
       //Get the seconds part
       ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
 
       //Get the seconds fraction
       ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
 
       //Convert From big-endian to little-endian
       intPart = SwapEndianness(intPart);
       fractPart = SwapEndianness(fractPart);
 
       var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
 
       return networkDateTime = networkDateTime.AddMilliseconds((long)milliseconds);
   }
 
   // stackoverflow.com/a/3294698/162671
   static uint SwapEndianness(ulong x)
   {
       return (uint)(((x & 0x000000ff) << 24) +
                     ((x & 0x0000ff00) << 8) +
                     ((x & 0x00ff0000) >> 8) +
                     ((x & 0xff000000) >> 24));
   }
}
1
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
04.04.2017, 12:16  [ТС]
Эта тема для меня продолжает быть актуальной.

Спасибо за то, что откликнулись!

А я уже было совсем расстроилась, что не могу сдвинуть этот свой проект с мёртвой точки.

Мне удалось запустить приведенный выше код.

Только пришлось заменить строчку
C#
1
int idx = WaitHandle.WaitAny(waitHandles).Dump();
на
C#
1
int idx = WaitHandle.WaitAny(waitHandles);
т.к. в противном случае компилятор выдавал ошибку:

error CS1061: Type `int' does not contain a definition for `Dump' and no extension method `Dump' of type `int' could be found (are you missing a using directive or an assembly reference?)

Скажите, а каким образом мне нужно видоизменить этот код для .NET 2, чтобы учесть следующий нюанс.

Я должна предусмотреть в своём коде случай, когда ни один из перечисленных в списке time-серверов не откликнулся в течении заданного тайм-аута.

Я выделила в отдельный метод тот участок кода, который отвечает за возвращение значения от наиболее быстро откликнувшегося тайм-сервера, чтобы реализовать этот подход (см. видоизменённый код ниже).

В качестве возвращаемого значения из этого метода, в случае неудачной попытки получения текущего времени, я предполагаю возвращать следующее значение, как признак того, что не удалось достучаться до тайм-серверов:

C#
1
new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)
Скажите, каким образом нужно видоизменить приведенный ниже код для .NET 2, чтобы учесть этот нюанс?

P.S.

Я просто не могу понять как обработать тот случай, когда в приведенной ниже строчке из этого кода не удаётся дождаться ни одного из waitHandles.

C#
1
int idx = WaitHandle.WaitAny(waitHandles);
Когда я блокирую на своём компьютере сетевое соединение и запускаю на выполнение приведенный ниже код, то у меня получается происходит бесконечное выполнение программы, т.к. ожидание никогда не завершается из-за отключенной сети.

Мне же нужно, чтобы по истечению 3000 миллисекунд, которые указаны в методе NtpClient.GetNetworkTime() при работе с сокетами (socket.ReceiveTimeout = 3000) происходило следующее.

В случае отсутствия сетевого соединения и истечения этого тайм-аута для всех перечисленных тайм-серверов на выходе из метода Main.RecieveNetworkTime() необходимо, чтобы происходило возвращение значения:


C#
1
new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)
Скажите, как это можно сделать?

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
 
namespace NtpTimeServiceModified
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime dateTimeNoConnection = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            DateTime dateTimeRecieved = RecieveNetworkTime();
 
            if (dateTimeRecieved != dateTimeNoConnection)
            {
                Console.WriteLine("Network Time: {0}", dateTimeRecieved.ToShortTimeString());
            }
            else
            {
                Console.WriteLine("Network Time: No Connection");
            }            
            Console.ReadKey();
        }
 
        static DateTime RecieveNetworkTime()
        {
            string[] ntpServers = { "time-a.nist.gov", "time.windows.com", "pool.ntp.org" };
            WaitHandle[] waitHandles = new WaitHandle[ntpServers.Length];
            Func<string, DateTime>[] funcs = new Func<string, DateTime>[ntpServers.Length];
            IAsyncResult[] asyncResults = new IAsyncResult[ntpServers.Length];
            for (int i = 0; i < ntpServers.Length; i++)
            {
                funcs[i] = NtpClient.GetNetworkTime;
                asyncResults[i] = funcs[i].BeginInvoke(ntpServers[i], null, null);
                waitHandles[i] = asyncResults[i].AsyncWaitHandle;
            }
            int idx = WaitHandle.WaitAny(waitHandles);
            DateTime d = funcs[idx].EndInvoke(asyncResults[idx]);
            return d;
        }
    }
 
    /// <summary>
    /// Class for acquiring time via Ntp. Useful for applications in which correct world time must be used and the 
    /// clock on the device isn't "trusted."
    /// </summary>
    public static class NtpClient
    {
        public static DateTime GetNetworkTime(string ntpServer)
        {
            //**UTC** time
            //var 
            DateTime networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc));
 
            // NTP message size - 16 bytes of the digest (RFC 2030)
            var ntpData = new byte[48];
 
            //Setting the Leap Indicator, Version Number and Mode values
            ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
 
            //Setting adresses list
            var addresses = Dns.GetHostEntry(ntpServer).AddressList;
 
            //The UDP port number assigned to NTP is 123
            var ipEndPoint = new IPEndPoint(addresses[0], 123);
 
            //NTP uses UDP
            using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
            {
                //Stops code hang if NTP is blocked
                socket.ReceiveTimeout = 3000;
                //Connecting, sending, recieving data
                socket.Connect(ipEndPoint);
                socket.Send(ntpData);
                socket.Receive(ntpData);
            }
 
            //Offset to get to the "Transmit Timestamp" field (time at which the reply 
            //departed the server for the client, in 64-bit timestamp format."
            const byte serverReplyTime = 40;
 
            //Get the seconds part
            ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
 
            //Get the seconds fraction
            ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
 
            //Convert From big-endian to little-endian
            intPart = SwapEndianness(intPart);
            fractPart = SwapEndianness(fractPart);
 
            var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
 
            return networkDateTime = networkDateTime.AddMilliseconds((long)milliseconds);
        }
 
        // stackoverflow.com/a/3294698/162671
        static uint SwapEndianness(ulong x)
        {
            return (uint)(((x & 0x000000ff) << 24) +
                          ((x & 0x0000ff00) << 8) +
                          ((x & 0x00ff0000) >> 8) +
                          ((x & 0xff000000) >> 24));
        }
    }
}
Добавлено через 16 часов 28 минут

======================================== ==========================

У меня возникла идея с возможным решением описанной мною выше задачи.

Но я не знаю каким образом её можно корректно реализовать в данном случае.

Для решения этой задачи необходимо по достижению socket.ReceiveTimeout = 3000 генерировать какое-то своё собственное специализированное исключение (например, с именем NtpServerTimeoutException).

Это своё сгенерированное исключение NtpServerTimeoutException необходимо затем как-то пробрасывать вверх по цепочке обработки исключений.

И в самом конце обработки необходимо сделать проверку на то, совпадает ли имя отловленного исключения с именем сгенерированного на начальном этапе исключения NtpServerTimeoutException.

Если причиной отловленного исключения служит исключение NtpServerTimeoutException, то в методе RecieveNetworkTime() необходимо возвращать значение:

C#
1
return new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
которое служит у меня в качестве признака невозможности установить сетевое соединение с удаленным сервером.

Скажите, каким образом должен выглядеть код с реализацией этого подхода в данном случае?
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18298 / 14222 / 5368
Регистрация: 17.03.2014
Сообщений: 28,900
Записей в блоге: 1
05.04.2017, 13:48
InessaSuper, начиная с .NET 3.5 есть перегрузка WaitAny с таймаутом. Есть возможность использовать .NET 3.5?
1
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
05.04.2017, 22:20  [ТС]
Я попробовала использовать эту перегрузку метода WaitAny c тайм-аутом в своём проекте под .NET 2, и она заработала!

Спасибо.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
05.04.2017, 22:20
Помогаю со студенческими работами здесь

Получение текущего значения переменной семафора
semControl = CreateSemaphore(NULL, n, n + n, _T(&quot;SemControl&quot;)); int currentValueSem = 0; ReleaseSemaphore(semControl, 0,...

Получение текущего значения из Html.Dropdownlist
Добрый вечер! Есть представление, где элементы из базы данных представлены в виде Html.Dropdownlist. Пользователь должен выбрать...

Получение текущего значения ячейки gridview в другой форме
Необходимо получить текущее значение ячейки greedview (devexpress) в другой форме. Пробовал так: Главная форма: OrderEdit orderEdit =...

Остановка цикла, перебирающего числа и получение текущего значения
задача состоит в следующем,нужно чтобы в подпрограмме сначала высчитывалось значение для х от -14 до 14.притом оно должно скакать,т.е....

Сохранение в файл значения секунд текущего времени
Здравствуйте всем. Нужно сделать программу на языке Ассемблер. В общем программа должна считывать поточную секунду и выводить её на экран...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Отправка уведомления на почту при изменении наименования справочника
Maks 24.03.2026
Программная отправка письма электронной почты на примере изменения наименования типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной. . .
модель ЗдравоСохранения 5. Меньше увольнений- больше дохода!
anaschu 24.03.2026
Теперь система здравосохранения уменьшает количество увольнений. 9TO2GP2bpX4 a42b81fb172ffc12ca589c7898261ccb/ https:/ / rutube. ru/ video/ a42b81fb172ffc12ca589c7898261ccb/ Слева синяя линия -. . .
Midnight Chicago Blues
kumehtar 24.03.2026
Такой Midnight Chicago Blues, знаешь?. . Когда вечерние улицы становятся ночными, а ты не можешь уснуть. Ты идёшь в любимый старый бар, и бармен наливает тебе виски. Ты смотришь на пролетающие. . .
SDL3 для Desktop (MinGW): Вывод текста со шрифтом TTF с помощью библиотеки SDL3_ttf на Си и C++
8Observer8 24.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-text-sdl3-c. zip finish-text-sdl3-cpp. zip
Жизнь в неопределённости
kumehtar 23.03.2026
Жизнь — это постоянное существование в неопределённости. Например, даже если у тебя есть список дел, невозможно дойти до точки, где всё окончательно завершено и больше ничего не осталось. В принципе,. . .
Модель здравоСохранения: работники работают быстрее после её введения.
anaschu 23.03.2026
geJalZw1fLo Корпорация до введения программа здравоохранения имела много невыполненных работниками заданий, после введения программы количество заданий выросло. Но на выплатах по больничным это. . .
Контроль уникальности заводского номера
Maks 23.03.2026
Алгоритм контроля уникальности заводского (или серийного) номера на примере нетипового документа выдачи шин для спецтехники с табличной частью, разработанного в конфигурации КА2. Данные берутся из. . .
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru