天天看點

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

1.定義

        SNTP(Simple Network Time Protocal簡單網絡時間協定),用于跨廣域網或區域網路同步時間的協定,具有較高的精确度(幾十毫秒)

這裡給出SNTP的詳細定義和解釋,以及基于C語言的SNTP用戶端實作代碼:SNTP code 實際測試過

小工具:IP查域名,位址;計算2個日期間的 間隔數值

1.1 SNTP與NTP差別:

1.SNTP協定相對于NTP,都是優化了網絡傳播延時的影響,但是沒有像NTP一樣考慮 備援伺服器 和 校正時鐘頻率誤差功能

2.NTP可跨平台和跨系統,精度較高,1-50ms精度;提供認證機制,安全機制高

3.SNTP是NTP簡化版本,精度在1s左右,SNTP通常與網絡葉子節點裝置進行時間同步

4.SNTP與NTP封包完全一緻

1.1.1 SNTP采用client - server模式,使用單點傳播/廣播:

1. 單點傳播是定期與SNTP server互動擷取時間差進行校時

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

2. 廣播是SNTP server定期向多點傳播位址發送時間資訊,client監聽多點傳播位址來擷取時間進行同步

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

1.2 SNTP校時原理:

T1,T2,T3,T4均是時間戳,用以計算各自的時間差

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

t1與t2之間的時間差(包在網絡上走了個來回):| [(T2-T1)-(T4-T3)] | / 2,此處的| | 表示取絕對值

資料包在網絡上的傳播時間: (T2-T1) + (T4-T3)

注意:NTP時間戳從1900年開始記秒數,而UNIX時間戳從1970年開始記秒數,即記錄到NTP結構體中的時間要加上JAN_1970(1900到1970共70年的秒數),而從起擷取出來需減掉JAN_1970

1.2.1 Demo例子:

DeviceA與DeviceB在時間同步前,A:10:00:00am,B:11:00:00am,将B作為timeserver來校準A的時間,假設NTP封包在A和B之間單向傳輸所需要的時間為1s,B接收到發送出去也需要1s

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

A發NTP封包至B,帶有10:00:00的時間戳,T1=10:00:00am;

NTP封包到達B後,B加上時間戳T2=11:00:01am;

NTP封包離開B時,B再加上時間戳T3=11:00:02am;

A收到NTP封包後,加上A本地的時間戳T4=10:00:03;

A與B時間差:[(T2-T1)-(T4-T3)]/2=1h

資料包在網絡上的傳播時間: (T2-T1) + (T4-T3)=2s

是以,client的真正的時間計算應該是 T3 + [(T4-T1)-(T3-T2)] / 2 = T3 + [(T2-T1)+(T4-T3)] / 2,即:T3 server 的時間戳 + 網絡單程時延的數值即可

或者是 T4 + [(T2-T1)+(T3-T4)] / 2,不過還是上面的以T3來算的公式是最簡明,最符合直覺上的邏輯

1.3 NTP封包格式:

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

LI:目前時間閏秒标志。字段長度為2位整數,隻在伺服器端有效

0:無警告  1:最後一分鐘是61秒;
2:最後一分鐘是59秒; 3:警告(時鐘沒有同步)

VN:表示NTP的版本号,長度為3比特,可以是3或者是4

0:保留 1:對稱主動 2:對稱被動 3:客戶
4:伺服器 5:廣播 6:保留為NTP控制資訊 7:保留為使用者定義

stratum:指從stratum-1時間伺服器(如從GPS取得的标準時間所存儲的server) 至 client所經過的server個數,類似于路由器經過的跳數,數目越小,精度越高;隻在伺服器端有效,字段長度為8個比特(unsigned char型)

0:故障資訊 1:一級伺服器 2-15:二級伺服器 6-255:保留

Poll Interval:訓示資料包的最大時間間隔,以秒為機關,作為2的指數方的指數部分,該字段隻在伺服器端有效。字段長度為8位整數,取值範圍從4-17,即16秒到131,072秒

Precision:訓示系統時鐘的精确性,以秒為機關,作為2的指數方的指數部分,該字段隻在伺服器端有效。字段長度為8位有符号整數,取值範圍從-6到-20

