シンプルなNTPクライアント-C言語実装
31375 ワード
作成時間: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メッセージ形式 Leap Indicator(LI)閏秒インジケータ.2ビットのコードで、その日の最後の分に挿入または削除された閏秒を警告します.値は次のとおりです. Version Number(VN)バージョン番号.NTPのバージョンを表す3ビットの整数です. Modeモード、これは3ビットの整数で、モードを表し、値定義は以下の通りである: Stratumローカルクロックレベル、これは8ビットの符号なし整数であり、ローカルクロックのレベルを表し、その値は以下のように定義される: 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タイムスタンプ
NTPタイムスタンプは、1970-01-01からの秒数(秒単位)を使用し、整数部と小数部に分けられます.NTPタイムスタンプ整数部は、ICMPタイムスタンプメッセージで使用されるタイムスタンプフォーマット整数部と一致しますが、小数部は異なります.
64ビットのNTPタイムスタンプでは、上位32ビットが整数部、下位32ビットが小数部となり、次のように変換されます.
以上の変換規則により得られるNTPタイムスタンプの最小精度は、1/4294.967296=0.2328307 e-9であり、約0.232ナノ秒である.
32ビットのNTPタイムスタンプでは、上位16ビットが整数部、下位16ビットが小数部となり、上記と同様に変換されます.
NTPクライアント操作
往復遅延とローカルクロックオフセットの計算について
サーバに対する往復遅延dおよびローカルクロックオフセットtを計算するために、クライアントは、クライアントクロックに基づいて要求中の送信タイムスタンプを設定する.サーバは、このフィールドを応答中の開始タイムスタンプにコピーし、サーバクロックに従って受信タイムスタンプと転送タイムスタンプを設定します.
サーバ応答が受信されると、クライアントは、NTPタイムスタンプフォーマットのクロックに従って、宛先タイムスタンプ変数を到着時間として決定する.次の4つのタイムスタンプをまとめます.
往復遅延dとローカルクロックオフセットtは、
実装(C言語)
インプリメンテーション分析
NTPメッセージの操作を容易にするためにntphdr構造体をカスタマイズし、以下のようにします.
ntphdr構造体定義は比較的特殊であり,ビットごとにフィールドが定義されているため,バイト順の問題を解決する必要がある.マクロを使用可能BYTE_ORDER(ヘッダファイル)でバイト順を判断します.
NTPタイムスタンプの操作を容易にするために、以下のマクロが定義されています.
上記のマクロ定義は簡略化できるが、その中の理由をより理解するために、ここでは元のプロセスを簡略化処理しないで保持する.
関数本体:
NTP要求メッセージ構築関数:
往復遅延および時間オフセットの計算関数:
フルソース
まとめ
全体の開発過程で大小の問題に遭遇したので、主な問題について以下の総括を行う. 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
個人のレベルが限られているので,文章の中に不足やまちがいがあったら指摘してほしい.
実験環境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
0
1 61
2 59
3 ( )
0
1
2
3
4
5
6 NTP
7
0
1 ( , )
2-255 ( NTP SNTP)
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;
}
まとめ
全体の開発過程で大小の問題に遭遇したので、主な問題について以下の総括を行う.
『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