獲取網絡時間

這兩天有一個應用需要獲取網絡時間,雖然一直知道可以從時間服務器獲取時間,卻從來也沒有操作過,借這個機會重新進行一下深入了了解。

基本的思路就是:通過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);
}




發佈了59 篇原創文章 · 獲贊 43 · 訪問量 58萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章