SNTP 获取时间服务器UTC时间



// SntpTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <math.h>
#include <time.h>
#include <winsock2.h>
#pragma comment(lib, "wsock32.lib")

//ref : www.ietf.org/rfc/rfc2030.txt
/*                   1                   2                   3   
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Root Delay                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Root Dispersion                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Reference Identifier                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Reference Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Originate Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                    Receive Timestamp (64)                     |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                    Transmit Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 Key Identifier (optional) (32)                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                                                               |
|                 Message Digest (optional) (128)               |
|                                                               |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

#pragma pack(push, 1)
typedef struct _tagNTPTimestamp_t
{
  unsigned char Seconds[4]; //(32-bit unsigned fixed-point number. Big-endian)
  unsigned char Seconds_Fraction[4]; //((32-bit unsigned fixed-point number. Big-endian) 
}NTPTIMESTAMP, *LPNTPTIMESTAMP;

typedef struct _tagNTPMsgFmtV4_t
{
  struct 
  {
    unsigned char LI:2; //Leap Indicator
    //0  no warning
    //1  last minute has 61 seconds
    //2  last minute has 59 seconds)
    //3  alarm condition (clock not synchronized)
    
    unsigned char VN:3; //Version Number
    //3  for Version 3 (IPv4 only)
    //4  for Version 4 (IPv4, IPv6 and OSI)
    
    unsigned char Mode:3; //indicating the mode
    //0  reserved
    //1  symmetric active
    //2  symmetric passive
    //3  client
    //4  server
    //5  broadcast
    //6  reserved for NTP control message
    //7  reserved for private use
    
    unsigned char Stratum; //stratum level of the local clock
    //0       unspecified or unavailable
    //1       primary reference (e.g., radio clock)
    //2-15    secondary reference (via NTP or SNTP)
    //16-255  reserved
    
    unsigned char Poll; //Poll Interval
    
    unsigned char Precision; //Precision 
  };
  
  unsigned char Root_Delay[4]; //Root Delay (32-bit signed fixed-point number. Big-endian)
  
  unsigned char Root_Dispersion[4]; //Root Dispersion (32-bit unsigned fixed-point number. Big-endian)
  
  char Reference_Identifier[4]; //Reference Identifier
  //LOCL     uncalibrated local clock used as a primary reference for a subnet without external means of synchronization
  //PPS      atomic clock or other pulse-per-second source individually calibrated to national standards
  //ACTS     NIST dialup modem service
  //USNO     USNO modem service
  //PTB      PTB (Germany) modem service
  //TDF      Allouis (France) Radio 164 kHz
  //DCF      Mainflingen (Germany) Radio 77.5 kHz
  //MSF      Rugby (UK) Radio 60 kHz
  //WWV      Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz
  //WWVB     Boulder (US) Radio 60 kHz
  //WWVH     Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz
  //CHU      Ottawa (Canada) Radio 3330, 7335, 14670 kHz
  //LORC     LORAN-C radionavigation system
  //OMEG     OMEGA radionavigation system
  //GPS      Global Positioning Service
  //GOES     Geostationary Orbit Environment Satellite
  
  NTPTIMESTAMP Reference_Timestamp; //Reference Timestamp (64-bit timestamp format. Big-endian)
  
  NTPTIMESTAMP Originate_Timestamp; //Originate Timestamp (64-bit timestamp format. Big-endian)
  
  NTPTIMESTAMP Receive_Timestamp; //ReceiveTimestamp (64-bit timestamp format. Big-endian)
  
  NTPTIMESTAMP Transmit_Timestamp; //Transmit Timestamp (64-bit timestamp format. Big-endian)
  
  //char Key_Identifier[4]; //Key Identifier (optional)
  
  //unsigned char Message_Digest[16]; //Message Digest (optional)    
  
}NTPMSGFMTV4, *LPNTPMSGFMTV4;
#pragma pack(pop)

//convert NTP timer to system time
BOOL _SNTPTimeToSystemTime(LPNTPTIMESTAMP pNTPTimeStame, SYSTEMTIME &tmDest)
{
  // Verifies will fail if the needed buffer size is too large
#define _MIN_DATE                (-657434L)  // about year 100
#define _MAX_DATE                2958465L    // about year 9999
  
  // Half a second, expressed in days
#define _HALF_SECOND  (1.0/172800.0)
  double dtSrc = 0;
  double fMillsecods = 0;

  //convert NTP time to days unit
  {
    u_long uSeconds = ntohl( *((u_long*)&pNTPTimeStame->Seconds[0]) ); //seconds
    u_long uSecondsF = ntohl( *((u_long*)&pNTPTimeStame->Seconds_Fraction[0]) ); //Seconds Fraction
    
    //convert Seconds Fraction to double type 
    double fSecondF = 0;
    {
      u_long uSecondF =uSecondsF;
      int iLen = 0;
      double fPart[64] = {0};        
      while(uSecondF && iLen < 64-1)
      {
        fPart[iLen++] = (uSecondF % 10);
        uSecondF = uSecondF/10;
      }
      
      double fDiv = 10.0;
      while((--iLen) >= 0)
      {
        fSecondF += fPart[iLen]/fDiv;
        fDiv *= 10;
      }
    }    
    
    double vtDate = uSeconds;
    if( pNTPTimeStame->Seconds[0] & 0x80 ) //since from 1900,January,1,00:00:00 to 2036
    {
    }
    else //since form 6h 28m 16s UTC on 7 February 2036
    {
      vtDate += 0xFFFFFFFFU; //overflow
    }

    vtDate += (2 * 24 * 3600); //system time Dec. 30, 1899 to NTP Jan.1, 1900

    dtSrc = vtDate  / (24 * 60 * 60); //convert to days
    fMillsecods = (int)(fSecondF * 1000.0);
  }

  if (dtSrc > _MAX_DATE || dtSrc < _MIN_DATE) // about year 100 to about 9999
		return FALSE;

  // The legal range does not actually span year 0 to 9999.
  long nDays;             // Number of days since Dec. 30, 1899
  long nDaysAbsolute;     // Number of days since 1/1/0
  long nSecsInDay;        // Time in seconds since midnight
  long nMinutesInDay;     // Minutes in day

  long n400Years;         // Number of 400 year increments since 1/1/0
  long n400Century;       // Century within 400 year block (0,1,2 or 3)
  long n4Years;           // Number of 4 year increments since 1/1/0
  long n4Day;             // Day within 4 year block
						  //  (0 is 1/1/yr1, 1460 is 12/31/yr4)
  long n4Yr;              // Year within 4 year block (0,1,2 or 3)
  BOOL bLeap4 = TRUE;     // TRUE if 4 year block includes leap year

  double dblDate = dtSrc; // tempory serial date

  // If a valid date, then this conversion should not overflow
  nDays = (long)dblDate;

  // Round to the second
  dblDate += ((dtSrc > 0.0) ? _HALF_SECOND : -_HALF_SECOND);

  nDaysAbsolute = (long)dblDate + 693959L; // Add days from 1/1/0 to 12/30/1899

  dblDate = fabs(dblDate);
  nSecsInDay = (long)((dblDate - floor(dblDate)) * 86400.);

  // Calculate the day of week (sun=0, mon=1...)
  tmDest.wDayOfWeek = (int)((nDaysAbsolute - 1) % 7L) + 0;

  // Leap years every 4 yrs except centuries not multiples of 400.
  n400Years = (long)(nDaysAbsolute / 146097L);

  // Set nDaysAbsolute to day within 400-year block
  nDaysAbsolute %= 146097L;

  // -1 because first century has extra day
  n400Century = (long)((nDaysAbsolute - 1) / 36524L);

  // Non-leap century
  if (n400Century != 0)
  {
    // Set nDaysAbsolute to day within century
    nDaysAbsolute = (nDaysAbsolute - 1) % 36524L;
    
    // +1 because 1st 4 year increment has 1460 days
    n4Years = (long)((nDaysAbsolute + 1) / 1461L);

    if (n4Years != 0)
    {
      n4Day = (long)((nDaysAbsolute + 1) % 1461L);
    }
    else
    {
      bLeap4 = FALSE;
      n4Day = (long)nDaysAbsolute;
    }
  }
  else
  {
    // Leap century - not special case!
    n4Years = (long)(nDaysAbsolute / 1461L);
    n4Day = (long)(nDaysAbsolute % 1461L);
  }

  if (bLeap4)
  {
    // -1 because first year has 366 days
    n4Yr = (n4Day - 1) / 365;
    
    if (n4Yr != 0)
      n4Day = (n4Day - 1) % 365;
  }
  else
  {
    n4Yr = n4Day / 365;
    n4Day %= 365;
  }

  // n4Day is now 0-based day of year. Save 1-based day of year, year number
  int tm_yday = (int)n4Day + 1; /* days since January 1 - [0,365] */
  tmDest.wYear = (WORD)(n400Years * 400 + n400Century * 100 + n4Years * 4 + n4Yr);

  do 
  {  
    // Handle leap year: before, on, and after Feb. 29.
    if (n4Yr == 0 && bLeap4)
    {
      // Leap Year
      if (n4Day == 59)
      {
        /* Feb. 29 */
        tmDest.wMonth = 2;
        tmDest.wDay = 29;
        break; //goto DoTime;
      }
      
      // Pretend it's not a leap year for month/day comp.
      if (n4Day >= 60)
        --n4Day;
    }

    // Make n4DaY a 1-based day of non-leap year and compute
    //  month/day for everything but Feb. 29.
    ++n4Day;

    // Month number always >= n/32, so save some loop time */
    // One-based array of days in year at month start
    static int _MonthDays[13] =
    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
    for (tmDest.wMonth = (n4Day >> 5) + 1;
      n4Day > _MonthDays[tmDest.wMonth]; tmDest.wMonth++);

    tmDest.wDay = (int)(n4Day - _MonthDays[tmDest.wMonth-1]);

  }while(0);

