close

 

如上圖, 在 NTP 的架構之下, 有一個 NTP Server, 這個 NTP Server 必須維持精準的時間,目前本校所採用的時間是接收衛星訊號(Global Positioning System, GPS)的時間, 它的準確度可以逹到1ms以內, 而這時間提供其他的 NTP Client 或個人電腦系統時間作為校正的一個標準, 當所有的 NTP Client 向NTP Server 同步時, 時間的問題就完美地解決了. 接著我們對圖一的代號作一些說明如下: 

 

T1-T0: 為Client 端向 NTP server要求提供對時服務的時間, 包括網路傳輸時間. 
T2-T1: NTP server 處理對時服務的作業時間. 
T3-T2: 為NTP server回覆 Client端對時服務的時間資料, 包括網路傳輸時間. 
T3-T0: 總共花費的時間. 

Client 端所獲得之時間與 NTP server時間約延遲(delay) T3-T2 至 T3-T1之間. 

 

 

簡單的網路校時程式 ntpc.c

 

程式碼很簡單, CROSS 編譯也非常簡單, 沒有什麼複雜的 dependency. 因此我不打算詳細解說. 因為非常簡單, 所也與非常適合 embedded system.  程式碼如下: 
 

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <netinet/tcp.h>

#define TIMEOUT         10              // seconds
#define JAN_1970        0x83aa7e80      // 2208988800 1970 - 1900 in seconds
#define NTP_PORT        (123)
typedef uint32_t __u32;

/* How to multiply by 4294.967296 quickly (and not quite exactly)
 * without using floating point or greater than 32-bit integers.
 * If you want to fix the last 12 microseconds of error, add in
 * (2911*(x))>>28)
 */
#define NTPFRAC(x) ( 4294*(x) + ( (1981*(x))>>11 ) )

/* The reverse of the above, needed if we want to set our microsecond
 * clock (via settimeofday) based on the incoming time in NTP format.
 * Basically exact.
 */
#define USEC(x) ( ( (x) >> 12 ) - 759 * ( ( ( (x) >> 10 ) + 32768 ) >> 16 ) )

// you'd better be root, or ntpclient will crash!
int settime(uint32_t *data) {
    struct timeval tv_set;
    unsigned int coarse;
    unsigned int fine;

#define Data(i) ntohl(((uint32_t *)data)[i])
    coarse = Data(10);
    fine   = Data(11);
#undef Data

    // it would be even better to subtract half the slop
    tv_set.tv_sec  = coarse - JAN_1970;
    // divide xmttime.fine by 4294.967296
    tv_set.tv_usec = USEC(fine);
    if (settimeofday(&tv_set,NULL)<0)
        return -1;
    return 0;
}

void send_packet(int usd) {
    __u32 data[12];
    struct timeval now;
#define LI 0
#define VN 3
#define MODE 3
#define STRATUM 0
#define POLL 4
#define PREC -6

    if (sizeof(data) != 48) {
        fprintf(stderr,"size error\n");
        return;
    }
    bzero((char *) data,sizeof(data));
    data[0] = htonl (
        ( LI << 30 ) | ( VN << 27 ) | ( MODE << 24 ) |
        ( STRATUM << 16) | ( POLL << 8 ) | ( PREC & 0xff ) );
    data[1] = htonl(1<<16);  /* Root Delay (seconds) */
    data[2] = htonl(1<<16);  /* Root Dispersion (seconds) */
    gettimeofday(&now,NULL);
    data[10] = htonl(now.tv_sec + JAN_1970); // Transmit Timestamp coarse
    data[11] = htonl(NTPFRAC(now.tv_usec));  // Transmit Timestamp fine
    send(usd,data,48,0);
}

int ntpHandle(int usd) {
    static uint32_t incoming_word[20];
    #define incoming ((char *) incoming_word)
    #define sizeof_incoming (sizeof(incoming_word)*sizeof(uint32_t))

    fd_set fds;
    struct sockaddr sa_xmit;
    int sa_xmit_len=sizeof(sa_xmit);
    int i, pack_len;
    struct timeval to;

    send_packet(usd);
    to.tv_sec=TIMEOUT;
    to.tv_usec=0;
    for (;;) {
        FD_ZERO(&fds);
        FD_SET(usd,&fds);
        i=select(usd+1,&fds,NULL,NULL,&to);  // Wait on read or error
        if ((i!=1)||(!FD_ISSET(usd,&fds))) {
            if (errno==EINTR) {
                fprintf(stderr,"interrupt by signal\n");
                continue;
            }
            if (i<0) {
                fprintf(stderr,"select error\n");
                return -1;
            }
            fprintf(stderr,"timeout\n");
            return -1;
        }
        pack_len=recvfrom(usd,incoming,sizeof_incoming,0,&sa_xmit,&sa_xmit_len);
        if (pack_len<0) {
            fprintf(stderr,"recvfrom error\n");
            return -1;
        //} else if (pack_len >0 && (unsigned)pack_len)
        } else if (pack_len > 0) {
            fprintf(stderr,"settime\n");
            return settime(incoming_word);
        }
        return -1;
    }
    return -1;
}

