1. 程式人生 > >基於NTP協議網路校時

基於NTP協議網路校時

先來個NTP協議介紹
http://blog.163.com/yzc_5001/blog/static/2061963420121283050787/

關於時間戳的換算工具
http://tool.chinaz.com/Tools/unixtime.aspx#

下面是程式碼,C-Free + MinGW 編譯,須在工程連線引數裡新增 -lwsock32


#include <stdio.h>
#include <time.h>
#include <winsock2.h>

#define JAN_1970 0x83AA7E80      /* 2208988800    1970-1900 in seconds*/

typedef union
{
    int  time;
    char time_array[4];
} Timestamp;

typedef struct 
{
    char message_hdr[16];//報文頭部 
    char RefTimestamp[8];
    Timestamp T1;
    char T1_decimal[4]; //T1的小數部分 
    Timestamp T2;
    char T2_decimal[4]; //T2的小數部分 
    Timestamp T3;
    char T3_decimal[4]; //T3的小數部分 
} NTP_MESSAGE;

int main()
{
    WORD socketVersion = MAKEWORD(2,2);
    WSADATA wsaData; 
    if(WSAStartup(socketVersion, &wsaData) != 0)
    {
        return 0;
    }
    SOCKET sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    SOCKADDR_IN sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(123);
    sin.sin_addr.S_un.S_addr = inet_addr("198.123.30.132");
    int len = sizeof(sin);
    
    char sendData[] = { //構造報文
    0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xAF, 0x5F, 0xF5, 0x23, 0xD7, 0x08, 0x00
    };
    
    printf("Connect to NTP Server...\n");
    
    time_t T1=time(NULL);
    
    sendto(sclient, sendData, sizeof(sendData), 0, (SOCKADDR *)&sin, len);

    char recvData[48]={0};
    int ret = recvfrom(sclient, recvData, 48, 0, (SOCKADDR *)&sin, &len);
    if(ret > 0)
    {
        time_t T4=time(NULL);
        
        printf("Connect success!\n\n");
        printf("Now the local time is %s",ctime(&T4));
        
        NTP_MESSAGE *ntp_time=(NTP_MESSAGE *)recvData;
        
        /* 大小端變換 由於NTP以1900為始,需變換 */
        ntp_time->T2.time=ntohl(ntp_time->T2.time)-JAN_1970;//T2
        ntp_time->T3.time=ntohl(ntp_time->T3.time)-JAN_1970;//T3
        
        /* NTP報文的網路延時=(T4-T1)-(T3-T2) */
        time_t net_delay=(T4-T1)-(ntp_time->T3.time-ntp_time->T2.time);
        printf("Net delay is %ld second(s).\n",net_delay);
        
        /* 誤差補償秒數=((T2-T1)+(T3-T4))/2 */
        time_t Deviation=(((time_t)ntp_time->T2.time-T1)+((time_t)ntp_time->T3.time-T4))/2;
        printf("\nThe deviation is %d second(s).\n",Deviation);
        
        if(Deviation != 0)
        {
            printf("Reset the local time...\n");
            
            time_t now=time(NULL)+Deviation;
            struct tm *ptr;
            ptr=localtime(&now);
            char buffer[20]={0};
            
            sprintf(buffer,"date %d-%d-%d",ptr->tm_year+1900,ptr->tm_mon+1,ptr->tm_mday);
            system(buffer);
            
            sprintf(buffer,"time %d:%d:%d",ptr->tm_hour,ptr->tm_min,ptr->tm_sec);
            system(buffer);
            now=time(NULL);
            printf("Now the local time is %s",ctime(&now));
        }
        else //沒有誤差,無需校時 
        {
            printf("Here is no need to set local time.\n");
        }
    }

    closesocket(sclient);
    WSACleanup();
    system("pause");
    return 0;
}

由於使用API 設定時間在 win7 下需要特殊的許可權,故採用系統命令 date 和 time 設定,算是  不是辦法的辦法。
關於NTP協議,最開始給出的連結有具體的說明,在程式中,只需48個位元組的必須內容即可,其它內容屬可選項。

時間的小數部分拋棄不用,對於一般校時而言足夠了。

下面給個截圖