通过NTP协议实现的网络对时程序

网络上有Windows平台的网络对时程序代码ntpclient.c,只是可读性、重用性不够强,还有一些冗余代码,本着精炼的目的,重写了代码,同时把有连接通信改为无连接通信。你可以把这段代码除了main函数以外的部分复制到一个.cpp文件,在你的工程中使用。release版本的大小只有7KB。

#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <crtdbg.h>
#include <sys/timeb.h>
#include <time.h>

const char* NTP_SERVER = "time.buptnet.edu.cn";
const int NTP_PORT = 123;
const unsigned int JAN_1970 = 0X83AA7E80;//=2208988800(1970/1/1 - 1900/1/1 in seconds)*/

#ifndef _DEBUG
#define printf(x)
#define printf(x, y)
#endif

struct ntp_timestamp
{
unsigned int secondsFrom1900;//1900年1月1日0时0分以来的秒数
unsigned int fraction;//微秒的4294.967296(=2^32/10^6)倍
};

/* How to multiply by 4294.967296 quickly (and not quite exactly)
* without using floating point or greater than 32-bit integers.
* If you want to fix the last 12 microseconds of error, add in
* (2911*(x))>>28)
*/
inline unsigned int microseconds2ntp_fraction(unsigned int x)
{
return (4294 * (x) + ((1981 * (x))>>11));
}

/* The reverse of the above, needed if we want to set our microsecond
* clock based on the incoming time in NTP format.
* Basically exact.
*/
inline unsigned int ntp_fraction2microseconds(unsigned int x)
{
return (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16));
}

struct ntp_header
{
union
{
   struct
   {
    char local_precision;//表示本机时钟精度为2^local_precision秒。local_precision通常为负数。
    char poll_intervals;//表示测试间隔为2^poll_intervals秒。
    unsigned char stratum;//NTP服务器阶级,0表示不指定,1表示校准用原子钟。应该为0。
    unsigned char mode : 3;//通信模式。应该为3,表示是client。
    unsigned char version_number : 3;//NTP协议版本号。应该为3。
    unsigned char leap_indicator : 2;//闰秒指示,一般填0。
   };
   int noname;
};

int root_delay;//可正可负。2^16表示一秒。具体含义参见RFC1305。
int root_dispersion;//只可为正。2^16表示一秒。具体含义参见RFC1305。
int reference_clock_identifier;//具体含义参见RFC1305。一般填0。
};//没有错误的话,ntp_header的大小应该为16字节。

struct ntp_packet
{
ntp_header header;
//以下四个时间均为本地时间。即考虑了时区位置的。
ntp_timestamp reference;//具体含义参见RFC1305。一般填0。
ntp_timestamp originate;//上次发出时刻
ntp_timestamp receive;//接收时刻
ntp_timestamp transmit;//发出时刻
};//没有错误的话,ntp_header的大小应该为48字节。

bool send_ntp_packet(SOCKET sock, const sockaddr* to)
{
ntp_packet packet;
memset(&packet, 0, sizeof(ntp_packet));
packet.header.local_precision = -6;
packet.header.poll_intervals = 4;
packet.header.stratum = 0;
packet.header.mode = 3;
packet.header.version_number = 3;
packet.header.leap_indicator = 0;

packet.header.root_delay = 1<<16;
packet.header.root_dispersion = 1<<16;
__timeb32 now;
_ftime32(&now);
packet.transmit.secondsFrom1900 = now.time + JAN_1970;
packet.transmit.fraction = microseconds2ntp_fraction(now.millitm);

packet.header.noname = htonl(packet.header.noname);
packet.header.root_delay = htonl(packet.header.root_delay);
packet.header.root_dispersion = htonl(packet.header.root_dispersion);
packet.transmit.secondsFrom1900 = htonl(packet.transmit.secondsFrom1900);
packet.transmit.fraction = htonl(packet.transmit.fraction);
int bytesSent = sendto(sock, (const char*)&packet, sizeof(packet), 0, to, sizeof(struct sockaddr_in));
return bytesSent != SOCKET_ERROR;
}

bool recv_ntp_packet(SOCKET sock, ntp_packet& packet)
{
int bytesRead = recvfrom(sock, (char*)&packet, sizeof(ntp_packet), 0, NULL, NULL);
return SOCKET_ERROR != bytesRead;
}