int ntpclient(char *hostname) {
    struct hostent *ntpserver;
    struct sockaddr_in sa_dest;
    struct sockaddr_in sa_rcvr;
    int usd;  // socket
    int err;

    // Startup sequence
    if ((usd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1) {
        fprintf(stderr,"could not open the sockey\n");
        return -1;
    }
    // setup receive
    bzero((char *) &sa_rcvr, sizeof(sa_rcvr));
    sa_rcvr.sin_family=AF_INET;
    sa_rcvr.sin_addr.s_addr=htonl(INADDR_ANY);
    //sa_rcvr.sin_port=htons(0);  // udp_local_port, default of 0 means kernel chooses
    sa_rcvr.sin_port=htons(5301);  // udp_local_port, default of 0 means kernel chooses
    if(bind(usd,(struct sockaddr *) &sa_rcvr,sizeof(sa_rcvr)) == -1) {
        fprintf(stderr,"could not bind to udp port\n");
        close(usd);
        return -1;
    }
    listen(usd, 3);

    // setup transmit
    // get host name
    bzero((char *) &sa_dest, sizeof(sa_dest));
    ntpserver=gethostbyname(hostname);
    if (ntpserver == NULL) {
        fprintf(stderr,"could not resolve hostname %s\n", hostname);
        close(usd);
        return -1;
    }
    if (ntpserver->h_length != 4) {
        fprintf(stderr,"oops %d\n",ntpserver->h_length);
        close(usd);
        return -1;
    }
    memcpy(&(sa_dest.sin_addr.s_addr),ntpserver->h_addr_list[0],4);
    // connect ntp server
    sa_dest.sin_family=AF_INET;
    sa_dest.sin_port=htons(NTP_PORT);
    if (connect(usd,(struct sockaddr *)&sa_dest,sizeof(sa_dest))==-1) {
        fprintf(stderr,"could not connect to udp port %d\n", NTP_PORT);
        close(usd);
        return -1;
    }
    // ntp handle
    err = ntpHandle(usd);
    // exit
    close(usd);
    return err;
}

int main(int argc, char *argv[]) {
    if(argc<2) {
        fprintf(stderr, "Usage: ntpc tick.stdtime.gov.tw\n");
        exit(-1);
    }
    return ntpclient(argv[1]);
}

 

在 Android 下設定 crontab 
 

Android 下可以使用 crond, 條件是 busybox 必須編譯支援 crond; 同樣地 telnetd 以及 ftpd, tcpsvd 也是如此. BUSYBOX 如何編譯? 就請讀者自行實做囉. 
 

...
chmod 777 /data/ftp
echo 'root::0:0:root:/data/ftp:/system/bin/sh' > /etc/passwd
echo 'shell::2000:0:shell:/data/ftp:/system/bin/sh' >> /etc/passwd
...
cd /system/bin

### CROND
[ ! -L crond ] && ln -s busybox crond
if [ ! -d /system/crontabs ]; then
    echo ""
    mkdir /system/crontabs ; chmod 777 /system/crontabs ; touch /system/crontabs/root ; chmod 666 /system/crontabs/root
    echo "30 2 * * *    /system/bin/ntpc tick.stdtime.gov.tw > /dev/null 2>&1" > /system/crontabs/root
fi

### TELNETD
[ -L telnetd ] && telnetd -l /system/bin/sh &

### FTPD
[ ! -d /data/ftp ] && mkdir /data/ftp
chmod 777 /data/ftp
ps | grep tcpsvd > /dev/null
if [ $? -ne 0  ]; then
    echo ""
    if [ -L tcpsvd -a -L ftpd  ]; then
        echo ""
        tcpsvd -u root -vE 0.0.0.0 21 ftpd -w /data/ftp &
    fi
fi
cd ..
...

 

/System/crontabs/root


echo "30 2 * * * /system/bin/ntpc tick.stdtime.gov.tw > /dev/null 2>&1" > /system/crontabs/root

 

30 2 * * *    /system/bin/ntpc tick.stdtime.gov.tw > /dev/null 2>&1

這樣 每天 2:30 ntpc 會向 tick.stdtime.gov.tw 要求正確時間來校時. 提供讀者參考. 
 

 

 


 

 

 

 

 

 

 

 

 

z.png
如果你覺得這篇文章對你有幫助, 請於留言板留個言, 以茲鼓勵. 謝謝! Email: jasonc@mail2000.com.tw .  

 

 

 

arrow
arrow
    創作者介紹
    創作者 Lexra 的頭像
    Lexra

    Lexra Pixnet

    Lexra 發表在 痞客邦 留言(0) 人氣()