NTP

前几天我们头王老师,让我做个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;
}
 
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值