シンプルなNTPクライアント-C言語実装


作成時間:2017-09-11最終変更時間:2017-09-11
個人のレベルが限られているので,文章の中に不足やまちがいがあったら指摘してほしい.
実験環境Linux 2.6.32 gcc version 4.4.6 20120305 (Red Hat 4.4.6-4) (GCC)
引用する
本NTPクライアント実装は、SNTPの実装(SNTPはNTPの簡略化版)を参照したNTPv 3ユニキャストモードに基づいて実現される.
クライアントの開発を完了するには、NTPワークモード、NTPメッセージフォーマットなど、いくつかの知識を用意する必要があります.これらを大体理解してから、開発プロセス全体をよりよく把握することができます.
NTPプロトコル
概要
NTP(Network Time Protocol)は、分散型の時間サーバとクライアントとの間に適用されるネットワーク時間プロトコルであり、クライアントとサーバとの間の時間同期を実現し、ネットワーク内のすべてのデバイスの時間をほぼ一致させる.NTPはUDPの123ポートで動作する.
NTPメッセージ形式
0   2     5     8               16              24              32
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Root Delay                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Root Dispersion                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Reference Identifier                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Reference Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Originate Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Receive Timestamp (64)                      |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Transmit Timestamp (64)                     |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                  Authentication (optional) (64)               |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            NTPv3    
  • Leap Indicator(LI)閏秒インジケータ.2ビットのコードで、その日の最後の分に挿入または削除された閏秒を警告します.値は次のとおりです.
    0          
    1             61 
    2             59 
    3           (     )
    
  • Version Number(VN)バージョン番号.NTPのバージョンを表す3ビットの整数です.
  • Modeモード、これは3ビットの整数で、モードを表し、値定義は以下の通りである:
    0             
    1               
    2               
    3              
    4               
    5             
    6            NTP      
    7                
    
  • Stratumローカルクロックレベル、これは8ビットの符号なし整数であり、ローカルクロックのレベルを表し、その値は以下のように定義される:
    0                   
    1               (       ,       )
    2-255           (  NTP SNTP)
    
  • Pollポーリング間隔は、連続メッセージ間の最大間隔を2に最も近いN乗で表す8ビット符号付き整数である.値が6の場合は2^6=64を表します.
  • Precisionローカルクロック精度.これは、ローカルクロック精度を表す8ビット符号付き整数であり、2に最も近いN乗で表される.
  • Root Delayこれは32ビットの符号付き定点数であり、主な参照ソースの総往復遅延を秒単位で表している.この変数は、時間精度とオフセットに応じて正と負の値にすることができます.
  • Root Dispersionこれは32ビットの符号付き定点数であり、主基準源に対する最大誤差を秒単位で15ビットと16ビットの間で表す.通常、このフィールドに表示される値の範囲は0~数百ミリ秒の
  • です.
  • Reference Identifier特定の参照ソースを識別する32ビット列です.NTPバージョン3またはバージョン4のレベル0またはレベル1サーバの場合、これは4文字のASCII文字列で、左揃えで32ビットに0で埋め込まれます.NTPバージョン3のセカンダリサーバでは、参照元の32ビットIPv 4アドレスです.
  • Reference Timestamp 64ビットタイムスタンプ形式で前回設定または訂正されたローカルクロック時間です.
  • Original Timestamp 64ビットタイムスタンプ形式で表される要求がクライアントを離れる時間である.
  • Receive Timestamp 64ビットタイムスタンプ形式で表されるリクエストがサーバ側に到達した時間です.
  • Transmit Timestamp 64ビットタイムスタンプ形式で表される応答がサーバ側から離れる時間である.
  • Authentication認証情報.

  • NTPタイムスタンプ
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                         Integer Part                          |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                         Fraction Part                         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
                                NTP     
    

    NTPタイムスタンプは、1970-01-01からの秒数(秒単位)を使用し、整数部と小数部に分けられます.NTPタイムスタンプ整数部は、ICMPタイムスタンプメッセージで使用されるタイムスタンプフォーマット整数部と一致しますが、小数部は異なります.
    64ビットのNTPタイムスタンプでは、上位32ビットが整数部、下位32ビットが小数部となり、次のように変換されます.
    frac * 1e6 / (2<<32)    
    = frac /  4294.967296
    

    以上の変換規則により得られるNTPタイムスタンプの最小精度は、1/4294.967296=0.2328307 e-9であり、約0.232ナノ秒である.
    32ビットのNTPタイムスタンプでは、上位16ビットが整数部、下位16ビットが小数部となり、上記と同様に変換されます.
    frac * 1e6 / (2<<16)
    = frac * 15.2587890625
    

    NTPクライアント操作
    往復遅延とローカルクロックオフセットの計算について
    サーバに対する往復遅延dおよびローカルクロックオフセットtを計算するために、クライアントは、クライアントクロックに基づいて要求中の送信タイムスタンプを設定する.サーバは、このフィールドを応答中の開始タイムスタンプにコピーし、サーバクロックに従って受信タイムスタンプと転送タイムスタンプを設定します.
    サーバ応答が受信されると、クライアントは、NTPタイムスタンプフォーマットのクロックに従って、宛先タイムスタンプ変数を到着時間として決定する.次の4つのタイムスタンプをまとめます.
    Originate Timestamp     T1                  
    Receive Timestamp       T2                  
    Transmit Timestamp      T3                  
    Destination Timestamp   T4                  
    

    往復遅延dとローカルクロックオフセットtは、
    d = (T4 - T1) - (T2 - T3)
    t = ((T2 - T1) + (T3 - T4)) / 2
    

    実装(C言語)
    インプリメンテーション分析
    NTPメッセージの操作を容易にするためにntphdr構造体をカスタマイズし、以下のようにします.
    
    /* 32     */
    struct s_fixedpt {
        uint16_t    intpart;
        uint16_t    fracpart;
    };
    
    /* 64     */
    struct l_fixedpt {
        uint32_t    intpart;
        uint32_t    fracpart;
    };
    
    struct ntphdr {
    #if __BYTE_ORDER == __BID_ENDIAN
        unsigned int        ntp_li:2;
        unsigned int        ntp_vn:3;
        unsigned int        ntp_mode:3;
    #endif
    #if __BYTE_ORDER == __LITTLE_ENDIAN
        unsigned int        ntp_mode:3;
        unsigned int        ntp_vn:3;
        unsigned int        ntp_li:2;
    #endif
        uint8_t             ntp_stratum;
        uint8_t             ntp_poll;
        int8_t              ntp_precision;
        struct s_fixedpt    ntp_rtdelay;
        struct s_fixedpt    ntp_rtdispersion;
        uint32_t            ntp_refid;
        struct l_fixedpt    ntp_refts;
        struct l_fixedpt    ntp_orits;
        struct l_fixedpt    ntp_recvts;
        struct l_fixedpt    ntp_transts;
    };
    

    ntphdr構造体定義は比較的特殊であり,ビットごとにフィールドが定義されているため,バイト順の問題を解決する必要がある.マクロを使用可能BYTE_ORDER(ヘッダファイル)でバイト順を判断します.
    NTPタイムスタンプの操作を容易にするために、以下のマクロが定義されています.
    
    /*
     *   1900-01-01 1970-01-01      
     */
    #define JAN_1970            0x83aa7e80
    
    
    /*
     *    64 NTP   ,      32 
     *      NTP_CONV_FRAC32(x) x   NTP          ;
     *      NTP_REVE_FRAC32(x)   , NTP        x      。
     */
    #define NTP_CONV_FRAC32(x)  (uint64_t) ((x) * ((uint64_t)1<<32))    
    #define NTP_REVE_FRAC32(x)  ((double) ((double) (x) / ((uint64_t)1<<32)))   
    
    
    /*
     *    32 NTP   ,      16 
     *      NTP_CONV_FRAC16(x) x   NTP          ;
     *      NTP_REVE_FRAC16(x)   , NTP        x      。
     */
    #define NTP_CONV_FRAC16(x)  (uint32_t) ((x) * ((uint32_t)1<<16))    
    #define NTP_REVE_FRAC16(x)  ((double)((double) (x) / ((uint32_t)1<<16)))    
    
    
    /*
     *  timeval   tv_usec   NTP         
     */
    #define USEC2FRAC(x)        ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 )) 
    #define FRAC2USEC(x)        ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 )) 
    
    
    /*
     *   l_fixedpt  (NTP64    )    1970-01-01      。
     *   l_fixedpt            。
     */
    #define NTP_LFIXED2DOUBLE(x)    ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))   
    

    上記のマクロ定義は簡略化できるが、その中の理由をより理解するために、ここでは元のプロセスを簡略化処理しないで保持する.
    関数本体:
    
    int main(int argc, char *argv[])
    {
        char buf[BUFSIZE];
        size_t nbytes;
        int sockfd, maxfd1;
        struct sockaddr_in servaddr;
        fd_set readfds;
        struct timeval timeout, recvtv, tv;
        double offset;
    
        if (argc != 2) {
            usage();
            exit(-1);
        }
    
    
        //            
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(NTP_PORT);
        servaddr.sin_addr.s_addr = inet_host(argv[1]);
    
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            perror("socket error");
            exit(-1);
        }
    
        if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) {
            perror("connect error");
            exit(-1);
        }
    
        //     NTP    
        nbytes = BUFSIZE;
        if (get_ntp_packet(buf, &nbytes) != 0) {
            fprintf(stderr, "construct ntp request error 
    "
    ); exit(-1); } send(sockfd, buf, nbytes, 0); FD_ZERO(&readfds); FD_SET(sockfd, &readfds); maxfd1 = sockfd + 1; // select timeout.tv_sec = TIMEOUT; timeout.tv_usec = 0; if (select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) { if (FD_ISSET(sockfd, &readfds)) { if ((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0) { perror("recv error"); exit(-1); } // gettimeofday(&recvtv, NULL); offset = get_offset((struct ntphdr *) buf, &recvtv); // gettimeofday(&tv, NULL); tv.tv_sec += (int) offset; tv.tv_usec += offset - (int) offset; if (settimeofday(&tv, NULL) != 0) { perror("settimeofday error"); exit(-1); } printf("%s
    "
    , ctime((time_t *) &tv.tv_sec)); } } // close(sockfd); return 0; }

    NTP要求メッセージ構築関数:
    
    /*
     *      NTP    
     *      c  buf    NTP      ;
     *      size  -    ,         ,    NTP    
     */
    int get_ntp_packet(void *buf, size_t *size)
    {
        struct ntphdr *ntp;
        struct timeval tv;
    
    
        if (!size || *sizereturn -1;
    
        memset(buf, 0, *size);
    
        ntp = (struct ntphdr *) buf;
        ntp->ntp_li = NTP_LI;
        ntp->ntp_vn = NTP_VN;
        ntp->ntp_mode = NTP_MODE;
        ntp->ntp_stratum = NTP_STRATUM;
        ntp->ntp_poll = NTP_POLL;
        ntp->ntp_precision = NTP_PRECISION;
    
        gettimeofday(&tv, NULL);
        ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
        ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
    
        *size = NTP_HLEN;
    
        return 0;
    }
    

    往復遅延および時間オフセットの計算関数:
    
    /*
     *                  
     *        ntp             ;
     *        recvtv              ;
     */
    double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv)
    {
        double t1, t2, t3, t4;
    
        t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
        t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
        t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
        t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
    
        return (t4 - t1) - (t3 - t2);
    }
    
    
    /*
     *                 
     *        ntp             ;
     *        recvtv              ;
     */
    double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv)
    {
        double t1, t2, t3, t4;
    
        t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
        t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
        t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
        t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
    
        return ((t2 - t1) + (t3 - t4)) / 2;
    }
    

    フルソース
    
    /* ntpclient.c */
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define VERSION_3           3
    #define VERSION_4           4
    
    #define MODE_CLIENT         3
    #define MODE_SERVER         4
    
    
    #define NTP_LI              0
    #define NTP_VN              VERSION_3   
    #define NTP_MODE            MODE_CLIENT
    #define NTP_STRATUM         0
    #define NTP_POLL            4
    #define NTP_PRECISION       -6
    
    #define NTP_HLEN            48
    
    #define NTP_PORT            123
    #define NTP_SERVER          "182.92.12.11"
    
    #define TIMEOUT             10
    
    #define BUFSIZE             1500
    
    #define JAN_1970            0x83aa7e80
    
    #define NTP_CONV_FRAC32(x)  (uint64_t) ((x) * ((uint64_t)1<<32))    
    #define NTP_REVE_FRAC32(x)  ((double) ((double) (x) / ((uint64_t)1<<32)))   
    
    #define NTP_CONV_FRAC16(x)  (uint32_t) ((x) * ((uint32_t)1<<16))    
    #define NTP_REVE_FRAC16(x)  ((double)((double) (x) / ((uint32_t)1<<16)))    
    
    
    #define USEC2FRAC(x)        ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 )) 
    #define FRAC2USEC(x)        ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 )) 
    
    
    #define NTP_LFIXED2DOUBLE(x)    ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))   
    
    
    struct s_fixedpt {
        uint16_t    intpart;
        uint16_t    fracpart;
    };
    
    struct l_fixedpt {
        uint32_t    intpart;
        uint32_t    fracpart;
    };
    
    
    struct ntphdr {
    #if __BYTE_ORDER == __BID_ENDIAN
        unsigned int    ntp_li:2;
        unsigned int    ntp_vn:3;
        unsigned int    ntp_mode:3;
    #endif
    #if __BYTE_ORDER == __LITTLE_ENDIAN
        unsigned int    ntp_mode:3;
        unsigned int    ntp_vn:3;
        unsigned int    ntp_li:2;
    #endif
        uint8_t         ntp_stratum;
        uint8_t         ntp_poll;
        int8_t          ntp_precision;
        struct s_fixedpt    ntp_rtdelay;
        struct s_fixedpt    ntp_rtdispersion;
        uint32_t            ntp_refid;
        struct l_fixedpt    ntp_refts;
        struct l_fixedpt    ntp_orits;
        struct l_fixedpt    ntp_recvts;
        struct l_fixedpt    ntp_transts;
    };
    
    
    in_addr_t inet_host(const char *host)
    {
        in_addr_t saddr;
        struct hostent *hostent;
    
        if ((saddr = inet_addr(host)) == INADDR_NONE) {
            if ((hostent = gethostbyname(host)) == NULL)
                return INADDR_NONE;
    
            memmove(&saddr, hostent->h_addr, hostent->h_length);
        }
    
        return saddr;
    }
    
    
    int get_ntp_packet(void *buf, size_t *size)
    {
        struct ntphdr *ntp;
        struct timeval tv;
    
    
        if (!size || *sizereturn -1;
    
        memset(buf, 0, *size);
    
        ntp = (struct ntphdr *) buf;
        ntp->ntp_li = NTP_LI;
        ntp->ntp_vn = NTP_VN;
        ntp->ntp_mode = NTP_MODE;
        ntp->ntp_stratum = NTP_STRATUM;
        ntp->ntp_poll = NTP_POLL;
        ntp->ntp_precision = NTP_PRECISION;
    
        gettimeofday(&tv, NULL);
        ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
        ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
    
        *size = NTP_HLEN;
    
        return 0;
    }
    
    
    void print_ntp(struct ntphdr *ntp)
    {
        time_t time;
    
        printf("LI:\t%d 
    "
    , ntp->ntp_li); printf("VN:\t%d
    "
    , ntp->ntp_vn); printf("Mode:\t%d
    "
    , ntp->ntp_mode); printf("Stratum:\t%d
    "
    , ntp->ntp_stratum); printf("Poll:\t%d
    "
    , ntp->ntp_poll); printf("precision:\t%d
    "
    , ntp->ntp_precision); printf("Route delay:\t %lf
    "
    , ntohs(ntp->ntp_rtdelay.intpart) + NTP_REVE_FRAC16(ntohs(ntp->ntp_rtdelay.fracpart))); printf("Route Dispersion:\t%lf
    "
    , ntohs(ntp->ntp_rtdispersion.intpart) + NTP_REVE_FRAC16(ntohs(ntp->ntp_rtdispersion.fracpart))); printf("Referencd ID:\t %d
    "
    , ntohl(ntp->ntp_refid)); time = ntohl(ntp->ntp_refts.intpart) - JAN_1970; printf("Reference:\t%d %ld %s
    "
    , ntohl(ntp->ntp_refts.intpart) - JAN_1970, FRAC2USEC(ntohl(ntp->ntp_refts.fracpart)), ctime(&time)); time = ntohl(ntp->ntp_orits.intpart) - JAN_1970; printf("Originate:\t%d %d frac=%ld (%s)
    "
    , ntohl(ntp->ntp_orits.intpart) - JAN_1970, FRAC2USEC(ntohl(ntp->ntp_orits.fracpart)), ntohl(ntp->ntp_orits.fracpart), ctime(&time) ); time = ntohl(ntp->ntp_recvts.intpart) - JAN_1970; printf("Receive:\t%d %d (%s)
    "
    , ntohl(ntp->ntp_recvts.intpart) - JAN_1970, FRAC2USEC(ntohl(ntp->ntp_recvts.fracpart)), ctime(&time) ); time = ntohl(ntp->ntp_transts.intpart) - JAN_1970; printf("Transmit:\t%d %d (%s)
    "
    , ntohl(ntp->ntp_transts.intpart) - JAN_1970, FRAC2USEC(ntohl(ntp->ntp_transts.fracpart)), ctime(&time) ); } double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv) { double t1, t2, t3, t4; t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits); t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts); t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts); t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0; return (t4 - t1) - (t3 - t2); } double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv) { double t1, t2, t3, t4; t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits); t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts); t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts); t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0; return ((t2 - t1) + (t3 - t4)) / 2; } void usage(void) { fprintf(stderr, "Usage : ntpclient" " destination" "
    "
    ); } int main(int argc, char *argv[]) { char buf[BUFSIZE]; size_t nbytes; int sockfd, maxfd1; struct sockaddr_in servaddr; fd_set readfds; struct timeval timeout, recvtv, tv; double offset; if (argc != 2) { usage(); exit(-1); } servaddr.sin_family = AF_INET; servaddr.sin_port = htons(NTP_PORT); servaddr.sin_addr.s_addr = inet_host(argv[1]); if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket error"); exit(-1); } if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) { perror("connect error"); exit(-1); } nbytes = BUFSIZE; if (get_ntp_packet(buf, &nbytes) != 0) { fprintf(stderr, "construct ntp request error
    "
    ); exit(-1); } send(sockfd, buf, nbytes, 0); FD_ZERO(&readfds); FD_SET(sockfd, &readfds); maxfd1 = sockfd + 1; timeout.tv_sec = TIMEOUT; timeout.tv_usec = 0; if (select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) { if (FD_ISSET(sockfd, &readfds)) { if ((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0) { perror("recv error"); exit(-1); } gettimeofday(&recvtv, NULL); offset = get_offset((struct ntphdr *) buf, &recvtv); gettimeofday(&tv, NULL); tv.tv_sec += (int) offset; tv.tv_usec += offset - (int) offset; if (settimeofday(&tv, NULL) != 0) { perror("settimeofday error"); exit(-1); } printf("%s
    "
    , ctime((time_t *) &tv.tv_sec)); } } close(sockfd); return 0; }

    まとめ
    全体の開発過程で大小の問題に遭遇したので、主な問題について以下の総括を行う.
  • NTPタイムスタンプは1900-01-01に対する時間であり、小数部変換は一般タイムスタンプ変換とは異なり、具体的にはfrac*1 e 6/(2<
  • )である.
  • ビット単位で構造体のメンバーを定義するときは、マクロ_を介してバイト順の問題に注意してください.BYTE_ORDERで判断します(ヘッダファイル).
  • リファレンス
    『UNIXネットワーク高度プログラミングボリューム1:ソケットネットワークAPI』RFC 1305 Network Time Protocol(Version 3)Specification,Implementation and Analysis RFC 4330 Simple Network Time Protocol(SNTP)Version 4 for IPv 4,IPv 6 and OSI