NTP 타임서버 시간 확인 및 시간 동기화 예제 소스

본 글은 인터넷 타임 서버로부터 시간을 받아오는 예제를 싣고 있다.


linux에는 타임 서버로부터 시간을 받아와 시간을 설정해주는 rdate라는 application이 있다. 하지만, 여러 플랫폼에서 개발하다 보면 이런 유용한 application을 사용하지 못하는 경우가 종종 있다. 이런 경우에는 아래 코드와 같이 직접 NTP 타임 서버에 접속해 시간을 받아오면 된다. 아래 예제는 간단히 서버에서 시간을 받아오는 코드이고, 인터넷 연결 상태 등을 고려한 예외처리 코드는 추가해야 한다.


#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>


typedef struct
{
uint8_t li_vn_mode; // 8 bits. li, vn, and mode.
uint8_t stratum; // 8 bits. Stratum level of the local clock.
uint8_t poll; // 8 bits. Maximum interval between successive messages.
uint8_t precision; // 8 bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
uint32_t txTm_s; // 32 bits and the most important field the client cares about. Tran
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
}ntp_packet;


void delay(long ms)
{
struct timeval stTimeVal;
stTimeVal.tv_sec = ms / 1000;
ms -= (stTimeVal.tv_sec * 1000);
stTimeVal.tv_usec = ms * 1000;

select(0, 0, 0, 0, &stTimeVal);
}


time_t query_time(char *addr)
{
int retry_cnt = 5;
time_t nettime = 0;
ntp_packet packet = { 0, };
int sockfd = 0;
struct sockaddr_in addr_in;
struct hostent *host_entry;

host_entry = gethostbyname(addr);
if (!host_entry)
{
printf("gethostbyname fail\n");
return 0;
}

sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd <= 0)
{
printf("socket fail\n");
return 0;
}

memset((void *)&addr_in, 0x00, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(123);
memcpy((void *)(&addr_in.sin_addr), (void *)(host_entry->h_addr), sizeof(addr_in.sin_addr));

if (connect(sockfd, (struct sockaddr *)&addr_in, sizeof(addr_in)) == -1)
{
printf("connect fail\n");
close(sockfd);
return 0;
}

memset(&packet, 0, sizeof(ntp_packet));
packet.li_vn_mode = 0x1b;

delay(100);

if (write(sockfd, (char*)&packet, sizeof(ntp_packet)) <= 0)
{
printf("write fail\n");
close(sockfd);
return 0;
}

do
{
delay(100);
if (read(sockfd, (char*)&packet, sizeof(ntp_packet)) > 0)
{
unsigned long long NTP_TIMESTAMP_DELTA = 2208988800ull;
packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds.
packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second

nettime = (time_t)(packet.txTm_s - NTP_TIMESTAMP_DELTA);
printf("[%s] Time: %s", addr, ctime((const time_t*)&nettime));
break;
}

retry_cnt--;

} while (retry_cnt>0);

close(sockfd);

return nettime;

}


위 함수를 사용해 3개의 타임 서버에 접속해 시간을 받아온다.

int main(int argc, char *argv[])
{
query_time("time.bora.net");
query_time("time.windows.com");
query_time("time.nist.gov");
return 0;
}

빌드
 
실행 결과





받아온 시간으로 리눅스 시스템의 시간을 동기화는 아래 함수를 사용하면 된다.

#include <time.h>
int stime(time_t *t);

댓글

이 블로그의 인기 게시물

간단한 cfar 알고리즘에 대해

쉽게 설명한 파티클 필터(particle filter) 동작 원리와 예제

아두이노(arduino) 심박센서 (heart rate sensor) 심박수 측정 example code

리눅스 디바이스 드라이버 기초와 예제

python winsound를 이용한 윈도우 환경에서 소리 재생 예제