SNTP サーバーとの時刻比較(その2)

SNTP サーバーとの時刻比較(その1)の続きです。前回は INtpMessage インターフェイスと NtpMessage クラスを書いたので、残りは INtpClient インターフェイスと NtpClient クラス、NtpTimeStampUtil クラス、NtpMessageException クラス、Program クラスになります。

まずは INtpMessage インターフェイスから。

using System;

namespace Sntp
{
    public interface INtpClient
    {
        /// <summary>
        /// NTP メッセージを取得する
        /// </summary>
        byte[] Message { get; }
        /// <summary>
        /// NTP サーバーの IP アドレスを取得/設定する
        /// </summary>
        string ServerIpAddr { get; set; }
        /// <summary>
        /// Leap indicator を取得する
        /// </summary>
        LeapIndicatorValue LeapIndicator { get; }
        /// <summary>
        /// SNTP version number を取得する
        /// </summary>
        byte VersionNumber { get; }
        /// <summary>
        /// Stratum number を取得する
        /// </summary>
        byte StratumNumber { get; }
        /// <summary>
        /// 発信タイムスタンプを取得する
        /// </summary>
        DateTime OriginateTimestamp { get; }
        /// <summary>
        /// 受信タイムスタンプを取得する
        /// </summary>
        DateTime ReceiveTimestamp { get; }
        /// <summary>
        /// 送信タイムスタンプを取得する
        /// </summary>
        DateTime TransmitTimestamp { get; }
        /// <summary>
        /// 到着地タイムスタンプを設定/取得する(ローカルタイム)
        /// </summary>
        DateTime DestinationTimestamp { get; }
        /// <summary>
        /// 往復の遅延時間を取得する
        /// </summary>
        uint DelayTime { get; }
        /// <summary>
        /// ローカル時計の誤差を取得する
        /// </summary>
        TimeSpan LocalClockOffset { get; }
        /// <summary>
        /// UDP クライアントを用いて NTP サーバーと会話を行う
        /// </summary>
        void Connect();
    }
}

次は NtpClient クラスです。いっきに書くには長いので、分割して書きます。

using System;
using System.Runtime.CompilerServices;

// TestTagManage からのアクセスだけは受け入れる
[assembly: InternalsVisibleTo("NtpGetTimestamp.Test")]

namespace Sntp
{
    public class NtpClient : INtpClient
    {
        private const int REMOTE_PORT = 123;
        private const int RECEIVE_TIMEOUT = 1000;     // 受信タイムアウト 1,000ミリ秒

        internal System.Net.IPEndPoint endPoint = null;
        private INtpMessage ntpMessage;
        internal ulong destinationTimestamp = 0;

        /// <summary>
        /// NTP メッセージと UDP クライアントを作成する
        /// </summary>
        public NtpClient() : this(new NtpMessage()) { }
        public NtpClient(INtpMessage message)
        {
            ntpMessage = message;
        }
//
        /// <summary>
        /// NTP メッセージを取得する
        /// </summary>
        public byte[] Message
        {
            get { return ntpMessage.NtpMessageArray; }
        }

        /// <summary>
        /// NTP サーバーの IP アドレスを取得/設定する
        /// </summary>
        public string ServerIpAddr
        {
            get { return endPoint.Address.ToString(); }
            set
            {
                System.Net.IPAddress serverIpAddr = null;
                if (!System.Net.IPAddress.TryParse(value, out serverIpAddr))
                {
                    throw new ArgumentException("IP アドレスではありません。");
                }

                endPoint = new System.Net.IPEndPoint(serverIpAddr, REMOTE_PORT);
            }
        }

        /// <summary>
        /// Leap indicator を取得する
        /// </summary>
        public LeapIndicatorValue LeapIndicator
        {
            get { return ntpMessage.LeapIndicator; }
        }

        /// <summary>
        /// SNTP version number を取得する
        /// </summary>
        public byte VersionNumber
        {
            get { return ntpMessage.VersionNumber; }
        }

