NTP服務(wù)器
1、NTP服務(wù)器
NTP協(xié)議是網(wǎng)絡(luò)時間協(xié)議(Network Time Protocol),它是用來同步網(wǎng)絡(luò)中各個計算機的時間的協(xié)議。它的用途是把計算機的時鐘同步到世界協(xié)調(diào)時UTC,其精度在局域網(wǎng)內(nèi)可達(dá)0.1ms,在互聯(lián)網(wǎng)上絕大多數(shù)的地方其精度可以達(dá)到1-50ms。它可以使計算機對其服務(wù)器或時鐘源(如石英鐘,GPS等等)做同步化,它可以提供高精準(zhǔn)度的時間校正(LAN上與標(biāo)準(zhǔn)間差小于1毫秒,WAN上幾十毫秒),且可介由加密確認(rèn)的方式來防止惡毒的協(xié)議攻擊。時間按NTP服務(wù)器的等級傳播。按照離外部UTC源的遠(yuǎn)近把所有服務(wù)器歸入不同的Stratum(層)中。
NTP要提供準(zhǔn)確的時間,就必須有準(zhǔn)確的時間來源,那可以用格林尼治時間嗎?答案是否定的。因為格林尼治時間是以地球自轉(zhuǎn)為基礎(chǔ)的時間計量系統(tǒng),但是地球每天的自轉(zhuǎn)是有些不規(guī)則的,而且正在緩慢加速,因此,格林尼治時間已經(jīng)不再被作為標(biāo)準(zhǔn)時間使用。
新的標(biāo)準(zhǔn)時間,是由原子鐘報時的國際標(biāo)準(zhǔn)時間UTC(Universal Time Coordinated,世界協(xié)調(diào)時)。所以NTP獲得UTC的時間來源可以是原子鐘、天文臺、衛(wèi)星,也可以從Internet上獲取。
有了準(zhǔn)確而可靠的的時間源,那這個時間如何傳播呢?
計算機主機一般同多個時間服務(wù)器連接, 利用統(tǒng)計學(xué)的算法過濾來自不同服務(wù)器的時間,以選擇最佳的路徑和來源來校正主機時間。即使主機在長時間無法與某一時間服務(wù)器相聯(lián)系的情況下,NTP服務(wù)依然有效運轉(zhuǎn)。
為防止對時間服務(wù)器的惡意破壞,NTP使用了識別(Authentication)機制,檢查來對時的信息是否是真正來自所宣稱的服務(wù)器并檢查資料的返回路徑,以提供對抗干擾的保護(hù)機制。
2、UTC
協(xié)調(diào)世界時,即格林威治平太陽時間,又稱世界統(tǒng)一時間、世界標(biāo)準(zhǔn)時間、國際協(xié)調(diào)時間。由于英文(CUT)和法文(TUC)的縮寫不同,作為妥協(xié),簡稱UTC。
協(xié)調(diào)世界時是以原子時秒長為基礎(chǔ),在時刻上盡量接近于世界時的一種時間計量系統(tǒng)。中國大陸采用ISO 8601-1988的《數(shù)據(jù)元和交換格式信息交換日期和時間表示法》(GB/T 7408-1994)稱之為國際協(xié)調(diào)時間,代替原來的GB/T 7408-1994;中國臺灣采用CNS 7648的《資料元及交換格式–資訊交換–日期及時間的表示法》,稱之為世界統(tǒng)一時間。
這套時間系統(tǒng)被應(yīng)用于許多互聯(lián)網(wǎng)和萬維網(wǎng)的標(biāo)準(zhǔn)中,例如,網(wǎng)絡(luò)時間協(xié)議就是協(xié)調(diào)世界時在互聯(lián)網(wǎng)中使用的一種方式。
GMT(Greenwich Mean Time,格林威治時間)是英國格林尼治當(dāng)?shù)貢r間,它和UTC是的時間是一樣的,及GMT=UTC+0。
在軍事中,協(xié)調(diào)世界時區(qū)會使用“Z”來表示。又由于Z在無線電聯(lián)絡(luò)中使用“Zulu”作代稱,協(xié)調(diào)世界時也會被稱為"Zulu time"。中國大陸、中國香港、中國澳門、中國臺灣、蒙古國、新加坡、馬來西亞、菲律賓、西澳大利亞州的時間與UTC的時差均為+8,也就是UTC+8。

