做開發(fā)有一段時間了,之前一直做服務類的產(chǎn)品,最近公司接手sip話機的項目,里面涉及到修改系統(tǒng)設置的問題,今天就把我在項目中有關NTP服務器同步系統(tǒng)時間遇到的種種問題,在這做一個總結,希望給后面的人一些幫助,本人也是首次接觸這樣的項目,總結的如有不完善的地方,希望大神指點出來。。。。
ntp服務器地址
有很多,網(wǎng)可以搜一下
北京郵電大學
清華大學
北京大學
東南大學
清華大學
清華大學
清華大學
中國[China]
美國[]
韓國[korea]
新加坡[]
由于項目要求,在項目中同步系統(tǒng)時間是每隔多久同步一次系統(tǒng)時間,通過來執(zhí)行任務。通過來發(fā)送處理消息。相關代碼:
/**
*校準時間
* 從ntp服務器中獲取時間
* @param
* ntp服務器域名地址
* @ 如果失敗返回-1,否則返回當前的毫秒數(shù)
*/
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);//設置系統(tǒng)時間
}
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);
}
處理消息,在不同的消息出處理不同的操作,針對項目要求,我在項目中保存了ntp服務器地址和每次隔多久同步一次的時間,可以自行定義。
涉及到的變量:
= “”;
/**
* NTP獲取時間失敗時,每隔30s周期性重新獲取,直至成功,成功后恢復正常計時
*/
final int = 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);//項目中設置的系統(tǒng)默認隔多久同步時間
int time = 168 * 60 * 60 * 1000;
if (!TextUtils.isEmpty(timeStr)
&& TextUtils.isDigitsOnly(timeStr)) {
time = Integer.parseInt(timeStr)*3600*1000;
}
startCalibrateTime(mhostAddress, time);
}
};
};
停止校準時間
public static void stopCalibrateTimer() {
if (mCycleTimer != null) {
mCycleTask.cancel();
mCycleTimer.cancel();
mCycleTimer.purge();
mCycleTask = null;
mCycleTimer = null;
}
}
最重要的部分就是從ntp服務器上獲取時間,這塊很重要。。重要原因就是NTP工作原理簡單分析一下:
下面是NTP工作原理圖,圖片是從我看的資料上截取過來勿噴。
A 與 B 通過網(wǎng)絡相連,都有自己獨立的系統(tǒng)時鐘時間同步系統(tǒng)哪家好,需要通過ntp實現(xiàn)兩個系統(tǒng)時鐘的時間同步
1. B作為服務器,A作為客戶端時間同步系統(tǒng)哪家好,需要網(wǎng)絡使本地時鐘與服務器時鐘同步,假設同步之前, A的時間是10:00:00am, B的時間是11:00:00am
2.NTP報文的 A 和 B 之間單向傳輸時間是1秒,
3.處理報文時間是1秒
A 與 B 工作流程如下
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;
}
接下來就是封裝好的獲取時間方法,獲取原理如上,接下來就是原理的代碼,可以配合原理來理解
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);
}
}
以上就是從ntp服務器上同步系統(tǒng)時間,項目比較完整,有疑問大家提出來,互相學習,到此結束。下一篇就寫手動設置系統(tǒng)時間的功能。