프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

markus / Rhino.Licensing / SntpClient.cs @ 42d49521

이력 | 보기 | 이력해설 | 다운로드 (5.01 KB)

1 42d49521 taeseongkim
using System;
2
using System.Net;
3
using System.Net.Sockets;
4
using System.Threading;
5
6
namespace Rhino.Licensing
7
{
8
    /// <summary>
9
    /// 
10
    /// </summary>
11
	public class SntpClient
12
	{
13
		private const byte SntpDataLength = 48;
14
		private readonly string[] hosts;
15
		private int index = -1;
16
17
        /// <summary>
18
        /// 
19
        /// </summary>
20
        /// <param name="hosts"></param>
21
		public SntpClient(string[] hosts)
22
		{
23
			this.hosts = hosts;
24
		}
25
26
		private static bool GetIsServerMode(byte[] sntpData)
27
		{
28
			return (sntpData[0] & 0x7) == 4 /* server mode */;
29
		}
30
31
		private static DateTime GetTransmitTimestamp(byte[] sntpData)
32
		{
33
			var milliSeconds = GetMilliSeconds(sntpData, 40);
34
			return ComputeDate(milliSeconds);
35
		}
36
37
		private static DateTime ComputeDate(ulong milliseconds)
38
		{
39
			return new DateTime(1900, 1, 1).Add(TimeSpan.FromMilliseconds(milliseconds));
40
		}
41
42
		private static ulong GetMilliSeconds(byte[] sntpData, byte offset)
43
		{
44
			ulong intpart = 0, fractpart = 0;
45
46
			for (var i = 0; i <= 3; i++)
47
			{
48
				intpart = 256*intpart + sntpData[offset + i];
49
			}
50
			for (var i = 4; i <= 7; i++)
51
			{
52
				fractpart = 256*fractpart + sntpData[offset + i];
53
			}
54
			var milliseconds = intpart*1000 + (fractpart*1000)/0x100000000L;
55
			return milliseconds;
56
		}
57
58
        /// <summary>
59
        /// 
60
        /// </summary>
61
        /// <param name="getTime"></param>
62
        /// <param name="failure"></param>
63
		public void BeginGetDate(Action<DateTime> getTime, Action failure)
64
		{
65
			index += 1;
66
			if (hosts.Length <= index)
67
			{
68
				if (hosts.Length == index)
69
				{
70
					failure();
71
				}
72
				return;
73
			}
74
			try
75
			{
76
				var host = hosts[index];
77
				var state = new State(null, null, getTime, failure);
78
				var result = Dns.BeginGetHostAddresses(host, EndGetHostAddress, state );
79
				RegisterWaitForTimeout(state, result);
80
			}
81
			catch (Exception)
82
			{
83
				// retry, recursion stops at the end of the hosts
84
				BeginGetDate(getTime, failure);
85
			}
86
		}
87
88
	    private void EndGetHostAddress(IAsyncResult asyncResult)
89
        {
90
	        var state = (State) asyncResult.AsyncState;
91
	        try
92
	        {
93
	            var addresses = Dns.EndGetHostAddresses(asyncResult);
94
	            var endPoint = new IPEndPoint(addresses[0], 123);
95
96
	            var socket = new UdpClient();
97
	            socket.Connect(endPoint);
98
	            socket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 500);
99
	            socket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 500);
100
	            var sntpData = new byte[SntpDataLength];
101
	            sntpData[0] = 0x1B; // version = 4 & mode = 3 (client)
102
103
	        	var newState = new State(socket, endPoint, state.GetTime, state.Failure);
104
	        	var result = socket.BeginSend(sntpData, sntpData.Length, EndSend, newState);
105
				RegisterWaitForTimeout(newState, result);
106
	        }
107
	        catch (Exception)
108
	        {
109
                // retry, recursion stops at the end of the hosts
110
                BeginGetDate(state.GetTime, state.Failure);
111
	
112
	        }
113
	    }
114
115
		private void RegisterWaitForTimeout(State newState, IAsyncResult result)
116
		{
117
			if(result != null)
118
				ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, MaybeOperationTimeout, newState, 500, true);
119
		}
120
121
		private void MaybeOperationTimeout(object state, bool timedOut)
122
		{
123
			if (timedOut == false)
124
				return;
125
126
			var theState = (State)state;
127
			try
128
			{
129
				if (theState.Socket != null)
130
				{
131
					theState.Socket.Close();
132
				}
133
			}
134
			catch (Exception)
135
			{
136
				// retry, recursion stops at the end of the hosts
137
				BeginGetDate(theState.GetTime, theState.Failure);
138
			}
139
		}
140
141
		private void EndSend(IAsyncResult ar)
142
		{
143
			var state = (State) ar.AsyncState;
144
			try
145
			{
146
				state.Socket.EndSend(ar);
147
				var result = state.Socket.BeginReceive(EndReceive, state);
148
				RegisterWaitForTimeout(state, result);
149
			}
150
			catch
151
			{
152
				state.Socket.Close();
153
				BeginGetDate(state.GetTime, state.Failure);
154
			}
155
		}
156
157
		private void EndReceive(IAsyncResult ar)
158
		{
159
			var state = (State) ar.AsyncState;
160
			try
161
			{
162
				var endPoint = state.EndPoint;
163
				var sntpData = state.Socket.EndReceive(ar, ref endPoint);
164
				if(IsResponseValid(sntpData)==false)
165
				{
166
					state.Failure();
167
					return;
168
				}
169
				var transmitTimestamp = GetTransmitTimestamp(sntpData);
170
				state.GetTime(transmitTimestamp);
171
			}
172
			catch
173
			{
174
				BeginGetDate(state.GetTime, state.Failure);
175
			}
176
			finally
177
			{
178
				state.Socket.Close();
179
			}
180
		}
181
182
		private bool IsResponseValid(byte[] sntpData)
183
		{
184
			return sntpData.Length >= SntpDataLength && GetIsServerMode(sntpData);
185
		}
186
187
		#region Nested type: State
188
189
		private class State
190
		{
191
			public State(UdpClient socket, IPEndPoint endPoint, Action<DateTime> getTime, Action failure)
192
			{
193
				Socket = socket;
194
				EndPoint = endPoint;
195
				GetTime = getTime;
196
				Failure = failure;
197
			}
198
199
			public UdpClient Socket { get; private set; }
200
201
			public Action<DateTime> GetTime { get; private set; }
202
203
			public Action Failure { get; private set; }
204
205
			public IPEndPoint EndPoint { get; private set; }
206
		}
207
208
		#endregion
209
	}
210
}
클립보드 이미지 추가 (최대 크기: 500 MB)