C#でSNTPから現在日時を取得

chakemiです。今回はC#でSNTPサーバから現在日時を取得してみたいと思います。 SNTP(Simple Network Time Protocol)は、名前の通り簡易ネットワーク時刻プロトコルでNTPの仕様から複雑な部分を省略した形となっています。

SNTPプロトコルについては、こちらや、こちらを参考にしてください。ってか、自分も参考にさせて頂きました。

開発環境

  • WindowsXP Pro SP3
  • VisualStudio2008

では、さっそく。

UDPクライアントを生成します。

1
2
3
System.Net.Sockets.UdpClient sock;
System.Net.IPEndPoint ipAny = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
sock = new System.Net.Sockets.UdpClient(ipAny);

送信用パケットを生成し、送信します。 SNTPサーバは、送られてきた要求パケットの中身を書き換えて、送り返してきます。

1
2
3
4
5
6
7
8
9
10
11
Byte[] s_data = new Byte[48];
//LI, Version, Mode
s_data[0] = 0xE3;
//Stratum
s_data[1] = 0;
//Polling Interval
s_data[2] = 6;
//Precision
s_data[3] = 0xEC;

sock.Send(s_data, s_data.GetLength(0), "time.windows.com", 123);

タイムサーバにはWindowsXPのデフォルトで指定されたタイムサーバを利用させて頂きました。

SNTPから返ってきたパケットを受信します。

1
Byte[] r_data = sock.Receive(ref ipAny);

タイムサーバは1900年1月1日0時を基準に相対的な差を秒単位で、64ビット符号無し固定小数点で返してきます。 整数部の上位32ビットを取得して、経過秒数を取得します。

1
2
3
4
5
6
7
long passSec;

long b0 = (long)r_data[40];
long b1 = (long)r_data[41];
long b2 = (long)r_data[42];
long b3 = (long)r_data[43];
passSec = (b0 << 24 | b1 << 16) | (b2 << 8 | b3);

基準時間に経過秒数を加えて、さらに日本標準時間にあわせます。

1
2
3
4
DateTime dateTime = new DateTime(1900, 1, 1);
dateTime = dateTime.AddSeconds(passSec);

dateTime = dateTime.AddHours(9);

以下、全文です。

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
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace testSNTPClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //UDP生成
            System.Net.Sockets.UdpClient sock;
            System.Net.IPEndPoint ipAny = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
            sock = new System.Net.Sockets.UdpClient(ipAny);

            //Send UDP
            Byte[] s_data = new Byte[48];
            //LI, Version, Mode
            s_data[0] = 0xE3;
            //Stratum
            s_data[1] = 0;
            //Polling Interval
            s_data[2] = 6;
            //Precision
            s_data[3] = 0xEC;

            sock.Send(s_data, s_data.GetLength(0), "time.windows.com", 123);

            //Recieve UDP
            Byte[] r_data = sock.Receive(ref ipAny);

            //1900/01/01から経過秒数
            long passSec;    //全秒数

            //Transmit Timestampの40バイト目からはじまる4バイトのデータ
            long b0 = (long)r_data[40];
            long b1 = (long)r_data[41];
            long b2 = (long)r_data[42];
            long b3 = (long)r_data[43];
            passSec = (b0 << 24 | b1 << 16) | (b2 << 8 | b3);

            //基準時間に経過乗数を加える
            DateTime dateTime = new DateTime(1900, 1, 1);
            dateTime = dateTime.AddSeconds(passSec);
            //日本標準時間にあわせる
            dateTime = dateTime.AddHours(9);

            label1.Text = dateTime.ToString();

        }
    }
}

ネットワークの遅延だとか考慮してません。。。 外部のサーバへアクセスする際は、ご注意ください。

Comments