当前位置:   article > 正文

NTP服务器同步android系统时间_android ntp

android ntp

做android开发有一段时间了,之前一直做服务类的产品,最近公司接手sip话机的项目,里面涉及到修改系统设置的问题,今天就把我在项目中有关NTP服务器同步android系统时间遇到的种种问题,在这做一个总结,希望给后面的人一些帮助,本人也是首次接触这样的项目,总结的如有不完善的地方,希望大神指点出来。。。。


ntp服务器地址

有很多,网可以搜一下
cn.pool.ntp.org
s1a.time.edu.cn 北京邮电大学
s1b.time.edu.cn 清华大学
s1c.time.edu.cn 北京大学
s1d.time.edu.cn 东南大学
s1e.time.edu.cn 清华大学
s2a.time.edu.cn 清华大学
s2b.time.edu.cn 清华大学
中国[China] cn.ntp.org.cn
美国[America] us.ntp.org.cn
韩国[korea] kr.ntp.org.cn
新加坡[Singapore] sgp.ntp.org.cn


由于项目要求,在项目中同步系统时间是每隔多久同步一次系统时间,通过TimerTask来执行任务。通过handler来发送处理消息。相关代码:
/**
*校准时间
* 从ntp服务器中获取时间
* @param ntpHost
* ntp服务器域名地址
* @return 如果失败返回-1,否则返回当前的毫秒数
*/

public static void startCalibrateTime(final String mhostAddress,
			final int cycleTime) {
		MyLog.d(tag, "startCalibrateTime()");
		if (mCycleTimer != null) {
			mCycleTask.cancel();
			mCycleTimer.cancel();
			mCycleTimer.purge();
			mCycleTask = null;
			mCycleTimer = null;
		}
		mCycleTask = new TimerTask() {

			@Override
			public void run() {
				MyLog.d(tag, "run()");
				long time = getTimeFromNtpServer(mhostAddress);//从获取ntp服务器上获取时间
				if (time == -1) {
					MyLog.e(tag, "async time failed.");
				} else {
					SystemClock.setCurrentTimeMillis(time);//设置系统时间
				}
				if (isTurnToSuccess) {
					isTurnToSuccess = false;
					mHandler.sendEmptyMessage(MSG_NTP_SEARCH_OK);
				}
			}
			@Override
			public boolean cancel() {
				MyLog.d(tag, "cancel()");
				return super.cancel();
			}
		};
		mCycleTimer = new NgnTimer();
		mCycleTimer.schedule(mCycleTask, 0, cycleTime);
		MyLog.d(tag, "start ntp timer  time:" + cycleTime / 1000);
	}
  • 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

handler处理消息,在不同的消息出处理不同的操作,针对项目要求,我在项目中保存了ntp服务器地址和每次隔多久同步一次的时间,可以自行定义。
涉及到的变量:
private static String mNtpServer = “pool.ntp.org”;
/**
* NTP获取时间失败时,每隔30s周期性重新获取,直至成功,成功后恢复正常计时
*/
private final static int CYCLE_TIME_ERROR = 30000;

private static Handler mHandler = new Handler() {
		@Override
		public void handleMessage(android.os.Message msg) {
			if (msg.what == MSG_NTP_SEARCH_FAILED) {
				String mhostAddress = GlobalConfigUtils
						.get(ConfTag.DATETIME_SNTP_SERVER);
				if (TextUtils.isEmpty(mhostAddress)) {
					mhostAddress = mNtpServer;
				}
				startCalibrateTime(mhostAddress, CYCLE_TIME_ERROR);
			} else if (msg.what == MSG_NTP_SEARCH_OK) {
				String mhostAddress = GlobalConfigUtils
						.get(ConfTag.DATETIME_SNTP_SERVER);//项目中保存的ntp服务器地址
				if (TextUtils.isEmpty(mhostAddress)) {
					mhostAddress = mNtpServer;
				}
				String timeStr = GlobalConfigUtils			.get(ConfTag.DATETIME_NTP_RESYNC_TIME);//项目中设置的系统默认隔多久同步时间
				int time = 168 * 60 * 60 * 1000;
				if (!TextUtils.isEmpty(timeStr)
						&& TextUtils.isDigitsOnly(timeStr)) {
					time = Integer.parseInt(timeStr)*3600*1000;
				}
				startCalibrateTime(mhostAddress, time);
			}
		};
	};
  • 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

停止校准时间