3、NTP報文格式
NTP是從時間協(xié)議(Time Protocol)和ICMP時間戳報文(ICMP TimeStamp Message)演變而來,在準(zhǔn)確性和健壯性方面進(jìn)行了特殊的設(shè)計,理論上精度可達(dá)十億分之一秒。
NTP協(xié)議應(yīng)用于分布式時間服務(wù)器和客戶端之間,實現(xiàn)客戶端和服務(wù)器的時間同步,從而使網(wǎng)絡(luò)內(nèi)所有設(shè)備的時鐘基本保持一致。
NTP協(xié)議是基于UDP進(jìn)行傳輸?shù)模褂枚丝谔枮?23。

| 字段名 | 長度 | 含義 |
|---|---|---|
| LI(Leap Indicator) | 2比特 | 這是一個兩位的代碼,表示在NTP時間標(biāo)尺中將要插入的下一跳情況。值為“11”時表示告警狀態(tài),時鐘不能被同步。 |
| VN(Version Number) | 3比特 | NTP的版本號。 |
| Mode | 3比特 |
NTP的工作模式。不同值表示的含義如下: 0:reserved,保留。 1:symmetric active,主動對等體模式。 2:symmetric passive,被動對等體模式。 3:client,客戶模式。 4:server,服務(wù)器模式。 5:broadcast,廣播模式。 6:reserved for NTP control messages,NTP控制報文。 7:reserved for private use,內(nèi)部使用預(yù)留。 |
| Stratum | 8比特 | 時鐘的層數(shù),定義了時鐘的準(zhǔn)確度。層數(shù)為1的時鐘準(zhǔn)確度最高,從1到15依次遞減。 |
| Poll Interval | 8比特 | 輪詢時間,即發(fā)送報文的最小間隔時間。 |
| Precision | 8比特 | 時鐘的精度。 |
| Root Delay | 32比特 | 到主參考時鐘的總往返延遲時間。 |
| Root Dispersion | 32比特 | 本地時鐘相對于主參考時鐘的最大誤差。 |
| Reference Identifier | 32比特 | 標(biāo)識特定參考時鐘。 |
| Reference Timestamp | 64比特 | 本地時鐘最后一次被設(shè)定或更新的時間。如果值為0表示本地時鐘從未被同步過。 |
| Originate Timestamp | 64比特 | NTP報文離開源端時的本地時間。 |
| Receive Timestamp | 64比特 | NTP報文到達(dá)目的端的本地時間。 |
| Transmit Timestamp | 64比特 | 目的端應(yīng)答報文離開服務(wù)器端的本地時間。 |
| Authenticator | 96比特 | (可選)驗證信息。 |
| 字段名 | 長度 | 含義 |
|---|---|---|
| 0 | 2比特 | 保留位。NTP本身不做處理。 |
| VN(Version Number) | 3比特 | NTP的版本號,目前值為3。 |
| 6 | 3比特 | 表明是控制報文。 |
| REM | 3比特 |
R:0表示命令,1表示響應(yīng)。 E:0表示發(fā)送正常響應(yīng),1表示發(fā)送錯誤響應(yīng)。 M:0表示最后一個分片,1表示其他。 |
| Op | 5比特 | 操作碼,表明命令的類型。 |
| Sequence | 16比特 | 發(fā)送或接受到報文的順序號。 |
| Status | 16比特 | 表明當(dāng)前系統(tǒng)的狀態(tài)。 |
| Association ID | 16比特 | 連接標(biāo)示。 |
| Offset | 16比特 | 偏移量。 |
| Count | 16比特 | 數(shù)據(jù)域的長度。 |
| Data | 最大468比特 | 包括發(fā)送報文或接受報文中的數(shù)據(jù)信息。 |
| Padding | 16比特 | 填充字段。 |
| Authenticator | 96比特 | (可選)驗證信息。 |
//NTP的端口號 NTP_PORT 123 //常見的NTP的IP地址 uint8_t ntp_server_ip[4]={103,11,143,248}; //新加坡 uint8_t ntp_server_ip[4]={133,243,238,243}; //日本東京 uint8_t ntp_server_ip[4]={85,199,214,100}; //英國 uint8_t ntp_server_ip[4] = {202,120,2,101}; //上海交通大學(xué)ntp服務(wù)器IP地址202.120.2.101 uint8_t ntp_server_ip[4] = {210,72,145,44}; //國家授時中心服務(wù)器IP地址 210,72,145,44 uint8_t ntp_server_ip[4] = {202,118,1,81}; //遼寧省沈陽市 教育網(wǎng) uint8_t ntp_server_ip[4]={203,107,6,88}; //阿里云 uint8_t ntp_server_ip[4]={120,25,115,20}; //阿里云 uint8_t ntp_server_ip[4]={120,25,108,11}; //阿里云
//報文結(jié)構(gòu)體 typedef struct NTP_MessageTypeDef { unsigned int leap : 2;/* leap indicator */ unsigned int version : 3;/* version number ntp版本號*/ unsigned int mode : 3;/* mode ntp工作模式*/ unsigned int stratum : 8;/* stratum 時鐘層數(shù)*/ unsigned int poll : 8;/* poll interval 輪詢時間*/ unsigned int precision : 8;/* precision 時鐘的精度*/ unsigned int rootdelay; /* root delay 到主參考時鐘的總往返延遲時間*/ unsigned int rootdisp; /* root dispersion 本地時鐘相對于主參考時鐘的最大誤差*/ unsigned int refid; /* reference ID 標(biāo)識特定參考時鐘*/ unsigned long long reftime; /* reference time 本地時鐘最后一次被設(shè)定或更新的時間*/ unsigned long long oritime; /* origin timestamp NTP報文離開源端時的本地時間*/ unsigned long long rectime; /* receive timestamp NTP報文到達(dá)目的端的本地時間*/ unsigned long long tratime; /* transmit timestamp 目的端應(yīng)答報文離開服務(wù)器端的本地時間*/ unsigned int Authenticator[3]; /* Authenticator (可選)驗證信息 */ } NTP_MessageTypeDef;
Leap Indicator(LI) :閏秒指示符,這是一個2位的代碼,用于警示在當(dāng)天的最后一分鐘里插入或刪除的閏秒。取值如下: 0:無預(yù)告 1:最近一分鐘有61秒 2:最近一分鐘有59秒 3:警告狀態(tài)(時鐘未同步) Version Number(VN) :版本號,這是一個3位的整數(shù),用于表示NTP的版本。 Mode :模式,這是一個3位的整數(shù),表示模式,值定義如下: 0:保留 1:對稱主動 2:對稱被動 3:客戶端 4:服務(wù)器端 5:廣播 6:為NTP控制控制消息 7:為自用保留 Stratum :本地時鐘層級,這是一個八位無符號整數(shù),表示本地時鐘的層級,其值定義如下: 0:未定義或難以獲得 1:主要參考(如無線電時鐘鐘,校正的院子時鐘) 2-255:第二參考(通過NTP或SNTP) Poll :輪詢間隔,這是一個8位有符號整數(shù),用于表示連續(xù)消息之間的最大間隔,以最接近2的N次冪來表示。如值為6表示2^6=64。 Precision :本地時鐘精度精度,這是一個8位有符號整數(shù),用于表示本地時鐘精度,以最接近2的N次冪來表示。 Root Delay :這是一個32位有符號定點數(shù),表示主要參考源的總往返時延,以秒為單位。該變量可以為正值和負(fù)值,具體取決于時間精度和偏移。 Root Dispersion :這是一個32位有符號定點數(shù),表示相對于主參考源的最大誤差,以秒為單位,在15和16位之間。通常在該字段中出現(xiàn)的值范圍為0到幾百毫秒 Reference Identifier :這是一個標(biāo)識特定參考源的32位位串。在NTP版本3或版本4層級0或?qū)蛹?服務(wù)器的情況下,這是一個4字符ASCII字符串,左對齊并且以0填充到32位。在NTP版本3輔助服務(wù)器中,這是參考源的32位IPv4地址。 Reference Timestamp :這是以64位時間戳格式表示的上次設(shè)置或更正的本地時鐘時間。 Original Timestamp :這是以64位時間戳格式表示的請求離開客戶端的時間。 Receive Timestamp :這是以64位時間戳格式表示的請求到達(dá)服務(wù)器端的時間。 Transmit Timestamp :這是以64位時間戳格式表示的應(yīng)答離開服務(wù)器端的時間。 Authentication :認(rèn)證信息。