//DoTime:
  if (nSecsInDay == 0)
  {
    tmDest.wHour = tmDest.wMinute = tmDest.wSecond = 0;
  }
  else
  {
    tmDest.wSecond = (WORD)(nSecsInDay % 60L);
    nMinutesInDay = nSecsInDay / 60L;
    tmDest.wMinute = (WORD)(nMinutesInDay % 60);
    tmDest.wHour = (WORD)(nMinutesInDay / 60);
  }

  tmDest.wMilliseconds = (WORD)fMillsecods;
  return TRUE;
}

int _SNTPGetUTCTime(const char *szSntpServer, SYSTEMTIME *ptmSys)
{  

  BOOL bRet = 0;
  do
  {
    //init socket
    int err = 0;
    WSADATA wsa;
    err = WSAStartup(MAKEWORD(2, 0), &wsa);
    if(err != 0)
    {
      bRet = -1;
      break;
    }

    SOCKET sSession = INVALID_SOCKET;

    do 
    {
      //get host ip by name
      hostent * hp = gethostbyname(szSntpServer);
      if(hp == NULL)
      {
        bRet = -2;
        break;
      }

      sockaddr_in SvrIp;
      SvrIp.sin_family		= AF_INET;
      memcpy(&SvrIp.sin_addr, hp->h_addr, sizeof(SvrIp.sin_addr));
      SvrIp.sin_port		= htons(123);
      
      //create socket
      sSession = socket(AF_INET, SOCK_DGRAM, 0);
      if(sSession == INVALID_SOCKET)
      {
        bRet = -3;
        break;
      }

      int iSizeSocketIn = sizeof(SOCKADDR_IN);

      //sendto
      int nTime = 3000;
      setsockopt(sSession, SOL_SOCKET, SO_SNDTIMEO, (char *)&nTime, sizeof(nTime));       
      NTPMSGFMTV4 txMsg; memset(&txMsg, 0, sizeof(txMsg));
      txMsg.LI = 3; //alarm condition (clock not synchronized)
      txMsg.VN = 4; //4  for Version 4 (IPv4, IPv6 and OSI)
      txMsg.Mode = 4; //4 server
      if(SOCKET_ERROR == sendto(sSession, (const char*)&txMsg, sizeof(txMsg), 0, (sockaddr*)&SvrIp, iSizeSocketIn))
      {
        bRet = -4;
        break;
      }

      //recvfrom
      setsockopt(sSession, SOL_SOCKET, SO_RCVTIMEO, (char *)&nTime, sizeof(nTime));       
      NTPMSGFMTV4 rxMsg; memset(&rxMsg, 0, sizeof(rxMsg));
      int nLenRcv = recvfrom(sSession, (char *)&rxMsg, sizeof(rxMsg), 0, (sockaddr*)&SvrIp, &iSizeSocketIn);
      if(nLenRcv == SOCKET_ERROR)
      {
        bRet = -5;
        break;
      }

      //use convert SNTP date to system time
      SYSTEMTIME tmSys;
      if(!_SNTPTimeToSystemTime(&rxMsg.Transmit_Timestamp, tmSys))
      {
        bRet = -6;
        break;
      }

      if(ptmSys)
      {
        *ptmSys = tmSys;
      }
      bRet = 1;
      
    }while(0);

    //close socket
    if(sSession != INVALID_SOCKET)
    {
      closesocket(sSession);
      sSession = INVALID_SOCKET;
    }
    
    //WASClearup
    WSACleanup();   
  }while(0);

  return bRet;
}

