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

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

域名IP地址
time-a.nist.gov129.6.15.28
time-b.nist.gov129.6.15.29
time-a.timefreq.bldrdoc.gov132.163.4.101
time-b.timefreq.bldrdoc132.163.4.102
time-c.timefreq.bldrdoc.gov132.163.4.103
utcnist.colorado.edu128.138.140.44
time.nist.gov192.43.244.18
time-nw.nist.gov131.107.1.10
nist1.datum.com66.243.43.21
nist1-dc.glassey.com216.200.93.8
nist1-ny.glassey.com208.184.49.9
nist1-sj.glassey.com207.126.98.204
nist1.aol-ca.truetime.com207.200.81.113
nist1.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 here

	SYSTEMTIME 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





已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页