測試程序
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; #define NTP_PORT 123 //NTP的端口 #define SECS_PERDAY 86400UL // 一天的秒數(shù) day = 60*60*24 #define NTP_START_TIME_YEAR 1900 //微軟的時間戳是從1900.1.1算起,而unix的時間戳是從1970.1.1算起 #define FOUR_YEAR_DAYS (366+365+365+365) //4年一個周期內(nèi)的總天數(shù)(NTP_START_TIME_YEAR~2038不存在2100這類年份,故暫不優(yōu)化)1900開始:(366+365+365+365),但是1900不是閏年,計算完后要加1天 1970開始:(365+365+366+365) #define BUFFER_SIZE 1024 //緩沖區(qū)大小 //NTP服務(wù)器報文 typedef struct NTP_MessageTypeDef { unsigned int leap : 2;/* leap indicator */ unsigned int version : 3;/* version number ntp版本號*/ unsigned int mode : 3;/* mode ntp工作模式*/ unsigned int stratum : 8;/* stratum 時鐘層數(shù)*/ unsigned int poll : 8;/* poll interval 輪詢時間*/ unsigned int precision : 8;/* precision 時鐘的精度*/ unsigned int rootdelay; /* root delay 到主參考時鐘的總往返延遲時間*/ unsigned int rootdisp; /* root dispersion 本地時鐘相對于主參考時鐘的最大誤差*/ unsigned int refid; /* reference ID 標(biāo)識特定參考時鐘*/ unsigned long long reftime; /* reference time 本地時鐘最后一次被設(shè)定或更新的時間*/ unsigned long long oritime; /* origin timestamp NTP報文離開源端時的本地時間*/ unsigned long long rectime; /* receive timestamp NTP報文到達(dá)目的端的本地時間*/ unsigned long long tratime; /* transmit timestamp 目的端應(yīng)答報文離開服務(wù)器端的本地時間*/ unsigned int Authenticator[3]; /* Authenticator (可選)驗證信息 */ } NTP_MessageTypeDef; typedef struct NTP_TimeTypeDef { uint16_t year; //年 uint8_t month; //月 uint8_t date; //日 uint8_t week; //星期 uint8_t hour; //小時 uint8_t minute; //分鐘 uint8_t second; //秒鐘 unsigned long long time_s; //秒時間 }NTP_TimeTypeDef;
#include "ntp.h" static uint8_t peace_month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//平年每個月的天數(shù) static uint8_t leap_month_days[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; //閏年每個月的天數(shù) /************************************************************************************************ * @ 函 數(shù) 名:NTP_GenerateReqMessage * @ 功能說明:生成NTP服務(wù)器請求報文 * @ 參 數(shù):p_Message_Buf:報文緩沖區(qū) * @ 返 回 值:無 ************************************************************************************************/ static void NTP_GenerateReqMessage(uint8_t *p_Message_Buf) { NTP_MessageTypeDef NTP_MessageStruct; NTP_MessageStruct.leap = 0; NTP_MessageStruct.version = 4; NTP_MessageStruct.mode = 3; NTP_MessageStruct.stratum = 0; NTP_MessageStruct.poll = 0; NTP_MessageStruct.precision = 0; NTP_MessageStruct.rootdelay = 0; NTP_MessageStruct.rootdisp = 0; NTP_MessageStruct.refid = 0; NTP_MessageStruct.reftime = 0; NTP_MessageStruct.oritime = 0; NTP_MessageStruct.rectime = 0; NTP_MessageStruct.tratime = 1; NTP_MessageStruct.Authenticator[0] = 0; NTP_MessageStruct.Authenticator[1] = 0; NTP_MessageStruct.Authenticator[2] = 0; //memcpy((void *)p_Message_Buf, (void const*)(&NTP_MessageStruct), sizeof(NTP_MessageStruct)); uint8_t temp = 0; temp = (NTP_MessageStruct.leap << 6) + (NTP_MessageStruct.version << 3) + (NTP_MessageStruct.mode); p_Message_Buf[0] = 0x23; // 0x23 = NTP_MessageStruct.leap << 6 + NTP_MessageStruct.version << 3 + NTP_MessageStruct.mode return; } /************************************************************************************************ * @ 函 數(shù) 名:NTP_WeekCalculate * @ 功能說明:根據(jù)日期計算星期 * @ 參 數(shù):p_NTP_TimeStruct:時間結(jié)構(gòu)體 * @ 返 回 值:無 ************************************************************************************************/ static uint8_t NTP_WeekCalculate(NTP_TimeTypeDef *p_NTP_TimeStruct) { uint16_t year = 0; uint8_t month = 0; uint8_t date = 0; uint8_t week = 0; year = p_NTP_TimeStruct->year; month = p_NTP_TimeStruct->month; date = p_NTP_TimeStruct->date; if (month == 1 || month == 2) { month += 12; year--; } week = (date + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7; return (week + 1); } /************************************************************************************************ * @ 函 數(shù) 名:NTP_Leapyearjudgment * @ 功能說明:判斷是否是閏年 * @ 參 數(shù):Year:年 * @ 返 回 值:0: 平年 1:閏年 ************************************************************************************************/ static uint8_t NTP_Leapyearjudgment(uint16_t Year) { uint8_t ret = 0; if (Year % 400 == 0)//能被400整除的就是閏年 { ret = 1; } else { if (Year % 4 == 0 && Year % 100 != 0)//不能被400整除,但是能被4整除并且不能被100整除是閏年 { ret = 1; } else//不能被400整除,但是不能被4整除或者能被100整除是平年 { ret = 0; } } return ret; } /************************************************************************************************ * @ 函 數(shù) 名:NTP_DateToTimeStamp * @ 功能說明:日期轉(zhuǎn)換成時間戳 * @ 參 數(shù):NTP_TimeStruct:時間結(jié)構(gòu)體 * @ 返 回 值:時間戳 ************************************************************************************************/ uint32_t NTP_DateToTimeStamp(NTP_TimeTypeDef *p_NTP_TimeStruct) { uint16_t year_temp = NTP_START_TIME_YEAR, month_temp = 1; //時間戳的起始時間 uint32_t day_num = 0, second_num = 0; //保存當(dāng)前時間到時間戳起始時間的天數(shù) //1.計算從NTP_START_TIME_YEAR年到當(dāng)前時間前一年的總天數(shù) 如當(dāng)前時間為2021.6.15 12.30.30,就計算從NTP_START_TIME_YEAR年到2020年的總天數(shù) while (year_temp < p_NTP_TimeStruct->year) { if (NTP_Leapyearjudgment(year_temp))//判斷是否為閏年 { day_num += 366;//閏年366天 } else { day_num += 365;//平年365天 } year_temp++; } //2.計算當(dāng)前年份的1月到前一個月的總天數(shù) 如當(dāng)前時間為2021.6.15 12.30.30,就計算從2021年1月到5月的總天數(shù) while (month_temp < (p_NTP_TimeStruct->month)) { if (NTP_Leapyearjudgment(p_NTP_TimeStruct->year)) //判斷當(dāng)前年是否為閏年 { day_num += leap_month_days[month_temp - 1];//閏年各個月的天數(shù) } else { day_num += peace_month_days[month_temp - 1];//平年各個月的天數(shù) } month_temp++; } //3.計算當(dāng)前月份的1號到前一天的天數(shù) 如當(dāng)前時間為2021.6.15 12.30.30,就計算從2021.6.1到2021.6.14的總天數(shù)為14天 day_num += (p_NTP_TimeStruct->date - 1); //4.計算當(dāng)前時分秒 second_num = day_num * 24 * 60 * 60; //從1900/1/1 00:00:00到當(dāng)前時間前一天的總秒數(shù) second_num += p_NTP_TimeStruct->hour * 60 * 60; second_num += p_NTP_TimeStruct->minute * 60; second_num += p_NTP_TimeStruct->second; return second_num; } /************************************************************************************************ * @ 函 數(shù) 名:NTP_TimeStampToDate * @ 功能說明:時間戳轉(zhuǎn)換成日期 * @ 參 數(shù):Time_Stamp:時間戳 NTP_TimeStruct:時間結(jié)構(gòu)體 * @ 返 回 值:無 ************************************************************************************************/ void NTP_TimeStampToDate(uint32_t Time_Stamp, NTP_TimeTypeDef *p_NTP_TimeStruct) { uint32_t totle_day_num = 0, totle_second_num = 0; uint16_t remain_day_of_year = 0; uint16_t year_temp = 0; uint8_t *p_str = NULL; totle_day_num = (Time_Stamp) / (24 * 60 * 60); //計算過去的總天數(shù)(注意加括號) totle_second_num = (Time_Stamp) % (24 * 60 * 60); //計算當(dāng)天過去的秒數(shù) memset((void *)p_NTP_TimeStruct, 0x00, sizeof(NTP_TimeTypeDef)); //1.先計算時分秒 HH:MM:SS p_NTP_TimeStruct->hour = totle_second_num / 3600; p_NTP_TimeStruct->minute = (totle_second_num % 3600) / 60; p_NTP_TimeStruct->second = (totle_second_num % 3600) % 60; //2.計算哪一年 p_NTP_TimeStruct->year = NTP_START_TIME_YEAR + (totle_day_num / FOUR_YEAR_DAYS) * 4; //4年為一個周期,先計算過了幾個4年周期,比如當(dāng)前為2021,計算出來就是2020,那么就是從NTP_START_TIME_YEAR~2019這120年 remain_day_of_year += totle_day_num % FOUR_YEAR_DAYS; //4年一個周期剩余的天數(shù) if (NTP_START_TIME_YEAR == 1900) //因為1900~1903連續(xù)四年都是平年,所以如果是從1900年開始計算,計算完后得重新加1天 { remain_day_of_year++; } year_temp = NTP_Leapyearjudgment(p_NTP_TimeStruct->year) ? 366 : 365;//計算當(dāng)前年的天數(shù) while (remain_day_of_year >= year_temp) // 判斷剩余的天數(shù)滿不滿一年的天數(shù) { p_NTP_TimeStruct->year++; remain_day_of_year -= year_temp; year_temp = NTP_Leapyearjudgment(p_NTP_TimeStruct->year) ? 366 : 365; } //3.計算哪一月的哪一天 p_str = NTP_Leapyearjudgment(p_NTP_TimeStruct->year) ? leap_month_days : peace_month_days; remain_day_of_year++; // 這里開始計算具體日期,remain_day_of_year為 0 時其實是 1 號,所以這里要先 +1 while (remain_day_of_year > *(p_str + p_NTP_TimeStruct->month)) { remain_day_of_year -= *(p_str + p_NTP_TimeStruct->month); p_NTP_TimeStruct->month++; } p_NTP_TimeStruct->month++; p_NTP_TimeStruct->date = remain_day_of_year; p_NTP_TimeStruct->week = NTP_WeekCalculate(p_NTP_TimeStruct); } /************************************************************************************************ * @ 函 數(shù) 名:NTP_GetMessageTime * @ 功能說明:從NTP回復(fù)的報文中獲取時間 * @ 參 數(shù):p_Message_Buf:報文緩沖區(qū) Time_Start_Addr:報文中時間的起始位置 p_NTP_TimeStruct:時間結(jié)構(gòu)體 * @ 返 回 值: ************************************************************************************************/ static void NTP_GetMessageTime(uint8_t* p_Message_Buf, uint16_t Time_Start_Addr, NTP_TimeTypeDef *p_NTP_TimeStruct) { unsigned long long second = 0; for (uint8_t i = 0; i < 4; i++) { second = (second << 8) | p_Message_Buf[Time_Start_Addr + i]; } second += 8 * 3600; p_NTP_TimeStruct->time_s = second; } int main() { WSADATA WSAData; SOCKET client_socket; //客戶端的Socket SOCKADDR_IN server_addr; //服務(wù)器的地址數(shù)據(jù)結(jié)構(gòu) SOCKADDR_IN sock; uint8_t send_buf[BUFFER_SIZE]; //發(fā)送數(shù)據(jù)的緩沖區(qū) uint8_t rece_buf[BUFFER_SIZE]; //接受數(shù)據(jù)的緩沖區(qū) NTP_TimeTypeDef NTP_TimeStruct; if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) { printf("初始化失敗!"); return -1; } //創(chuàng)建客戶端的Socket client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);//創(chuàng)建客戶端的Socket if (client_socket == INVALID_SOCKET) { printf("創(chuàng)建套接字失敗!"); } /* 設(shè)置阻塞超時 */ struct timeval timeOut; timeOut.tv_sec = 5; //設(shè)置5s超時 timeOut.tv_usec = 0; if (setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &timeOut, sizeof(timeOut)) < 0) { printf("time out setting failed\n"); } //配置服務(wù)器的IP地址和端口號 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(123);//端口號為4567 server_addr.sin_addr.s_addr = inet_addr("120.25.108.11"); //127.0.0.1為本電腦IP地址 int len = sizeof(sock); while (1) { uint8_t message_buf[48] = { 0 }; memset(&rece_buf, 0, sizeof(rece_buf)); NTP_GenerateReqMessage(message_buf); sendto(client_socket, message_buf, sizeof(message_buf), 0, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)); int recv_len = recvfrom(client_socket, rece_buf, sizeof(rece_buf), 0, (SOCKADDR*)&sock, &len); if (recv_len >= 48)//接收到服務(wù)器返回的數(shù)據(jù) { for (int i = 0; i < recv_len; i++) { printf("%02x ", rece_buf[i]); } printf("\r\n"); printf("\r\n"); NTP_GetMessageTime(rece_buf, 40, &NTP_TimeStruct); //從回復(fù)的報文中獲取時間,回復(fù)數(shù)據(jù)包中時間的首地址為40 NTP_TimeStampToDate(NTP_TimeStruct.time_s, &NTP_TimeStruct); //由UTC時間計算日期 unsigned int time_temp = NTP_DateToTimeStamp(&NTP_TimeStruct); printf("%04d-%02d-%02dT%02d:%02d:%02d", NTP_TimeStruct.year, NTP_TimeStruct.month, NTP_TimeStruct.date, NTP_TimeStruct.hour, NTP_TimeStruct.minute, NTP_TimeStruct.second); printf("\r\n"); printf("\r\n"); printf("%u", time_temp); printf("\r\n"); printf("\r\n"); } Sleep(5000); } closesocket(client_socket); WSACleanup(); return 0; }

浙公網(wǎng)安備 33010602011771號