        /// <summary>
        /// Stratum number を取得する
        /// </summary>
        public byte StratumNumber
        {
            get { return ntpMessage.StratumNumber; }
        }
//
        /// <summary>
        /// 発信タイムスタンプを取得する
        /// </summary>
        public DateTime OriginateTimestamp
        {
            get
            {
                return NtpTimeStampUtil.NtpTimeStamp2LocalTime(ntpMessage.OriginateTimestamp);
            }
        }

        /// <summary>
        /// 受信タイムスタンプを取得する
        /// </summary>
        public DateTime ReceiveTimestamp
        {
            get { return NtpTimeStampUtil.NtpTimeStamp2LocalTime(ntpMessage.ReceiveTimestamp); }
        }

        /// <summary>
        /// 送信タイムスタンプを取得する
        /// </summary>
        public DateTime TransmitTimestamp
        {
            get { return NtpTimeStampUtil.NtpTimeStamp2LocalTime(ntpMessage.TransmitTimestamp); }
        }

        /// <summary>
        /// 到着地タイムスタンプを設定/取得する(ローカルタイム)
        /// </summary>
        public DateTime DestinationTimestamp
        {
            get
            {
                if (destinationTimestamp == 0)
                    throw new InvalidOperationException("まだ NTP サーバーからメッセージを取得していません。");

                return NtpTimeStampUtil.NtpTimeStamp2LocalTime(destinationTimestamp);
            }
        }
//
        /// <summary>
        /// 往復の遅延時間を取得する
        /// </summary>
        public uint DelayTime
        {
            get
            {
                if (destinationTimestamp == 0)
                    throw new InvalidOperationException("まだ NTP サーバーからメッセージを取得していません。");

                var d = ((destinationTimestamp - ntpMessage.OriginateTimestamp) -
                    (ntpMessage.ReceiveTimestamp - ntpMessage.TransmitTimestamp));

                return (uint)((double)d * 1000 / 0x100000000);
            }
        }

        /// <summary>
        /// ローカル時計の誤差を取得する
        /// </summary>
        public TimeSpan LocalClockOffset
        {
            get
            {
                if (destinationTimestamp == 0)
                    throw new InvalidOperationException("まだ NTP サーバーからメッセージを取得していません。");

                var t = ((ntpMessage.ReceiveTimestamp - ntpMessage.OriginateTimestamp) +
                         (ntpMessage.TransmitTimestamp - destinationTimestamp)) / 2;

                return TimeSpan.FromMilliseconds(NtpTimeStampUtil.NtpTimeStamp2Milliseconds(t));
            }
        }
//
        /// <summary>
        /// UDP クライアントを用いて NTP サーバーと会話を行う
        /// </summary>
        public void Connect()
        {
            ntpMessage.InitializeMessage();
            var message = ntpMessage.NtpMessageArray;
            using (var udpClient = new System.Net.Sockets.UdpClient(endPoint.Address.AddressFamily))
            {
                udpClient.Send(message, message.Length, endPoint);

                udpClient.Client.ReceiveTimeout = RECEIVE_TIMEOUT;
                (ntpMessage as NtpMessage).NtpMessageArray = udpClient.Receive(ref endPoint);
            }

            if (!isResponseValid())
                throw new NtpMessageException(string.Format(
                    "NTP サーバー[{0}]のレスポンスが不正でした。", endPoint.Address.ToString()));
            destinationTimestamp = NtpTimeStampUtil.Datetime2NtpTimeStamp(DateTime.UtcNow);
        }

        internal bool isResponseValid()
        {
            if (ntpMessage.NtpMessageArray.Length == ntpMessage.MessageLength &&
                ntpMessage.Mode == ModeValue.Server)
                return true;
            else
                return false;

        }
    }
}

長くなったので、(その3)へ続きます。


SNTP サーバーとの時刻比較(その2)」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です