这两天有一个应用需要获取网络时间,虽然一直知道可以从时间服务器获取时间,却从来也没有操作过,借这个机会重新进行一下深入了了解。
基本的思路就是:通过SOCKET连接时间服务器,直接接收从服务器发送的过来的时间数据。
void GetNetTime()
{
TIME_ZONE_INFORMATION tzinfo;
DWORD dwStandardDaylight;
int nRet;
/* Initialize Winsock */
WORD wVersionRequested;
WSADATA wsaData;
int nErrCode;
wVersionRequested = MAKEWORD(2, 2);
nErrCode = WSAStartup(wVersionRequested, &wsaData);
if (0 != nErrCode)
{
return;
}
/* Get server IP */
struct hostent *host;
char *pServerIP;
host = gethostbyname("time.nist.gov");
if (NULL == host)
{
return -1;
}
pServerIP = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
/* Connect to time server, and get time */
SOCKET sockfd;
char cTimeBuf[40] = { 0 };
unsigned long ulTime = 0;
int nTry = 0;
do
{
if (5 == nTry++)
{
return -1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockfd)
{
continue;
}
int TimeOut = 3000;//设置接收超时6秒
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut));
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(37);
addr.sin_addr.s_addr = inet_addr(pServerIP);
nRet = connect(sockfd, (sockaddr *)&addr, sizeof(addr));
if (SOCKET_ERROR == nRet)
{
continue;
}
nRet = recv(sockfd, (char *)&ulTime, sizeof(ulTime), 0);
if ((SOCKET_ERROR != nRet) && (0 != nRet))
{
break;
}
int nErr = WSAGetLastError();
TRACE(_T("[%d]%s"), nErr, ConvertErrcodeToString(nErr));
closesocket(sockfd);
} while (1);
closesocket(sockfd);
unsigned long ulTimehl = ntohl(ulTime);
ConvertTime(ulTimehl);
}
void ConvertTime(unsigned long ulTime)
{
// Windows文件时间是一个64位的值,它是从1601年1月1日中午12:00到现在的时间间隔,
// 单位是1/10,000,000秒,即1000万分之1秒(100-nanosecond)
FILETIME ft;
SYSTEMTIME st;
// 首先将基准时间(1900年1月1日0点0分0秒0毫秒)转化为Windows文件时间
st.wYear = 1900;
st.wMonth = 1;
st.wDay = 1;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, &ft);
// 然后将Time Protocol使用的基准时间加上逝去的时间(ulTime)
LONGLONG *pLLong = (LONGLONG *)&ft;
/* 注意:
文件时间单位是1/1000 0000秒(即100ns),
需要将从时间服务器上获取的以秒为单位的ulTime做一下转换
*/
*pLLong += (LONGLONG) 10000000 * ulTime;
// 再将时间转化回来,更新系统时间
FileTimeToSystemTime(&ft, &st);
TRACE(_T("%04d%02d%02d %02d:%02d:%02d\n"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond );
return;
}
各地时差都不一致,可以根据GetTimeDiff函数计算当地时差,对上面的时间加以调整:
void GetTimeDiff()
{
float bias;
long sminute,shour;
/* 获取时区信息 */
dwStandardDaylight = GetTimeZoneInformation(&tzinfo); //获取时区与UTC的时间差 应该返回-8
if (dwStandardDaylight == TIME_ZONE_ID_INVALID) //函数执行失败
{
return;
}
/* 时差调整 */
bias = tzinfo.Bias;
if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) //标准时间有效
bias += tzinfo.StandardBias;
if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) //夏令时间
bias += tzinfo.DaylightBias;
shour = bias / 60;
sminute = fmod(bias, (float)60);
}