在windows server 2003服务器上提供NTP时间同步服务

本文介绍Network Time Protocol (NTP)的基本概念及其在Windows Server 2003上的配置方法,并提供了一个使用C#实现的NTP客户端示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准 度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来防止恶毒的协议攻击。

  NTP采用C/S结构,Server端作为时间来源,可以是原子钟、天文台、卫星,也可以从Internet上获取;而在我们的局域网内,则可以配置一个服务器,将其硬件时间作为时间源来提供服务;我在网上搜索了一下,大部分关于NTP服务的文章都是基于Linux的,提到windows server系列操作系统的不是很多,不过还是有很实用的内容;总结下来,其实要在windows server 2003上配置NTP服务是一件很简单的事情,如果该服务器是域控制器(DC),则已经默认启动了一个叫做win32time的服务,这个服务就是我们的NTPServer;如果不是域控制器,只需要修改注册表HKEY_LOCAL_MACHINE>>SYSTEM>>CurrentControlSet>>Services>>W32Time>>TimeProviders>>NtpServer下的Enabled为1(true)即可强制其宣布自身为可靠的时间源以启动win32time服务。

  Client端通过主动连接有效的Server端即可同步本地时间;以下是我在CodeProject上找到的有关NTPClient在C#下实现的代码(原帖地址):

ContractedBlock.gif ExpandedBlockStart.gif NTPClient
/*
 * NTPClient
 * Copyright (C)2001 Valer BOCAN <vbocan@dataman.ro>
 * Last modified: June 29, 2001
 * All Rights Reserved
 * 
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * To fully understand the concepts used herein, I strongly
 * recommend that you read the RFC 2030.
 * 
 * NOTE: This example is intended to be compiled with Visual Studio .NET Beta 2
 
*/

