前几天我们头王老师,让我做个ntp库,当时一片空白,后来几经周折,才整出了这么一套代码,希望能帮助需要的人。 关于ntp原理的信息请大家还是去官网查看吧,再次不在赘述, 很基础的的代码,我已经验证过了没问题,大家如果有什么其他的疑问,可以随时qq联系
#ifndef MY_NTP_H
#define MY_NTP_H
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#if _WIN32
#define PLATFORM_WINDOWS_ENVIRONMENTS 1
#else
#define PLATFORM_LINUX_ENVIRONMENTS 1
#endif
#if PLATFORM_WINDOWS_ENVIRONMENTS
#pragma message("WIN-0")
#pragma comment(lib,"ws2_32.lib")
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <winbase.h>
#include <windef.h>
#pragma message("WIN-1")
#elif PLATFORM_LINUX_ENVIRONMENTS
#pragma message("LINUX-0")
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <iostream>
#pragma message("LINUX-1")
#endif // PLATFORM_LINUX_ENVIRONMENTS
#define TIME_PORT 37
#define NTPV1 "NTP/V1"
#define NTPV2 "NTP/V2"
#define NTPV3 "NTP/V3"
#define NTPV4 "NTP/V4"
#define TIME "TIME/UDP"
#define NTP_PCK_LEN 48
#define TIME_PORT 37
#define NTP_PORT 123
#define LI 0
#define VN 3
#define MODE 3
#define STRATUM 0
#define POLL 4
#define PREC -6
#define NINT 4
#define JAN_1970 0x83aa7e80 //3600s*24h*(365days*70years+17days)
//x*10^(-6)*2^32 ����ת NtpTime �ṹ�� fraction ����
#define NTPFRAC(x) (4294 * (x) + ((1981 * (x)) >> 11))
//NTPFRAC��������
#define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16))
const int my_debug_ = 1;
//ntpʱ����ṹ
typedef struct _ntp_time
{
unsigned int coarse;
unsigned int fine;
} Ntp_Time;
struct Ntp_Packet
{
unsigned char leap_ver_mode;
unsigned char startum;
char poll;
char precision;
int root_delay;
int root_dispersion;
int reference_identifier;
Ntp_Time reference_timestamp;//c-receive
Ntp_Time originage_timestamp;//c-send
Ntp_Time receive_timestamp;//s-receive
Ntp_Time transmit_timestamp;//s-send
};
/*****************************************************************************************************
*Function Name: construct_packet
*Description:
* ����ntp��-��:NTP packet = NTP header + Four TimeStamps = 48byt��
*
* Detail-------->>>>>>
* NTP header : 16byte
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
* |LI | VN |Mode | Stratum | Poll | Precision | Root Delay | Root Dispersion | Reference Identifier |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
* LeapYearIndicator : 2bit
* VersionNumber : 3bit
* Stratum : 8bit
* Mode : 3 bit
* PollInterval : 8 bit
* Percision : 8bit
* Root delay : 32bit
* Root Dispersion : 32bit
* Reference Identifier : 32bit
* Four TimeStamps each: 32byte
*Input: protocol --> �汾��
*Output�� packet ������sendʱ����
*Return��NTP_PCK_LEN����48��wei��ȷ��other ������
*Other��
**************************************************************************************************/
static int construct_packet( unsigned int* packet, char* protocol);
/************************************************************
*Function : connect_ntp_server
*Description:
* 1��getaddrinfo������ socket�������ͨ��
*Input: IP --> NTP������IP��ַ
*Output: res->NTP��������Ϣ
*
*
*Return��������������Ϊ��������ȷ����������������
*Other��
*************************************************************/
static int connect_ntp_server(const char* serverIP, struct addrinfo* res);
/***************************************************************
*Function : get_Ntp_Time
*Description:
* 1��getaddrinfo������ socket�������ͨ��
* 2��construct_packet������sendto ����������ʱ����
*�� 3��rcvfrom����ʱ�����������յ���ʱ�������õ�translateʱ����
*
*Input: IP --> NTP������IP��ַ
*Return��0��ȷ��1����
*Other��
****************************************************************/
int get_ntp_time(const char* serverIP);
/*************************************************************
*Function : closesk
*Description: close socket
*Input: sk --> sk description
*Return�� void
*Other��
************************************************************/
static void closesk(int sk);
/**********************************************************
*Function : set_local_time
*Description: ���ã��������졡�������壨�����ǣ�����������������������
*
*Input: pnew_time_packet --> �Σԣз�����ص�translateʱ����
*
*Return��0��ȷ��1����
*Other��
************************************************************/
static int set_local_time(struct Ntp_Packet* pnew_time_packet);
/*#undef PLATFORM_WINDOWS_ENVIRONMENTS
#undef PLATFORM_WINDOWS_ENVIRONMENTS*/
#endif // MY_NTP_H
///
#include "../include/ntp.h"
using namespace std;
static int construct_packet(unsigned int* packet, char* protocol)
{
char version = 1;
unsigned int tmp_wrd;
int port;
time_t timer = 0;
//
if (strcmp(protocol, NTPV1) == 0 || strcmp(protocol, NTPV2) == 0
|| strcmp(protocol, NTPV3) == 0 || strcmp(protocol, NTPV4) == 0)
{
port = NTP_PORT;
version = protocol[5] - 0x30;/*VN*version*/
tmp_wrd = htonl((LI << 30) | (version << 27) | (MODE << 24) | (STRATUM << 16) | (POLL << 8) | (PREC & 0xff));
memcpy(packet, &tmp_wrd, sizeof(int));
//root Delay .. root Dispersion ..Reference Identifier
tmp_wrd = htonl(1 << 16);
memcpy(&packet[1], &tmp_wrd, sizeof(int));
memcpy(&packet[2], &tmp_wrd, sizeof(int));
//Timestamp
time(&timer);
//Timestamp coarse
tmp_wrd = htonl(JAN_1970 + (long)timer);//COURSE TIME
memcpy(&packet[10], &tmp_wrd, sizeof(int));
//TimeStamp Fine
tmp_wrd = htonl((long)NTPFRAC(timer));//FINE TIME
memcpy(&packet[11], &tmp_wrd, sizeof(int));
return NTP_PCK_LEN;
}
else if (!strcmp(protocol, TIME))/* "TIME/UDP"*/
{
port = TIME_PORT;
return 4;
}
return 0;
}
static int connect_ntp_server(const char* serverIP, struct addrinfo* res)
{
int rc, sd;
struct addrinfo hints, *tmp_addr;
char cPort[32] = { 0 };
#ifdef PLATFORM_WINDOWS_ENVIRONMENTS
int err;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
#pragma message("WIN ->sk Init ok")
#endif // WIN32
memset(&hints, 0, sizeof(hints));
//建立socket
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
sprintf(cPort, "%d", NTP_PORT);
rc = getaddrinfo(serverIP, cPort, &hints, &tmp_addr);
if (rc != 0)
{
perror("getaddrinfo()");
exit(1);
}
memcpy(res, tmp_addr, sizeof(struct addrinfo));
sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sd < 0)
{
perror("socket()");
exit(1);
}
return sd;
}
static void closesk(int sk)
{
#ifdef PLATFORM_LINUX_ENVIRONMENTS
close(sk);
#pragma message("LINUX ->sk closed")
#elif PLATFORM_WINDOWS_ENVIRONMENTS
closesocket(sk);
#pragma message("WIN ->sk closed")
#endif
}
int get_ntp_time(const char* serverIP)
{
char protocol[32] = { 0 };
struct Ntp_Packet ret_time_s, *ret_time = &ret_time_s;
unsigned int data[NTP_PCK_LEN];
int sockfd, packet_len, count = 0, addr_len = 0, result;
struct addrinfo res;
fd_set pending_data;
struct timeval block_time;
memset(&res, 0, sizeof(struct addrinfo));
sockfd = connect_ntp_server(serverIP, &res);
strcpy(protocol, NTPV3);
int nMAX = 5;//如果没能收到server发来的包 那么最多循环发送5次,超出退出
do{
memset(data, 0, NTP_PCK_LEN * NINT);
if ((packet_len = construct_packet(data, protocol)) != NTP_PCK_LEN)
{
exit(1);
}
addr_len = res.ai_addrlen;
if ((result = sendto(sockfd, (const char*)data, packet_len, 0, res.ai_addr, addr_len)) < 0)
{
perror("sendto()");
exit(1);
}
if (my_debug_){ cout << "socket :" << socket << "data :" << &data << res.ai_addr << addr_len << endl; }
//do{// set delay 10tv_sec
FD_ZERO(&pending_data);
FD_SET(sockfd, &pending_data);
block_time.tv_sec = 1;
block_time.tv_usec = 0;
int nfds = 0;
if (my_debug_) { int i = 0; cout << "check : " << nfds<< endl; }
//select() 轮巡 端口 看有没有收到time packet 信息
if (nfds = select(sockfd + 1, &pending_data, NULL, NULL, &block_time) > 0)
{
if (((count = recvfrom(sockfd, (char*)data, NTP_PCK_LEN * 8, 0, res.ai_addr, (socklen_t*)&addr_len)) < 0))
{
perror("recvfrom()");
exit(1);
}
printf("time recefrom course: %ud\n", ntohl((int)data[10]));
printf("time recefrom fine : %ud\n", ntohl((int)data[11]));
if (strcmp(protocol, TIME) == 0)
{
printf("time ==0: \n");
ret_time->transmit_timestamp.coarse = ntohl(*((unsigned int*)&data[10]));
ret_time->transmit_timestamp.fine = ntohl(*((unsigned int*)&data[11]));
closesk(sockfd);
exit(1);
}
else if (count < NTP_PCK_LEN)
{
closesk(sockfd);
exit(1);
}
//get trans_timestamp from receiver packet;
ret_time->leap_ver_mode = (char)ntohl(*(char*)&(data[0]))/*(data[0])*/;
ret_time->startum = (char)ntohl(*((char*)&(data[0]) + 1))/*(data[1])*/;
ret_time->poll = (char)ntohl(*((char*)&(data[0]) + 2))/*(data[2])*/;
ret_time->precision = (*((char*)&(data[0]) + 3))/*(data[3])*/;
ret_time->root_delay = ntohl(*(int*)&(data[1]));
ret_time->root_dispersion = ntohl(*(int*)&(data[2]));
ret_time->reference_identifier = ntohl(*(int*)&(data[3]));
ret_time->reference_timestamp.coarse = ntohl(*(int*)&(data[4]));
ret_time->reference_timestamp.fine = ntohl(*(int*)&(data[5]));
ret_time->originage_timestamp.coarse = ntohl(*(int*)&(data[6]));
ret_time->originage_timestamp.fine = ntohl(*(int*)&(data[7]));
ret_time->receive_timestamp.coarse = ntohl(*(int*)&(data[8]));
ret_time->receive_timestamp.fine = ntohl(*(int*)&(data[9]));
ret_time->transmit_timestamp.coarse = ntohl(*(int*)&(data[10]));
ret_time->transmit_timestamp.fine = ntohl(*(int*)&(data[11]));
closesk(sockfd);
return set_local_time(ret_time);
} //end of if select
} while (nMAX--);
printf("Can not recvfrom() server .\n");
exit(1);
}
static int set_local_time(struct Ntp_Packet* pnew_time_packet)
{
int set_time_right = 1;
struct timeval tv;
int res_set = 1;
memset(&tv, 0, sizeof(struct timeval));
tv.tv_sec = pnew_time_packet->transmit_timestamp.coarse - JAN_1970;
tv.tv_usec = USEC(pnew_time_packet->transmit_timestamp.fine);
#ifdef PLATFORM_WINDOWS_ENVIRONMENTS
time_t t;
t = tv.tv_sec;
struct tm temptm = *localtime(&t);
SYSTEMTIME curtime = { 1900 + temptm.tm_year,//*******将收到的时间填入SYSTEM结构体;
1 + temptm.tm_mon,
temptm.tm_wday,
temptm.tm_mday,
1 + temptm.tm_hour,
temptm.tm_min,
temptm.tm_sec,
0 };
if(res_set = SetLocalTime(&curtime))//set Localtime time
set_time_right = 0;
#pragma message("WIN-set local time")
#elif PLATFORM_LINUX_ENVIRONMENTS // PLATFORM_LINUX_ENVIRONMENTS
if (res_set = settimeofday(&tv, NULL) == 0)
set_time_right = 0;
#pragma message("LINUX-set local time")
#endif // PLATFORM_LINUX_ENVIRONMENTS or PLATFORM_LINUX_ENVIRONMENTS
if(my_debug_)
cout << "set LOcal time :" << res_set << endl;
return set_time_right;
}