前面写过一篇文章,也提到了校时,但是那篇文章的侧重点在于如何利用套接字获取网络时间服务器的实现信息。这里将那篇文章提到的函数放到一个类中,并考虑了网络延迟和程序假死。

    下面是获取网络时间的类:

头文件:GetNetworkTime.h


// GetNetworkTime.h: interface for the GetNetworkTime class.
//
//

#if !defined(AFX_GETNETWORKTIME_H__74508B77_CDAE_4D84_8DED_B880E595742E__INCLUDED_)
#define AFX_GETNETWORKTIME_H__74508B77_CDAE_4D84_8DED_B880E595742E__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000


// 时间同步服务器IP地址列表
static char *ip_addr_table[15] = {	
	"132.163.4.101",
	"132.163.4.102",
	"132.163.4.103",
	"128.138.140.44",
	"129.6.15.28",
	"129.6.15.29",
	"192.43.244.18",
	"131.107.1.10",
	"66.243.43.21",
	"216.200.93.8",
	"208.184.49.9",
	"207.126.98.204",
	"207.200.81.113",
	"205.188.185.33",
	"210.72.145.44"
};


class GetNetworkTime  
{
public:

	int m_networkLatency;  // 网络延迟,单位毫秒

	SYSTEMTIME FormatServerTime(DWORD serverTime);  // 将从时间同步服务器得到的毫秒数转化为标准时间
	DWORD GetTimeFromServer(char * ip_addr);        // 从时间同步服务器获取时间信息
	DWORD GetTimeFromServer();

	GetNetworkTime();
	virtual ~GetNetworkTime();

};

#endif // !defined(AFX_GETNETWORKTIME_H__74508B77_CDAE_4D84_8DED_B880E595742E__INCLUDED_)


CPP文件:GetNetworkTime.cpp


// GetNetworkTime.cpp: implementation of the GetNetworkTime class.
//
//

#include "stdafx.h"
#include "GetNetworkTime.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

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



//
// Construction/Destruction
//

GetNetworkTime::GetNetworkTime()
{
	m_networkLatency = 0; // 初始值为0
}

GetNetworkTime::~GetNetworkTime()
{

}


/************************************************************************/
/* 从时间同步服务器获取时间信息                                         */
/************************************************************************/
DWORD GetNetworkTime::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(NULL, _T("初始化套接字库失败!"), _T("提示"), MB_OK | MB_ICONWARNING);
		return 0;
	}

	// 定义连接套接字句柄
	SOCKET s;

	// 定义接收信息保存变量
	DWORD  m_serverTime;

	// 创建TCP套接字
	s = ::socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == s)
	{
		//MessageBox(NULL, _T("创建套接字失败!"), _T("提示"), MB_OK | MB_ICONWARNING);

		// 关闭套接字句柄
		::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);

	// 设置计时函数,用于计算延迟
	clock_t start,finish;
    clock_t totaltime;
	start = clock();

	// 连接
	if ( ::connect(s, (sockaddr*)&addr, sizeof(addr)) !=0 )
	{
		int errorCode = ::WSAGetLastError();
		switch(errorCode)
		{
		case 10060:
			//MessageBox(NULL, _T("连接超时!"), _T("提示"), MB_OK | MB_ICONWARNING);
			break;
		case 10051:
			//MessageBox(NULL, _T("网络不可抵达!"), _T("提示"), MB_OK | MB_ICONWARNING);
			break;
		default:
			char temp[20];
			sprintf(temp, _T("WSAGetLastError()错误代码:%d"), errorCode);
			//MessageBox(NULL, temp, _T("提示"), MB_OK | MB_ICONWARNING);
		}

		// 关闭套接字句柄
		::closesocket(s);
		// 释放套接字库
	    ::WSACleanup();

		return 0;
	}

	// 接收
	if ( ::recv(s, (char *)&m_serverTime, 4, MSG_PEEK) <= 0 )
	{
		//MessageBox(NULL, _T("接收错误!"), _T("提示"), MB_OK | MB_ICONWARNING);
		
		// 关闭套接字句柄
		::closesocket(s);
		// 释放套接字库
	    ::WSACleanup();

		return 0;
	}

	// 关闭计时器
	finish=clock();
	totaltime=(finish-start)/CLOCKS_PER_SEC; // 转化为秒数


	// 关闭套接字句柄
	::closesocket(s);

	// 释放套接字库
	::WSACleanup();

	// 网络字节顺序转换为主机字节顺序
	m_serverTime = ::ntohl(m_serverTime);

	// 加上网络延迟
	m_serverTime += totaltime;

	// 返回接收到的数据
	return m_serverTime;
}



/************************************************************************/
/* 将从毫秒数转化为SYSTEMTIME                                           */
/************************************************************************/
SYSTEMTIME GetNetworkTime::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;
}


/************************************************************************/
/* 根据ip表获取网络时间                                                 */
/************************************************************************/
DWORD GetNetworkTime::GetTimeFromServer()
{
	DWORD m_serverTime;

	for (int i=0; i<sizeof(ip_addr_table); i++)
	{
		m_serverTime = GetTimeFromServer(ip_addr_table[i]);
		if ( m_serverTime != 0)
		{
			return m_serverTime;
		}
	}

	return m_serverTime;
}



下面是主对话框中实现校时功能,为了防止程序假死,设置一个新线程:

声明:


HANDLE h1; // 线程句柄
DWORD WINAPI GetTimeFun (LPVOID lpParameter);  // 声明线程函数


创建:


/************************************************************************/
/* 校时按钮的响应函数                                                   */
/************************************************************************/
void CTimingShutdownDlg::OnBtnGettime() 
{
	// 为防止程序假死,创建一个新线程用于执行校时操作
	h1 = ::CreateThread(NULL, 0, GetTimeFun, NULL, 0, NULL);
}



线程函数的实现:


/************************************************************************/
/* 校时线程                                                             */
/************************************************************************/
DWORD WINAPI GetTimeFun(LPVOID lpParameter)
{
	if (6 == ::MessageBox(NULL, _T("是否进行网络校时?"), _T("提示"), MB_YESNO|MB_ICONQUESTION))
	{
		GetNetworkTime getNetTime;
		DWORD tempSeconds = getNetTime.GetTimeFromServer();
		if (0 == tempSeconds)
		{
			::MessageBox(NULL, _T("当前网络不可用,校时失败!"), _T("提示"), MB_OK|MB_ICONWARNING);
		}
		else
		{
			// 获取网络时间
			SYSTEMTIME net_st = getNetTime.FormatServerTime(tempSeconds);
			// 设置系统时间
			if (TRUE == SetSystemTime(&net_st))
			{
				::MessageBox(NULL, _T("校时成功!"), _T("提示"), MB_OK|MB_ICONWARNING);
			}
			else
			{
				::MessageBox(NULL, _T("校时失败!"), _T("提示"), MB_OK|MB_ICONWARNING);
			}
		}
	}
	
	// 校时完成后关闭线程
	::CloseHandle(h1);
	
	return 0;
}


另外,

别忘了在主对话框中引入头文件:

#include "GetNetworkTime.h"