namespace TimeSync {
    
using System;
    
using System.Net;
    
using System.Net.Sockets;
    
using System.Runtime.InteropServices;

    
// Leap indicator field values
    public enum _LeapIndicator {
        NoWarning,        
// 0 - No warning
        LastMinute61,    // 1 - Last minute has 61 seconds
        LastMinute59,    // 2 - Last minute has 59 seconds
        Alarm            // 3 - Alarm condition (clock not synchronized)
    }

    
//Mode field values
    public enum _Mode {
        SymmetricActive,    
// 1 - Symmetric active
        SymmetricPassive,    // 2 - Symmetric pasive
        Client,                // 3 - Client
        Server,                // 4 - Server
        Broadcast,            // 5 - Broadcast
        Unknown                // 0, 6, 7 - Reserved
    }

    
// Stratum field values
    public enum _Stratum {
        Unspecified,            
// 0 - unspecified or unavailable
        PrimaryReference,        // 1 - primary reference (e.g. radio-clock)
        SecondaryReference,        // 2-15 - secondary reference (via NTP or SNTP)
        Reserved                // 16-255 - reserved
    }

    
/// <summary>
    
/// NTPClient is a C# class designed to connect to time servers on the Internet.
    
/// The implementation of the protocol is based on the RFC 2030.
    
/// 
    
/// Public class members:
    
///
    
/// LeapIndicator - Warns of an impending leap second to be inserted/deleted in the last
    
/// minute of the current day. (See the _LeapIndicator enum)
    
/// 
    
/// VersionNumber - Version number of the protocol (3 or 4).
    
/// 
    
/// Mode - Returns mode. (See the _Mode enum)
    
/// 
    
/// Stratum - Stratum of the clock. (See the _Stratum enum)
    
/// 
    
/// PollInterval - Maximum interval between successive messages.
    
/// 
    
/// Precision - Precision of the clock.
    
/// 
    
/// RootDelay - Round trip time to the primary reference source.
    
/// 
    
/// RootDispersion - Nominal error relative to the primary reference source.
    
/// 
    
/// ReferenceID - Reference identifier (either a 4 character string or an IP address).
    
/// 
    
/// ReferenceTimestamp - The time at which the clock was last set or corrected.
    
/// 
    
/// OriginateTimestamp - The time at which the request departed the client for the server.
    
/// 
    
/// ReceiveTimestamp - The time at which the request arrived at the server.
    
/// 
    
/// Transmit Timestamp - The time at which the reply departed the server for client.
    
/// 
    
/// RoundTripDelay - The time between the departure of request and arrival of reply.
    
/// 
    
/// LocalClockOffset - The offset of the local clock relative to the primary reference
    
/// source.
    
/// 
    
/// Initialize - Sets up data structure and prepares for connection.
    
/// 
    
/// Connect - Connects to the time server and populates the data structure.
    
///    It can also set the system time.
    
/// 
    
/// IsResponseValid - Returns true if received data is valid and if comes from
    
/// a NTP-compliant time server.
    
/// 
    
/// ToString - Returns a string representation of the object.
    
/// 
    
/// -----------------------------------------------------------------------------
    
/// Structure of the standard NTP header (as described in RFC 2030)
    
///                       1                   2                   3
    
///   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                          Root Delay                           |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                       Root Dispersion                         |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                     Reference Identifier                      |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                                                               |
    
///  |                   Reference Timestamp (64)                    |
    
///  |                                                               |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                                                               |
    
///  |                   Originate Timestamp (64)                    |
    
///  |                                                               |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                                                               |
    
///  |                    Receive Timestamp (64)                     |
    
///  |                                                               |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                                                               |
    
///  |                    Transmit Timestamp (64)                    |
    
///  |                                                               |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                 Key Identifier (optional) (32)                |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
///  |                                                               |
    
///  |                                                               |
    
///  |                 Message Digest (optional) (128)               |
    
///  |                                                               |
    
///  |                                                               |
    
///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
/// 
    
/// -----------------------------------------------------------------------------
    
/// 
    
/// NTP Timestamp Format (as described in RFC 2030)
    
///                         1                   2                   3
    
///     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
/// |                           Seconds                             |
    
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
/// |                  Seconds Fraction (0-padded)                  |
    
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
/// 
    
/// </summary>

    
public class NTPClient {
        
// NTP Data Structure Length
        private const byte NTPDataLength = 48;
        
// NTP Data Structure (as described in RFC 2030)
        byte[] NTPData = new byte[NTPDataLength];

        
// Offset constants for timestamps in the data structure
        private const byte offReferenceID = 12;
        
private const byte offReferenceTimestamp = 16;
        
private const byte offOriginateTimestamp = 24;
        
private const byte offReceiveTimestamp = 32;
        
private const byte offTransmitTimestamp = 40;

        
// Leap Indicator
        public _LeapIndicator LeapIndicator {
            
get {
                
// Isolate the two most significant bits
                byte val = (byte)(NTPData[0>> 6);
                
switch (val) {
                    
case 0return _LeapIndicator.NoWarning;
                    
case 1return _LeapIndicator.LastMinute61;
                    
case 2return _LeapIndicator.LastMinute59;
                    
case 3goto default;
                    
default:
                        
return _LeapIndicator.Alarm;
                }
            }
        }

        
// Version Number
        public byte VersionNumber {
            
get {
                
// Isolate bits 3 - 5
                byte val = (byte)((NTPData[0& 0x38>> 3);
                
return val;
            }
        }

        
// Mode
        public _Mode Mode {
            
get {
                
// Isolate bits 0 - 3
                byte val = (byte)(NTPData[0& 0x7);
                
switch (val) {
                    
case 0goto default;
                    
case 6goto default;
                    
case 7goto default;
                    
default:
                        
return _Mode.Unknown;
                    
case 1:
                        
return _Mode.SymmetricActive;
                    
case 2:
                        
return _Mode.SymmetricPassive;
                    
case 3:
                        
return _Mode.Client;
                    
case 4:
                        
return _Mode.Server;
                    
case 5:
                        
return _Mode.Broadcast;
                }
            }
        }

        
// Stratum
        public _Stratum Stratum {
            
get {
                
byte val = (byte)NTPData[1];
                
if (val == 0return _Stratum.Unspecified;
                
else
                    
if (val == 1return _Stratum.PrimaryReference;
                    
else
                        
if (val <= 15return _Stratum.SecondaryReference;
                        
else
                            
return _Stratum.Reserved;
            }
        }

        
// Poll Interval
        public uint PollInterval {
            
get {
                
return (uint)Math.Round(Math.Pow(2, NTPData[2]));
            }
        }

        
// Precision (in milliseconds)
        public double Precision {
            
get {
                
return (1000 * Math.Pow(2, NTPData[3]));
            }
        }

        
// Root Delay (in milliseconds)
        public double RootDelay {
            
get {
                
int temp = 0;
                temp 
= 256 * (256 * (256 * NTPData[4+ NTPData[5]) + NTPData[6]) + NTPData[7];
                
return 1000 * (((double)temp) / 0x10000);
            }
        }

        
// Root Dispersion (in milliseconds)
        public double RootDispersion {
            
get {
                
int temp = 0;
                temp 
= 256 * (256 * (256 * NTPData[8+ NTPData[9]) + NTPData[10]) + NTPData[11];
                
return 1000 * (((double)temp) / 0x10000);
            }
        }

        
// Reference Identifier
        public string ReferenceID {
            
get {
                
string val = "";
                
switch (Stratum) {
                    
case _Stratum.Unspecified:
                        
goto case _Stratum.PrimaryReference;
                    
case _Stratum.PrimaryReference:
                        val 
+= (char)NTPData[offReferenceID + 0];
                        val 
+= (char)NTPData[offReferenceID + 1];
                        val 
+= (char)NTPData[offReferenceID + 2];
                        val 
+= (char)NTPData[offReferenceID + 3];
                        
break;
                    
case _Stratum.SecondaryReference:
                        
switch (VersionNumber) {
                            
case 3:    // Version 3, Reference ID is an IPv4 address
                                string Address = NTPData[offReferenceID + 0].ToString() + "." +
                                                 NTPData[offReferenceID 
+ 1].ToString() + "." +
                                                 NTPData[offReferenceID 
+ 2].ToString() + "." +
                                                 NTPData[offReferenceID 
+ 3].ToString();
                                
try {
                                    IPHostEntry Host 
= Dns.GetHostByAddress(Address);
                                    val 
= Host.HostName + " (" + Address + ")";
                                } 
catch (Exception) {
                                    val 
= "N/A";
                                }
                                
break;
                            
case 4// Version 4, Reference ID is the timestamp of last update
                                DateTime time = ComputeDate(GetMilliSeconds(offReferenceID));
                                
// Take care of the time zone
                                TimeSpan offspan = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
                                val 
= (time + offspan).ToString();
                                
break;
                            
default:
                                val 
= "N/A";
                                
break;
                        }
                        
break;
                }

                
return val;
            }
        }

        
// Reference Timestamp
        public DateTime ReferenceTimestamp {
            
get {
                DateTime time 
= ComputeDate(GetMilliSeconds(offReferenceTimestamp));
                
// Take care of the time zone
                TimeSpan offspan = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
                
return time + offspan;
            }
        }

        
// Originate Timestamp
        public DateTime OriginateTimestamp {
            
get {
                
return ComputeDate(GetMilliSeconds(offOriginateTimestamp));
            }
        }

        
// Receive Timestamp
        public DateTime ReceiveTimestamp {
            
get {
                DateTime time 
= ComputeDate(GetMilliSeconds(offReceiveTimestamp));
                
// Take care of the time zone
                TimeSpan offspan = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
                
return time + offspan;
            }
        }

        
// Transmit Timestamp
        public DateTime TransmitTimestamp {
            
get {
                DateTime time 
= ComputeDate(GetMilliSeconds(offTransmitTimestamp));
                
// Take care of the time zone
                TimeSpan offspan = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
                
return time + offspan;
            }
            
set {
                SetDate(offTransmitTimestamp, value);
            }
        }

        
// Reception Timestamp
        public DateTime ReceptionTimestamp;

        
// Round trip delay (in milliseconds)
        public int RoundTripDelay {
            
get {
                TimeSpan span 
= (ReceiveTimestamp - OriginateTimestamp) + (ReceptionTimestamp - TransmitTimestamp);
                
return (int)span.TotalMilliseconds;
            }
        }

        
// Local clock offset (in milliseconds)
        public int LocalClockOffset {
            
get {
                TimeSpan span 
= (ReceiveTimestamp - OriginateTimestamp) - (ReceptionTimestamp - TransmitTimestamp);
                
return (int)(span.TotalMilliseconds / 2);
            }
        }

        
// Compute date, given the number of milliseconds since January 1, 1900
        private DateTime ComputeDate(ulong milliseconds) {
            TimeSpan span 
= TimeSpan.FromMilliseconds((double)milliseconds);
            DateTime time 
= new DateTime(190011);
            time 
+= span;
            
return time;
        }

        
// Compute the number of milliseconds, given the offset of a 8-byte array
        private ulong GetMilliSeconds(byte offset) {
            
ulong intpart = 0, fractpart = 0;

            
for (int i = 0; i <= 3; i++) {
                intpart 
= 256 * intpart + NTPData[offset + i];
            }
            
for (int i = 4; i <= 7; i++) {
                fractpart 
= 256 * fractpart + NTPData[offset + i];
            }
            
ulong milliseconds = intpart * 1000 + (fractpart * 1000/ 0x100000000L;
            
return milliseconds;
        }

        
// Compute the 8-byte array, given the date
        private void SetDate(byte offset, DateTime date) {
            
ulong intpart = 0, fractpart = 0;
            DateTime StartOfCentury 
= new DateTime(190011000);    // January 1, 1900 12:00 AM

            
ulong milliseconds = (ulong)(date - StartOfCentury).TotalMilliseconds;
            intpart 
= milliseconds / 1000;
            fractpart 
= ((milliseconds % 1000* 0x100000000L/ 1000;

            
ulong temp = intpart;
            
for (int i = 3; i >= 0; i--) {
                NTPData[offset 
+ i] = (byte)(temp % 256);
                temp 
= temp / 256;
            }

            temp 
= fractpart;
            
for (int i = 7; i >= 4; i--) {
                NTPData[offset 
+ i] = (byte)(temp % 256);
                temp 
= temp / 256;
            }
        }

        
// Initialize the NTPClient data
        private void Initialize() {
            
// Set version number to 4 and Mode to 3 (client)
            NTPData[0= 0x1B;
            
// Initialize all other fields with 0
            for (int i = 1; i < 48; i++) {
                NTPData[i] 
= 0;
            }
            
// Initialize the transmit timestamp
            TransmitTimestamp = DateTime.Now;
        }

        
public NTPClient(string host) {
            TimeServer 
= host;
        }

        
// Connect to the time server and update system time
        public void Connect(bool UpdateSystemTime) {
            
try {
                
// Resolve server address
                IPHostEntry hostadd = Dns.Resolve(TimeServer);
                IPEndPoint EPhost 
= new IPEndPoint(hostadd.AddressList[0], 123);

                
//Connect the time server
                UdpClient TimeSocket = new UdpClient();
                TimeSocket.Connect(EPhost);

                
// Initialize data structure
                Initialize();
                TimeSocket.Send(NTPData, NTPData.Length);
                NTPData 
= TimeSocket.Receive(ref EPhost);
                
if (!IsResponseValid()) {
                    
throw new Exception("Invalid response from " + TimeServer);
                }
                ReceptionTimestamp 
= DateTime.Now;
            } 
catch (SocketException e) {
                
throw new Exception(e.Message);
            }

            
// Update system time
            if (UpdateSystemTime) {
                SetTime();
            }
        }

        
// Check if the response from server is valid
        public bool IsResponseValid() {
            
if (NTPData.Length < NTPDataLength || Mode != _Mode.Server) {
                
return false;
            } 
else {
                
return true;
            }
        }

        
// Converts the object to string
        public override string ToString() {
            
string str;

            str 
= "Leap Indicator: ";
            
switch (LeapIndicator) {
                
case _LeapIndicator.NoWarning:
                    str 
+= "No warning";
                    
break;
                
case _LeapIndicator.LastMinute61:
                    str 
+= "Last minute has 61 seconds";
                    
break;
                
case _LeapIndicator.LastMinute59:
                    str 
+= "Last minute has 59 seconds";
                    
break;
                
case _LeapIndicator.Alarm:
                    str 
+= "Alarm Condition (clock not synchronized)";
                    
break;
            }
            str 
+= "\r\nVersion number: " + VersionNumber.ToString() + "\r\n";
            str 
+= "Mode: ";
            
switch (Mode) {
                
case _Mode.Unknown:
                    str 
+= "Unknown";
                    
break;
                
case _Mode.SymmetricActive:
                    str 
+= "Symmetric Active";
                    
break;
                
case _Mode.SymmetricPassive:
                    str 
+= "Symmetric Pasive";
                    
break;
                
case _Mode.Client:
                    str 
+= "Client";
                    
break;
                
case _Mode.Server:
                    str 
+= "Server";
                    
break;
                
case _Mode.Broadcast:
                    str 
+= "Broadcast";
                    
break;
            }
            str 
+= "\r\nStratum: ";
            
switch (Stratum) {
                
case _Stratum.Unspecified:
                
case _Stratum.Reserved:
                    str 
+= "Unspecified";
                    
break;
                
case _Stratum.PrimaryReference:
                    str 
+= "Primary Reference";
                    
break;
                
case _Stratum.SecondaryReference:
                    str 
+= "Secondary Reference";
                    
break;
            }
            str 
+= "\r\nLocal time: " + TransmitTimestamp.ToString();
            str 
+= "\r\nPrecision: " + Precision.ToString() + " ms";
            str 
+= "\r\nPoll Interval: " + PollInterval.ToString() + " s";
            str 
+= "\r\nReference ID: " + ReferenceID.ToString();
            str 
+= "\r\nRoot Dispersion: " + RootDispersion.ToString() + " ms";
            str 
+= "\r\nRound Trip Delay: " + RoundTripDelay.ToString() + " ms";
            str 
+= "\r\nLocal Clock Offset: " + LocalClockOffset.ToString() + " ms";
            str 
+= "\r\n";

            
return str;
        }

        
// SYSTEMTIME structure used by SetSystemTime
        [StructLayoutAttribute(LayoutKind.Sequential)]
        
private struct SYSTEMTIME {
            
public short year;
            
public short month;
            
public short dayOfWeek;
            
public short day;
            
public short hour;
            
public short minute;
            
public short second;
            
public short milliseconds;
        }

        [DllImport(
"kernel32.dll")]
        
static extern bool SetLocalTime(ref SYSTEMTIME time);


        
// Set system time according to transmit timestamp
        private void SetTime() {
            SYSTEMTIME st;

            DateTime trts 
= TransmitTimestamp;
            st.year 
= (short)trts.Year;
            st.month 
= (short)trts.Month;
            st.dayOfWeek 
= (short)trts.DayOfWeek;
            st.day 
= (short)trts.Day;
            st.hour 
= (short)trts.Hour;
            st.minute 
= (short)trts.Minute;
            st.second 
= (short)trts.Second;
            st.milliseconds 
= (short)trts.Millisecond;

            SetLocalTime(
ref st);
        }

        
// The URL of the time server we're connecting to
        private string TimeServer;

    }
}

   对NTPClient的调用

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  TimeSync;
using  System.Configuration;

namespace  CommandLine {
    
class  Program {
        
static   void  Main( string [] args) {
            timeServer 
=  ConfigurationManager.AppSettings[ " NTPServer " ];
            Console.WriteLine(
" Connecting to:{0} " , timeServer);

            
try  {
                client 
=   new  NTPClient(timeServer);
                client.Connect(
true );

                Console.Write(client.ToString());
            } 
catch  (Exception ex) {
                Console.WriteLine(ex);
            }

            Console.WriteLine(
" == End == " );
            Console.ReadKey();
        }

        
private   static   string  timeServer;
        
private   static  NTPClient client;
    }
}

   配置文件:

<? xml version = " 1.0 "  encoding = " utf-8 "   ?>
< configuration >
    
< appSettings >
        
< add key = " NTPServer "  value = " 192.168.16.101 " />
    
</ appSettings >
</ configuration >

   运行结果:

 

  对于大型局域网,可以使用多台服务器在逻辑上形成阶梯式的架构相互连接,对服务器进行分层(Stratum),Stratum-1的时间服务器是整个系统的基础,Stratum-2从Stratum-1获取时间,Stratum-3从Stratum-2获取时间,以此类推,Stratum的总数限制在15以内;由于我目前只是使用单一服务器提供整个局域网内的时间服务,因此服务端配置比较简单,而层的配置则相对复杂,还有很多需要研究的地方。

转载于:https://www.cnblogs.com/acexist/archive/2009/05/15/1457520.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值