获取网络时间(即获取网络时间同步服务器的时间)

来源:互联网 发布:jdk 8u25 windows x64 编辑:程序博客网 时间:2024/06/05 22:48

    要获取准确的时间,用于校时或其他操作,可以通过获取时间同步服务器的信息来实现。下面介绍几个常用的时间同步服务器的域名及IP地址:

域名IP地址time-a.nist.gov129.6.15.28time-b.nist.gov129.6.15.29time-a.timefreq.bldrdoc.gov132.163.4.101time-b.timefreq.bldrdoc132.163.4.102time-c.timefreq.bldrdoc.gov132.163.4.103utcnist.colorado.edu128.138.140.44time.nist.gov192.43.244.18time-nw.nist.gov131.107.1.10nist1.datum.com66.243.43.21nist1-dc.glassey.com216.200.93.8nist1-ny.glassey.com208.184.49.9nist1-sj.glassey.com207.126.98.204nist1.aol-ca.truetime.com207.200.81.113nist1.aol-va.truetime.com205.188.185.33国家授时210.72.145.44
    可以通过套接字实现对时间的获取,但是获取到的时间信息是 基于1900年1月1日0时0分0秒的信息,也就是说从时间同步服务器返回的是1900年1月1日0时0分0秒至今的秒数。显然需要将其转化为我们常用的时间格式。另外,还需注意一点:时差。

    时间同步服务器返回的时间数据是基于世界时(GMT),也就是格林尼治所在地的标准时间。而北京时间与伦敦GMT存在8小时的时差。所以,在转化过程中要考虑时差。

    下面先介绍利用套接字获取时间数据的函数GetTimeFromServer:

头文件:

#include "winsock2.h"#pragma comment(lib, "WS2_32.lib")  // 显式连接套接字库

函数:

/************************************************************************//* 从时间同步服务器获取时间信息                                         *//************************************************************************/DWORD CGetNetworkTimeDlg::GetTimeFromServer(char *ip_addr){// 参数ip_addr:表示指定的时间服务器IP地址// 返回:自1900年1月1日午0时0分0秒至今的毫秒数 或 0(表示获取失败)// 默认的时间服务器为"国家授时中心"if (ip_addr == NULL){ip_addr = _T("210.72.145.44");}// 定义WSADATA结构体对象WSADATA date;// 定义版本号码WORD w = MAKEWORD(2, 0);// 初始化套接字库if ( ::WSAStartup(w, &date) != 0 ){MessageBox(_T("初始化套接字库失败!"));return 0;}// 定义连接套接字句柄SOCKET s;// 定义接收信息保存变量DWORD  m_serverTime;// 创建TCP套接字s = ::socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == s){MessageBox(_T("创建套接字失败!"));// 关闭套接字句柄::closesocket(s);// 释放套接字库    ::WSACleanup();return 0;}// 定义套接字地址结构sockaddr_in addr;// 初始化地址结构addr.sin_family = AF_INET;addr.sin_port = htons(37);addr.sin_addr.S_un.S_addr = inet_addr(ip_addr);// 连接if ( ::connect(s, (sockaddr*)&addr, sizeof(addr)) !=0 ){int errorCode = ::WSAGetLastError();switch(errorCode){case 10060:MessageBox(_T("连接超时!"));break;case 10051:MessageBox(_T("网络不可抵达!"));break;default:char temp[20];sprintf(temp, _T("WSAGetLastError()错误代码:%d"), errorCode);MessageBox(temp);}// 关闭套接字句柄::closesocket(s);// 释放套接字库    ::WSACleanup();return 0;}// 接收if ( ::recv(s, (char *)&m_serverTime, 4, MSG_PEEK) <= 0 ){MessageBox(_T("接收错误!"));// 关闭套接字句柄::closesocket(s);// 释放套接字库    ::WSACleanup();return 0;}// 关闭套接字句柄::closesocket(s);// 释放套接字库::WSACleanup();// 网络字节顺序转换为主机字节顺序m_serverTime = ::ntohl(m_serverTime);// 返回接收到的数据return m_serverTime;}


介绍另一个函数,用于将毫秒数(上述函数的返回值)转化为SYSTEMTIME型时间:

/************************************************************************//* 将从毫秒数转化为SYSTEMTIME                                           *//************************************************************************/SYSTEMTIME CGetNetworkTimeDlg::FormatServerTime(DWORD serverTime){ FILETIME      ftNew ;          SYSTEMTIME    stNew ;          stNew.wYear         = 1900 ;     stNew.wMonth        = 1 ;     stNew.wDay          = 1 ;     stNew.wHour         = 0 ;     stNew.wMinute       = 0 ;     stNew.wSecond       = 0 ;     stNew.wMilliseconds = 0 ; ::SystemTimeToFileTime (&stNew, &ftNew);     /*  将SYSTEMTIME结构设定为1900年1月1日午夜(0时)。 并将这个SYSTEMTIME结构传递给SystemTimeToFileTime,将此结构转化为FILETIME结构。 FILETIME实际上只是由两个32位元的DWORD一起组成64位元的整数, 用来表示从1601年1月1日至今间隔为100奈秒(nanosecond)的间隔数。 */      LARGE_INTEGER li ;//64位大整数     li = * (LARGE_INTEGER *) &ftNew;     li.QuadPart += (LONGLONG) 10000000 * serverTime;      ftNew = * (FILETIME *) &li;     ::FileTimeToSystemTime (&ftNew, &stNew); // 返回时间(注意:这里返回的是格林尼治时间,与北京时间相差8小时) return stNew;}


说明一点:那就是这样校时存在一定的误差,误差的范围很小,取决于网络延迟,要解决这个问题,可以设置一个计时器,取得网络延迟,加到获得的时间数据后面。


另外,再介绍两个API函数,用于设置时间,即SetLocalTime和SetSystemTime:

函数原型:BOOL WINAPI SetLocalTime( _In_ constSYSTEMTIME *lpSystemTime);函数功能: 设置当前本地时间及日期。 参数: lpSystemTime 一个SYSTEMTIME结构的指针,包含了新的本地日期和时间。SYSTEMTIME 结构wDayOfWeek成员被忽略。返回值: 如果函数调用成功,则返回值为非零值。如果函数失败,返回值是零。 为了得到扩展的错误信息,调用GetLastError函数 

函数原型:BOOL SetSystemTime(CONST SYSTEMTIME *lpSystemTime);函数功能:此函数设置当前系统的时间和日期参数说明:lpSystemTime:指向一个SYSTEMTIME数据结构.它接收当前系统的日期和时间.


总之,setsysemtime中的时间是格林尼治时间,setlocaltime中的是本地时间。

最后,为了演示方便加一个按钮,响应函数为:

void CGetNetworkTimeDlg::OnButton1() {// TODO: Add your control notification handler code hereSYSTEMTIME st = FormatServerTime( GetTimeFromServer("132.163.4.101") );// 校时SetSystemTime(&st); //SetLocalTime(&st); // 用这个差八小时CString m_Time;m_Time.Format(_T("格林尼治时间为:%d年%d月%d日%d时%d分%d秒%d"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);MessageBox(m_Time);m_Time.Format(_T("北京时间为:%d年%d月%d日%d时%d分%d秒%d"), st.wYear, st.wMonth, st.wDay, st.wHour+8, st.wMinute, st.wSecond, st.wMilliseconds);MessageBox(m_Time);}



下面演示程序:

第一步,将系统时间改为一个错误值,如图:





第二步,运行程序,点击校时按钮,如图:



结果,如图:










另附:程序源代码:http://download.csdn.net/detail/wwkaven/7531151





1 0
原创粉丝点击