int TestSNTP()
{
  LPCTSTR szTimeSvr[]=
  {
    "time.windows.com",
    "time.nist.gov",
    "time-nw.nist.gov",
    "pool.ntp.org",
    "europe.pool.ntp.org",
    "asia.pool.ntp.org",
    "oceania.pool.ntp.org",
    "north-america.pool.ntp.org",
    "south-america.pool.ntp.org",
    "africa.pool.ntp.org",
    "ca.pool.ntp.org",
    "uk.pool.ntp.org",
    "us.pool.ntp.org",
    "au.pool.ntp.org",
    0
  };
  
  for(size_t i = 0; szTimeSvr[i] ; ++i)
  {
    SYSTEMTIME tm = {0};
    if(_SNTPGetUTCTime(szTimeSvr[i], &tm) > 0)
    {
      printf("%s: %04dY-%02dM-%02dD (Week %d) %02dH:%02dM:%02dS.%03dms\n",
        szTimeSvr[i],
        tm.wYear, tm.wMonth, tm.wDay, tm.wDayOfWeek,
        tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
    }
  }

  return 0;
}



int main(int argc, char* argv[])
{
  TestSNTP();

  printf("Hello World!\n");
  return 0;
}



/* debug output
time.windows.com: 2015Y-05M-25D (Week 1) 07H:05M:20S.915ms
time.nist.gov: 2015Y-05M-25D (Week 1) 07H:05M:20S.133ms
pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:24S.573ms
oceania.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:31S.116ms
south-america.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:35S.617ms
africa.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:35S.235ms
ca.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:35S.405ms
uk.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:36S.580ms
us.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:36S.185ms
au.pool.ntp.org: 2015Y-05M-25D (Week 1) 07H:05M:36S.328ms
Hello World!
Press any key to continue
*/










  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值