public static void stopCalibrateTimer() {
		if (mCycleTimer != null) {
			mCycleTask.cancel();
			mCycleTimer.cancel();
			mCycleTimer.purge();
			mCycleTask = null;
			mCycleTimer = null;
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最重要的部分就是从ntp服务器上获取时间,这块很重要。。重要原因就是NTP工作原理简单分析一下:
下面是NTP工作原理图,图片是从我看的资料上截取过来勿喷。
Device A 与 Device B 通过网络相连,都有自己独立的系统时钟,需要通过ntp实现两个系统时钟的时间同步
1.Device B作为服务器,Device A作为客户端,需要网络使本地时钟与服务器时钟同步,假设同步之前,Device A的时间是10:00:00am,Device B的时间是11:00:00am
2.NTP报文的Device A 和Device B 之间单向传输时间是1秒,
3.Device处理报文时间是1秒

这里写图片描述

Device A 与Devide B 工作流程如下

  • Device A发送一个NTP请求报文给Devide B ,该报文带有Device A 离开时的时间戳,该时间戳为10:00:00am(T1)
  • 当NTP报文到达Devide B 时,Devide B 加上自己的时间戳11:00:01am(T2)
  • 当NTP报文离开Devide B 时,Devide B 再加上自己的时间戳11:00:02am(T3)
  • 当Device A接受到NTP响应报文 时,Devide A本地时间11:00:02am(T4)
    ntp工作原理介绍完了,现在应该很清楚原理了吧,现在回过头来继续。。

获取ntp时间:

public static long getTimeFromNtpServer(String hostAddress) {
		MyLog.d(tag, "getTimeFromNtpServer()");
		if (TextUtils.isEmpty(hostAddress)) {
			MyLog.e(tag, "Ntp host is null.");
			return -1;
		}
		if (mNtpClient == null) {
			mNtpClient = new SntpClient();
		}
		boolean isSuccessful = mNtpClient.requestTime(hostAddress, 20000);
		MyLog.d(tag, "requestTime:" + isSuccessful);
		if (isSuccessful) {
			long now = mNtpClient.getNtpTime();//now就是获取的时间
			if (isInErrorCycle) {
				if(!isTurnToSuccess){
					isTurnToSuccess = true;
				}
				isInErrorCycle = false;
			}
			return now;
		} else {
			if (!isInErrorCycle) {
				isInErrorCycle = true;
				isTurnToSuccess = false;
				mHandler.sendEmptyMessage(MSG_NTP_SEARCH_FAILED);
			}

		}
		return -1;
	}

  • 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

接下来就是封装好的获取时间方法,获取原理如上,接下来就是原理的代码,可以配合原理来理解

public static class SntpClient {
		private static final String TAG = "SntpClient";

		private static final int REFERENCE_TIME_OFFSET = 16;
		private static final int ORIGINATE_TIME_OFFSET = 24;
		private static final int RECEIVE_TIME_OFFSET = 32;
		private static final int TRANSMIT_TIME_OFFSET = 40;
		private static final int NTP_PACKET_SIZE = 48;

		private static final int NTP_PORT = 123;
		private static final int NTP_MODE_CLIENT = 3;
		private static final int NTP_VERSION = 3;

		// Number of seconds between Jan 1, 1900 and Jan 1, 1970
		// 70 years plus 17 leap days
		private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;

		// system time computed from NTP server response
		private long mNtpTime;

		// value of SystemClock.elapsedRealtime() corresponding to mNtpTime
		private long mNtpTimeReference;

		// round trip time in milliseconds
		private long mRoundTripTime;

		/**
		 * Sends an SNTP request to the given host and processes the response.
		 * 
		 * @param host
		 *            host name of the server.
		 * @param timeout
		 *            network timeout in milliseconds.
		 * @return true if the transaction was successful.
		 */
		public boolean requestTime(String host, int timeout) {
			DatagramSocket socket = null;
			try {
				socket = new DatagramSocket();
				socket.setSoTimeout(timeout);
				InetAddress address = InetAddress.getByName(host);
				byte[] buffer = new byte[NTP_PACKET_SIZE];
				DatagramPacket request = new DatagramPacket(buffer,
						buffer.length, address, NTP_PORT);

				// set mode = 3 (client) and version = 3
				// mode is in low 3 bits of first byte
				// version is in bits 3-5 of first byte
				buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);

				// get current time and write it to the request packet
				long requestTime = System.currentTimeMillis();
				MyLog.d(TAG, "RequestTime:"+new Date(requestTime));
				long requestTicks = SystemClock.elapsedRealtime();
				writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);

				socket.send(request);

				// read the response
				DatagramPacket response = new DatagramPacket(buffer,
						buffer.length);
				socket.receive(response);
				long responseTicks = SystemClock.elapsedRealtime();
				long responseTime = requestTime
						+ (responseTicks - requestTicks);

				// extract the results
				long originateTime = readTimeStamp(buffer,
						ORIGINATE_TIME_OFFSET);
				long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
				long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
				long roundTripTime = responseTicks - requestTicks
						- (transmitTime - receiveTime);
				// receiveTime = originateTime + transit + skew
				// responseTime = transmitTime + transit - skew
				// clockOffset = ((receiveTime - originateTime) + (transmitTime
				// - responseTime))/2
				// = ((originateTime + transit + skew - originateTime) +
				// (transmitTime - (transmitTime + transit - skew)))/2
				// = ((transit + skew) + (transmitTime - transmitTime - transit
				// + skew))/2
				// = (transit + skew - transit + skew)/2
				// = (2 * skew)/2 = skew
				long clockOffset = ((receiveTime -  requestTime) + (transmitTime - System.currentTimeMillis())) / 2;
				// if (false) Log.d(TAG, "round trip: " + roundTripTime +
				// " ms");
				// if (false) Log.d(TAG, "clock offset: " + clockOffset +
				// " ms");

				// save our results - use the times on this side of the network
				// latency
				// (response rather than request time)
				mNtpTime = System.currentTimeMillis() + clockOffset;
//				mNtpTime = transmitTime;
				mNtpTimeReference = responseTicks;
				mRoundTripTime = roundTripTime;
			} catch (Exception e) {
				if (false)
					Log.d(TAG, "request time failed:" + e);
				e.printStackTrace();
				return false;
			} finally {
				if (socket != null) {
					socket.close();
				}
			}

			return true;
		}

		/**
		 * Returns the time computed from the NTP transaction.
		 * 
		 * @return time value computed from NTP server response.
		 */
		public long getNtpTime() {
			return mNtpTime;
		}

		/**
		 * Returns the reference clock value (value of
		 * SystemClock.elapsedRealtime()) corresponding to the NTP time.
		 * 
		 * @return reference clock corresponding to the NTP time.
		 */
		public long getNtpTimeReference() {
			return mNtpTimeReference;
		}

		/**
		 * Returns the round trip time of the NTP transaction
		 * 
		 * @return round trip time in milliseconds.
		 */
		public long getRoundTripTime() {
			return mRoundTripTime;
		}

		/**
		 * Reads an unsigned 32 bit big endian number from the given offset in
		 * the buffer.
		 */
		private long read32(byte[] buffer, int offset) {
			byte b0 = buffer[offset];
			byte b1 = buffer[offset + 1];
			byte b2 = buffer[offset + 2];
			byte b3 = buffer[offset + 3];

			// convert signed bytes to unsigned values
			int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
			int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
			int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
			int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);

			return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8)
					+ (long) i3;
		}

		/**
		 * Reads the NTP time stamp at the given offset in the buffer and
		 * returns it as a system time (milliseconds since January 1, 1970).
		 */
		private long readTimeStamp(byte[] buffer, int offset) {
			long seconds = read32(buffer, offset);
			long fraction = read32(buffer, offset + 4);
			return ((seconds - OFFSET_1900_TO_1970) * 1000)
					+ ((fraction * 1000L) / 0x100000000L);
		}

		/**
		 * Writes system time (milliseconds since January 1, 1970) as an NTP
		 * time stamp at the given offset in the buffer.
		 */
		private void writeTimeStamp(byte[] buffer, int offset, long time) {
			long seconds = time / 1000L;
			long milliseconds = time - seconds * 1000L;
			seconds += OFFSET_1900_TO_1970;

			// write seconds in big endian format
			buffer[offset++] = (byte) (seconds >> 24);
			buffer[offset++] = (byte) (seconds >> 16);
			buffer[offset++] = (byte) (seconds >> 8);
			buffer[offset++] = (byte) (seconds >> 0);

			long fraction = milliseconds * 0x100000000L / 1000L;
			// write fraction in big endian format
			buffer[offset++] = (byte) (fraction >> 24);
			buffer[offset++] = (byte) (fraction >> 16);
			buffer[offset++] = (byte) (fraction >> 8);
			// low order bits should be random data
			buffer[offset++] = (byte) (Math.random() * 255.0);
		}
	}


  • 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
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195

以上就是从ntp服务器上同步系统时间,项目比较完整,有疑问大家提出来,互相学习,到此结束。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/797954
推荐阅读
相关标签
  

闽ICP备14008679号