這兩天有一個應用需要獲取網絡時間,雖然一直知道可以從時間服務器獲取時間,卻從來也沒有操作過,借這個機會重新進行一下深入了了解。
基本的思路就是:通過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);
}