Android NTP V4鉴权修改

1、NTP同步时间原理

NTP协议是对网络内所有具有时钟的设备进行时钟同步,使网络内所有设备的时钟保持一致。

系统时钟同步的工作过程如下:

Device A发送一个NTP报文给Device B,该报文带有它离开Device A时的时间戳,该时间戳为10:00:00am(T1)。

当此NTP报文到达Device B时,Device B加上自己的时间戳,该时间戳为11:00:01am(T2)。

当此NTP报文离开Device B时,Device B再加上自己的时间戳,该时间戳为11:00:02am(T3)。

当Device A接收到该响应报文时,Device A的本地时间为10:00:03am(T4)。

至此,Device A已经拥有足够的信息来计算两个重要的参数:

NTP报文的往返时延Delay=(T4-T1)-(T3-T2)=2秒。

Device A相对Device B的时间差offset=((T2-T1)+(T3-T4))/2=1小时。

这样,Device A就能够根据这些信息来设定自己的时钟,使之与Device B的时钟同步。
对于android平台,framework中有关于NTP客户端实现的代码SntpClient,使用其requestTime函数,同步网络时间。

2、android中同步时间代码流程

android代码中,NetworkTimeUpdateService用于同步网络时间。
(1)简单介绍下NetworkTimeUpdateService的代码流程:
onPollNetworkTime(msg.what);是其时间同步的流程所在,可以看到有三种情况会调用到该函数。

    /** Handler to do the network accesses on */
    private class MyHandler extends Handler {

        public MyHandler(Looper l) {
            super(l);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case EVENT_AUTO_TIME_CHANGED:
                case EVENT_POLL_NETWORK_TIME:
                case EVENT_NETWORK_CONNECTED:
                    onPollNetworkTime(msg.what);
                    break;
                default :
                    break;
            }
        }
    }

进入onPollNetworkTime进行分析,主要看更新时间的条件:当网络连接,或者是 mTime.getCacheAge() >= mPollingIntervalMs时,执行mTime.forceRefresh()调用到NtpTrustedTime的requestTime函数去重新同步网络时间。
默认系统设置mPollingIntervalMs为24小时。而设置系统时间的流程则是调用SystemClock.setCurrentTimeMillis(ntp);,前提是设同步的时间和当前时间的时间差在mTimeErrorThresholdMs以内,默认为5s。


    private void onPollNetworkTime(int event) {
......
        final long currentTime = System.currentTimeMillis();
        // Get the NTP time
        if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
                || event == EVENT_AUTO_TIME_CHANGED || event == EVENT_NETWORK_CONNECTED) {
            // force refresh NTP cache when outdated
            if (mTime.getCacheAge() >= mPollingIntervalMs || event == EVENT_NETWORK_CONNECTED) {
                mTime.forceRefresh();
            }

            // only update when NTP time is fresh
            if (mTime.getCacheAge() < mPollingIntervalMs) {
                final long ntp = mTime.currentTimeMillis();
                mTryAgainCounter = 0;
                // If the clock is more than N seconds off or this is the first time it's been
                // fetched since boot, set the current time.
                if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
                        || mLastNtpFetchTime == NOT_SET) {
                    // Set the system time
                    if (DBG && mLastNtpFetchTime == NOT_SET
                            && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
                        Log.d(TAG, "For initial setup, rtc = " + currentTime);
                    }
                    if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
                    // Make sure we don't overflow, since it's going to be converted to an int
                    if (ntp / 1000 < Integer.MAX_VALUE) {
                        SystemClock.setCurrentTimeMillis(ntp);
                    }
                } else {
                    if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
                }
                mLastNtpFetchTime = SystemClock.elapsedRealtime();
            } else {
                // Try again shortly
                mTryAgainCounter++;
                if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
                    resetAlarm(mPollingIntervalShorterMs);
                } else {
                    // Try much later
                    mTryAgainCounter = 0;
                    resetAlarm(mPollingIntervalMs);
                }
                return;
            }
        }
        resetAlarm(mPollingIntervalMs);
    }

3、鉴权模式修改

问题是这样的,最近有需求需要使用特定的NTP服务器,其服务器进行了鉴权加密处理,我使用Android原生的流程去进行时间同步,却一直请求超时,而使用Linux的ntpdate可以正确同步到时间。
下面是NTP报文格式:
这里写图片描述
其中最后两个字段,则是用于认证用的,分别是KEY ID和信息认证码。所以对于Android原生代码,则需要实现NTP V4版本的才能支持鉴权。从包的格式来看,需要多发KEY ID和信息认证码即可。

修改也不难,首先会从服务器端得到鉴权的KEY ID和鉴权信息。代码部分只需要将android里的NTP版本号改为4,在原本的包后加发一个KEY ID的4个字节数据,最后加发鉴权信息和包数据一起加密出来16个字节(MD5等,这个也得从服务器端得知)。

这样就试了一下,可以从服务器端同步到时间了,具体代码就不贴了,流程基本就是这样。

4、偶发同步到错误时间

但是后面发现一个bug,在同步的时候,会有概率(大概1/10)同步到错误的时间,看了一下包,其中 Originate Timestamp(NTP请求报文离开发送端时发送端的本地时间)和我请求时发出去的Transmit Timestamp(应答报文离开应答者时应答者的本地时间)不对应,这个时间就是我们在上面介绍的时间戳T1。但是Linux服务器使用ntpdate抓的包有时也这样,同步的时间是对的。。。

我对于SntpClient.java中的一处地方不是很理解,正常来说,这两处时间,应该是一样的才对,但是为什么SntpClient.java中不使用自己本地发出去的时间来计算,而是用了服务器返回的时间戳来计算?这里我不是很明白,希望有高手解答一下。

所以当我发现服务器返回的时间戳和我发出去的差别很大(针对我调试的这个服务器我觉得超过30分钟就是服务器回错了,当然这种方法很挫),我就直接用我自己发出去的时间来计算。
这里写图片描述

这下子就没出现同步时间错误的情况了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值