Root Delay:訓示與主時鐘參考源的總共往返延遲,以秒為機關,該字段隻在伺服器端有效。字段長度為32位浮點數,小數部分在16位以後,取值範圍從負幾毫秒到正幾百毫秒

Root Dispersion:訓示與主時鐘參考源的誤差,以秒為機關,該字段隻在伺服器端有效。字段長度為32位浮點數,小數部分在16位以後,取值範圍從零毫秒到正幾百毫秒

Reference Identifier:訓示時鐘參考源的标記,該字段隻在伺服器端有效。對于一級伺服器,字段長度為4位元組ASCII字元串,左對齊不足添零。對于二級伺服器,在IPV4環境下,取值為一級伺服器的IP位址,在IPV6環境下,是一級伺服器的NSAP位址

Reference Timestamp:訓示系統時鐘最後一次校準的時間,該字段隻在伺服器端有效,以前面所述64位時間戳格式表示

Originate Timestamp:訓示客戶向伺服器發起請求的時間,以前面所述64位時間戳格式表示

lReceive Timestamp:指伺服器收到客戶請求的時間 ,以前面所述64位時間戳格式表示。

Transmit Timestamp:訓示伺服器向客戶發時間戳的時間,以前面所述64位時間戳格式表示

//Authenticator(可選):當需要進行SNTP認證時,該字段包含密鑰和資訊加密碼

2. code結果測試驗證

2.1 用wireshark抓到UE發出去的封包

UE隻發了個Transmit timestamp這個時間戳給server

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

2.2 Server回複的SNTP封包

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

2.3 記錄T1、T2、T3、T4各個時間點的數值

T1 = 1970.1.1 00:00:50.550999998

T2 = 2020.8.31 06:30:12.591166208 + 8h = 2020.8.31 14:30:12.591166208

T3 = 2020.8.31 06:30:12.591192346 + 8h = 2020.8.31 14:30:12.591192346

T4 = 根據tick得出ticks數值

然後 UE做列印,其公式為:T3 + [(T4-T1)-(T3-T2)] / 2,

整數數值:1598884212

毫秒數值:718953

該數值為基準時間,即擷取tick那一刻,對應的絕對時間相對于1970.1.1 00:00:00過了1598884212s,718953ms

下面來分析下如何判斷所的數值的正确性,注意:隻能大緻比較,因為100%正确的絕對時間是得不出來的,隻能用上面的算法得出相對準确的時間進行同步。

網絡單程時延:[(T4-T1)-(T3-T2)] / 2 = 127761ms

将T3的時間戳輸入網頁時間計算工具,得出的秒數差:1598884212.5,加上單程的網絡時延,得到1598884212.718953,秒數的能從wireshark中得到驗證,但毫秒數,由于計算得到的網絡時延= 上行網絡時延+下行網絡時延,這裡是将其兩者看作等價的,是以直接除以2得到單程網絡時延,是以在毫秒數值上有些許差異,很微小

SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證
SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

開發中需注意點:

  1. 網絡大端,UE小端的問題
  2. 得出UE與server之間時間差,還需+東八區8h時差,+UE開機的時間,再加上單程的網絡時延,就是目前tick對應 北京标準時間 從1970.1.1 00:00:00所經過的時長數值
  3. 這段代碼,按照最直接的思路:
    SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證
    4.在第一次擷取ticks後,需要FRAC2USEC(tv.tv_usec)一下,再轉成大端發給server,因為: 2^32 /10^6,表示sntp_transmit_timestamp.fracpart這裡面,每1ms, frapart=2^32 /10^6

附加:Python代碼

#/usr/local/bin/python3.5
#coding:utf-8

import socket, struct, time
NTP_server = "0.uk.pool.ntp.org"
TIME1970 = 2208988800

def sntp_client():
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = "\x1b" + 47 * "\0"
    client.sendto(data.encode('utf-8'), (NTP_server, 123))
    data, address = client.recvfrom(1024)
    
    if data:
        print("Response received from:", address)
    t = struct.unpack('!12I', data)[10]
    t -= TIME1970
    print('\tTime=%s' % time.ctime(t))
     
sntp_client()
           
SNTP原理講解 用戶端 C語言實作1.定義2. code結果測試驗證

繼續閱讀