参考:
NTP协议-蒲公英云
#ifndef __NTP_UPDATE_TIME_H__
#define __NTP_UPDATE_TIME_H__
#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 1
#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;
};
class ntpOptSyncTimeClass
{
public:
static 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;
}
static int get_ntp_packet(void *buf, size_t *size) //构建并发送NTP请求报文
{
struct ntphdr *ntp;
struct timeval tv;
if (!size || *size < NTP_HLEN)
return -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); //把目前的时间用tv 结构体返回
ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
*size = NTP_HLEN;
return 0;
}
static 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);
}
static 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;
}
static int getNtpTimeNow(std::string ipaddr, u16 ntpPort, time_t &ntpTime)
{
// char dateBuf[64] = {0};
// char cmd[128] = {0};
// tm *local;
char buf[BUFSIZE];
size_t nbytes;
int sockfd, maxfd1;
struct sockaddr_in servaddr;
fd_set readfds;
struct timeval timeout, recvtv, tv;
double offset;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(ntpPort);
servaddr.sin_addr.s_addr = inet_host(ipaddr.c_str());
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
logwerr("open err");
return -1;
}
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)) != 0)
{
logwerr("connect error");
goto outFuncPoint;
}
nbytes = BUFSIZE;
if (get_ntp_packet(buf, &nbytes) != 0)
{
fprintf(stderr, "construct ntp request error \n");
goto outFuncPoint;
}
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)
{
logwerr("recv error");
goto outFuncPoint;
}
//计算C/S时间偏移量
gettimeofday(&recvtv, NULL);
offset = get_offset((struct ntphdr *)buf, &recvtv);
gettimeofday(&tv, NULL);
tv.tv_sec += (int)offset; //+28800;//alen
tv.tv_usec += offset - (int)offset;
ntpTime = tv.tv_sec;
return 0;
}
}
outFuncPoint:
close(sockfd);
return -2;
}
//reference ntp server:https://blog.csdn.net/weixin_42284074/article/details/123173016
// std::string testNtpHostList[] =
// {
// "ntp.ntsc.ac.cn",//中国国家授时中心
// // "cn.ntp.org.cn",//中国授时
// //阿里云NTP时间源服务器
// "ntp1.aliyun.com",
// "ntp2.aliyun.com",
// "ntp3.aliyun.com",
// "ntp4.aliyun.com",
// "ntp5.aliyun.com",
// "ntp6.aliyun.com",
// "ntp7.aliyun.com",
// //国内一些大学NTP时间源服务器
// // "s1a.time.edu.cn",// 北京邮电大学
// // "s1b.time.edu.cn",// 清华大学
// // "s1c.time.edu.cn",// 北京大学
// // "s1d.time.edu.cn",// 东南大学
// // "s1e.time.edu.cn",// 清华大学
// // "s2a.time.edu.cn",// 清华大学
// // "s2b.time.edu.cn",// 清华大学
// // "s2c.time.edu.cn",// 北京邮电大学
// // "s2d.time.edu.cn",// 西南地区网络中心
// // "s2e.time.edu.cn",// 西北地区网络中心
// // "s2f.time.edu.cn",// 东北地区网络中心
// // "s2g.time.edu.cn",// 华东南地区网络中心
// // "s2h.time.edu.cn",// 四川大学网络管理中心
// // "s2j.time.edu.cn",// 大连理工大学网络中心
// // "s2k.time.edu.cn",// CERNET桂林主节点
// // "s2m.time.edu.cn",// 北京大学
// };
// int main()
// {
// for(int i = 0; i < (int)ARRAY_LEN(testNtpHostList); i++)
// {
// logwdbg("-------->update :%s", testNtpHostList[i].c_str());
// ntpOptSyncTimeClass::updateNtpTime(testNtpHostList[i], 123);
// }
// return 0;
// }
static void updateNtpTime(std::string ipaddr, u16 ntpPort)
{
time_t ntpGetTime, timeNow;
if (getNtpTimeNow(ipaddr, ntpPort, ntpGetTime) == 0)
{
timeNow = time(NULL);
tm *loct = localtime(&ntpGetTime);
logwdbg("%d-%d-%d %d:%d:%d", loct->tm_year + 1900, loct->tm_mon + 1, loct->tm_mday, loct->tm_hour, loct->tm_min, loct->tm_sec);
if (abs(ntpGetTime - timeNow) < 30)
{
return;
}
struct timeval tmptval = {ntpGetTime, 0};
settimeofday(&tmptval, NULL);
miscOperateClass::sysPro("hwclock -w -u");
}
else
{
logwwrn("up ntp fail: %s", ipaddr.c_str());
}
}
};
#endif