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件のフィードバック