void printDateTime()
{
__time32_t now;
_time32(&now);
tm* ptm = _localtime32(&now);
char * message = asctime(ptm);
printf(message);
}

bool set_local_time(const ntp_packet& packet)
{
ntp_timestamp server_transmit_time;
server_transmit_time.secondsFrom1900 = ntohl(packet.transmit.secondsFrom1900);
server_transmit_time.fraction = ntohl(packet.transmit.fraction);
timeval newtime;
newtime.tv_sec = server_transmit_time.secondsFrom1900 - JAN_1970;
newtime.tv_usec = ntp_fraction2microseconds(server_transmit_time.fraction);
tm* ptm = _localtime32(&newtime.tv_sec);
if(ptm == NULL)
{
   return false;
}
SYSTEMTIME time;
time.wYear = ptm->tm_year + 1900;
time.wMonth = ptm->tm_mon + 1;
time.wDay = ptm->tm_mday;
time.wHour = ptm->tm_hour;
time.wMinute = ptm->tm_min;
time.wSecond = ptm->tm_sec;
time.wMilliseconds = 0;

#ifdef _DEBUG
printDateTime();
#endif
SetLocalTime(&time);
#ifdef _DEBUG
printDateTime();
#endif
return true;
}

int adjust_time()
{
int errorNo = 0;
//校验数据结构长度
_ASSERTE(sizeof(ntp_header) == 16);
_ASSERTE(sizeof(ntp_packet) == 48);
if(sizeof(ntp_packet) != 48)
   return -1;

// Initialize Winsock.
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ( iResult != NO_ERROR )
{
   printf("Error at WSAStartup()\n");
   return -2;
}

// Create a socket.
SOCKET m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if ( m_socket == INVALID_SOCKET )
{
   printf( "Error at socket(): %ld\n", WSAGetLastError() );
   WSACleanup();
   return -3;
}

/* bind local address. */
sockaddr_in addr_src;
int addr_len = sizeof(struct sockaddr_in);
memset(&addr_src, 0, addr_len);
addr_src.sin_family = AF_INET;
addr_src.sin_addr.s_addr = htonl(INADDR_ANY);
addr_src.sin_port = htons(0);
if (SOCKET_ERROR == bind(m_socket, (struct sockaddr*)&addr_src, addr_len))
{
   printf( "Error at bind(): %ld\n", WSAGetLastError() );
   errorNo = -4;
   goto Cleanup;
}

//fill addr_dst
sockaddr_in addr_dst;
memset(&addr_dst, 0, addr_len);
addr_dst.sin_family = AF_INET;
{
   struct hostent* host = gethostbyname(NTP_SERVER);
   if (NULL == host || 4 != host->h_length)
   {
    printf( "Error at gethostbyname()\n");
    errorNo = -5;
    goto Cleanup;
   }
   memcpy(&(addr_dst.sin_addr.s_addr), host->h_addr_list[0], 4);
}
addr_dst.sin_port = htons(NTP_PORT);

//设置读取超时
fd_set fds_read;
timeval timeout;
fds_read.fd_count = 1;
fds_read.fd_array[0] = m_socket;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
if (SOCKET_ERROR == select(0, &fds_read, NULL, NULL, &timeout))
{
   printf( "Error at select(): %ld\n", WSAGetLastError() );
   errorNo = -6;
   goto Cleanup;
}

if(send_ntp_packet(m_socket, (sockaddr*)&addr_dst) == false)
{
   printf( "Error at send(): %ld\n", WSAGetLastError() );
   errorNo = -7;
   goto Cleanup;
}

ntp_packet packet;
if(recv_ntp_packet(m_socket, packet) == false)
{
   printf( "Error at recvfrom(): %ld\n", WSAGetLastError() );
   errorNo = -8;
   goto Cleanup;
}

if(set_local_time(packet) == false)
{
   printf("now is after 03:14:07, January 19, 2038, UTC");
   errorNo = -9;
   //goto Cleanup;
}

Cleanup:
closesocket(m_socket);
WSACleanup();
return errorNo;
}

int main()
{
return adjust_time();
}

参考文献
RFC1305
ntpclient.c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值