有时候需要获取网络时间,又没有自己的服务器去返回,那么可以考虑从NTP授时服务器获取,windows的同步网络时间就是从NTP授时服务器获取的。
NTP授时服务器协议采用UDP,端口一般为123,下面给出从github上整理的代码,亲测可用,授时服务器地址选用的是阿里云的公共NTP服务器。
INT64 GetNTPTime()
{
//初始化网络库
WSADATA wsaData;
::WSAStartup(MAKEWORD(2, 2), &wsaData);
//下面是获取过程
INT64 curTm = 0;
struct hostent *host = NULL;
int nErrno = 0;
const char *server = "ntp.aliyun.com";
const char *port = "123";
ADDRINFOA hints, *res, *ap; /* address info structs */
socklen_t addrlen = sizeof(struct sockaddr_storage);
struct ntpPacket{
UINT8 flags;
UINT8 stratum;
UINT8 poll;
UINT8 precision;
UINT32 root_delay;
UINT32 root_dispersion;
UINT8 referenceID[4];
UINT32 ref_ts_sec;
UINT32 ref_ts_frac;
UINT32 origin_ts_sec;
UINT32 origin_ts_frac;
UINT32 recv_ts_sec;
UINT32 recv_ts_frac;
UINT32 trans_ts_sec;
UINT32 trans_ts_frac;
};
#define ENDIAN_SWAP32(data) ((data >> 24) | /* right shift 3 bytes */ \
((data & 0x00ff0000) >> 8) | /* right shift 1 byte */ \
((data & 0x0000ff00) << 8) | /* left shift 1 byte */ \
((data & 0x000000ff) << 24)) /* left shift 3 bytes */
struct ntpPacket packet;
UINT8 *ptr = (UINT8 *)(&packet); /* to read raw bytes */
int server_sock; /* send through this socket */
int error; /* error checking */
unsigned int recv_secs;
/* server is required, port is optional */
memset(&packet, 0, sizeof(struct ntpPacket));
packet.flags = 0xe3;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
/* fill our address structs for ntp server */
error = getaddrinfo(server, port, &hints, &res);
/* loop through results */
for (ap = res; ap != NULL; ap = ap->ai_next) {
server_sock = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
if (server_sock == -1)
continue;
break;
}
if (ap == NULL) {
return curTm;
}
error = ::sendto(server_sock, (const char*)&packet, sizeof(struct ntpPacket), 0, ap->ai_addr, addrlen);
if (error == -1) {
return curTm;
}
error = recvfrom(server_sock, (char*)&packet, sizeof(struct ntpPacket), 0, ap->ai_addr, &addrlen);
if (error == -1) {
return curTm;
}
freeaddrinfo(res); /* all done */
/* correct for right endianess */
packet.recv_ts_sec = ENDIAN_SWAP32(packet.recv_ts_sec);
/* print date with receive timestamp */
recv_secs = packet.recv_ts_sec - 2208988800L; /* convert to unix time */
curTm = recv_secs;
return curTm;
}
代码比较粗糙,比如没有加winsock2.h等头文件,比如没有WSACleanup()等,请使用者根据自己的实际使用情况自行添加和优化。
代码我是在win下面跑的,一切正常。linux和unix下,稍微改改也可以跑。