Ekri yon senp kliyan NTP

Bonjou, Habrausers. Jodi a mwen vle pale sou ki jan yo ekri pwòp kliyan NTP senp ou. Fondamantalman, konvèsasyon an pral vire nan estrikti a nan pake a ak metòd la nan pwosesis repons lan soti nan sèvè NTP la. Kòd la pral ekri nan Python, paske li sanble ke pa gen tou senpleman pa gen pi bon lang pou bagay sa yo. Amater yo pral sonje resanblans kòd la ak kòd ntplib la - mwen te "enspire" pa li.

Se konsa, ki sa egzakteman se NTP? NTP se yon pwotokòl pou entèraksyon ak serveurs tan egzak. Pwotokòl sa a itilize nan anpil machin modèn. Pou egzanp, sèvis la w32tm nan fenèt yo.

Gen 5 vèsyon pwotokòl NTP an total. Premye a, vèsyon 0 (1985, RFC958)), kounye a konsidere kòm demode. Koulye a, plus yo itilize, 1ye (1988, RFC1059), 2yèm (1989, RFC1119), 3yèm (1992, RFC1305) ak 4yèm (1996, RFC2030). Vèsyon 1-4 yo konpatib youn ak lòt; yo diferan sèlman nan algoritm operasyon sèvè yo.

Fòma pake

Ekri yon senp kliyan NTP

Leap endikatè (endikatè koreksyon) - yon nimewo ki endike yon avètisman sou dezyèm kowòdinasyon an. Siyifikasyon:

  • 0 - pa gen okenn koreksyon
  • 1 - dènye minit nan jounen an gen 61 segonn
  • 2 - dènye minit nan jounen an gen 59 segonn
  • 3 - malfonksyònman sèvè (tan pa senkronize)

Nimewo vèsyon (nimewo vèsyon) - nimewo vèsyon pwotokòl NTP (1-4).

mòd (mòd) - mòd opere nan moun k ap voye pake a. Valè soti nan 0 a 7, pi komen:

  • 3 - kliyan
  • 4 - sèvè
  • 5 - mòd emisyon

stratus (nivo stratifikasyon) - kantite kouch entèmedyè ant sèvè a ak revèy referans la (1 - sèvè a pran done ki sòti dirèkteman nan revèy referans la, 2 - sèvè a pran done ki sòti nan yon sèvè ak kouch 1, elatriye).
pisin se yon nonb antye siyen ki reprezante entèval maksimòm ant mesaj youn apre lòt. Kliyan NTP a espesifye isit la entèval nan ki li espere sondaj sèvè a, ak sèvè a NTP presize entèval la nan ki li espere yo dwe vote. Valè a egal a logaritm binè segonn.
Presizyon (presizyon) se yon nonb antye siyen ki reprezante presizyon nan revèy sistèm lan. Valè a egal a logaritm binè segonn.
Reta rasin (reta sèvè) - tan li pran pou lekti revèy yo rive nan sèvè NTP la, kòm yon nimewo pwen fiks nan segonn.
Dispèsyon rasin (sèvè gaye) - gaye lekti revèy sèvè NTP kòm yon kantite segonn ak yon pwen fiks.
Ref id (idantifyan sous) – id revèy. Si sèvè a gen strat 1, lè sa a ref id se non revèy atomik la (4 karaktè ASCII). Si sèvè a sèvi ak yon lòt sèvè, Lè sa a, id ref la gen adrès sèvè sa a.
Dènye 4 jaden yo reprezante tan an - 32 bits - pati nan nonb antye relatif, 32 bits - pati nan fraksyon.
Referans — dènye lekti revèy yo sou sèvè a.
Origine – tan lè pake a te voye (ranpli pa sèvè a - plis sou sa a anba a).
Resevwa – tan sèvè a te resevwa pake a.
Transmèt – tan pou voye pake a soti nan sèvè a nan kliyan an (ranpli pa kliyan an, plis sou sa a anba a).

Nou p ap konsidere de dènye domèn yo.

Ann ekri pake nou an:

Pake kòd

class NTPPacket:
    _FORMAT = "!B B b b 11I"

    def __init__(self, version_number=2, mode=3, transmit=0):
        # Necessary of enter leap second (2 bits)
        self.leap_indicator = 0
        # Version of protocol (3 bits)
        self.version_number = version_number
        # Mode of sender (3 bits)
        self.mode = mode
        # The level of "layering" reading time (1 byte)
        self.stratum = 0
        # Interval between requests (1 byte)
        self.pool = 0
        # Precision (log2) (1 byte)
        self.precision = 0
        # Interval for the clock reach NTP server (4 bytes)
        self.root_delay = 0
        # Scatter the clock NTP-server (4 bytes)
        self.root_dispersion = 0
        # Indicator of clocks (4 bytes)
        self.ref_id = 0
        # Last update time on server (8 bytes)
        self.reference = 0
        # Time of sending packet from local machine (8 bytes)
        self.originate = 0
        # Time of receipt on server (8 bytes)
        self.receive = 0
        # Time of sending answer from server (8 bytes)
        self.transmit = transmit

Pou voye (ak resevwa) yon pake nan sèvè a, nou dwe kapab tounen l 'nan yon etalaj byte.
Pou operasyon sa a (ak ranvèse), nou pral ekri de fonksyon - pake () ak depake ():

fonksyon pake

def pack(self):
        return struct.pack(NTPPacket._FORMAT,
                (self.leap_indicator << 6) + 
                    (self.version_number << 3) + self.mode,
                self.stratum,
                self.pool,
                self.precision,
                int(self.root_delay) + get_fraction(self.root_delay, 16),
                int(self.root_dispersion) + 
                    get_fraction(self.root_dispersion, 16),
                self.ref_id,
                int(self.reference),
                get_fraction(self.reference, 32),
                int(self.originate),
                get_fraction(self.originate, 32),
                int(self.receive),
                get_fraction(self.receive, 32),
                int(self.transmit),
                get_fraction(self.transmit, 32))

fonksyon depake

def unpack(self, data: bytes):
        unpacked_data = struct.unpack(NTPPacket._FORMAT, data)

        self.leap_indicator = unpacked_data[0] >> 6  # 2 bits
        self.version_number = unpacked_data[0] >> 3 & 0b111  # 3 bits
        self.mode = unpacked_data[0] & 0b111  # 3 bits

        self.stratum = unpacked_data[1]  # 1 byte
        self.pool = unpacked_data[2]  # 1 byte
        self.precision = unpacked_data[3]  # 1 byte

        # 2 bytes | 2 bytes
        self.root_delay = (unpacked_data[4] >> 16) +
            (unpacked_data[4] & 0xFFFF) / 2 ** 16
         # 2 bytes | 2 bytes
        self.root_dispersion = (unpacked_data[5] >> 16) +
            (unpacked_data[5] & 0xFFFF) / 2 ** 16 

        # 4 bytes
        self.ref_id = str((unpacked_data[6] >> 24) & 0xFF) + " " + 
                      str((unpacked_data[6] >> 16) & 0xFF) + " " +  
                      str((unpacked_data[6] >> 8) & 0xFF) + " " +  
                      str(unpacked_data[6] & 0xFF)

        self.reference = unpacked_data[7] + unpacked_data[8] / 2 ** 32  # 8 bytes
        self.originate = unpacked_data[9] + unpacked_data[10] / 2 ** 32  # 8 bytes
        self.receive = unpacked_data[11] + unpacked_data[12] / 2 ** 32  # 8 bytes
        self.transmit = unpacked_data[13] + unpacked_data[14] / 2 ** 32  # 8 bytes

        return self

Pou moun ki parese, kòm yon aplikasyon - kòd ki vire yon pake nan yon fisèl bèl

def to_display(self):
        return "Leap indicator: {0.leap_indicator}n" 
                "Version number: {0.version_number}n" 
                "Mode: {0.mode}n" 
                "Stratum: {0.stratum}n" 
                "Pool: {0.pool}n" 
                "Precision: {0.precision}n" 
                "Root delay: {0.root_delay}n" 
                "Root dispersion: {0.root_dispersion}n" 
                "Ref id: {0.ref_id}n" 
                "Reference: {0.reference}n" 
                "Originate: {0.originate}n" 
                "Receive: {0.receive}n" 
                "Transmit: {0.transmit}"
                .format(self)

Voye yon pake sou sèvè a

Yo dwe voye yon pake ki gen jaden plen sou sèvè a Version, mòd и Transmèt. Nan Transmèt ou dwe presize tan aktyèl la sou machin lokal la (kantite segonn depi 1 janvye 1900), vèsyon - nenpòt nan 1-4, mòd - 3 (mòd kliyan).

Sèvè a, li te aksepte demann lan, ranpli tout jaden yo nan pake NTP la, kopye nan jaden an. Origine valè soti nan Transmèt, ki te vini nan demann lan. Li se yon mistè pou mwen poukisa kliyan an pa ka imedyatman ranpli nan valè a nan tan li nan jaden an Origine. Kòm yon rezilta, lè pake a tounen, kliyan an gen 4 valè tan - tan an te voye demann lan (Origine), tan sèvè a te resevwa demann lan (Resevwa), tan sèvè a voye repons lan (Transmèt) ak lè kliyan an te resevwa repons lan - Rive (pa nan pakè a). Sèvi ak valè sa yo nou ka fikse tan ki kòrèk la.

Pake voye ak resevwa kòd

# Time difference between 1970 and 1900, seconds
FORMAT_DIFF = (datetime.date(1970, 1, 1) - datetime.date(1900, 1, 1)).days * 24 * 3600
# Waiting time for recv (seconds)
WAITING_TIME = 5
server = "pool.ntp.org"
port = 123
    
packet = NTPPacket(version_number=2, mode=3, transmit=time.time() + FORMAT_DIFF)
answer = NTPPacket()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.settimeout(WAITING_TIME)
    s.sendto(packet.pack(), (server, port))
    data = s.recv(48)
    arrive_time = time.time() + FORMAT_DIFF
    answer.unpack(data)

Pwosesis done ki soti nan sèvè a

Pwosesis done ki soti nan sèvè a sanble ak aksyon mesye angle a nan ansyen pwoblèm nan Raymond M. Smullyan (1978): "Yon nonm pa t 'gen yon mont ponyèt, men te gen yon revèy miray egzat nan kay la, ki pafwa li bliye. van. Yon jou, li te bliye remontre mont li ankò, li te ale vizite zanmi l ', li pase aswè a avè l', epi lè li tounen lakay li, li te rive mete mont lan kòrèkteman. Ki jan li te jere fè sa si tan vwayaj la pa te konnen davans? Repons lan se: “Lè yon moun ap kite kay la, mont mont li epi li sonje nan ki pozisyon men yo ye. Li te vin jwenn yon zanmi epi li te kite envite yo, li te note lè arive ak depa l. Sa a pèmèt li konnen konbyen tan li te vizite. Retounen lakay li epi gade revèy la, yon moun detèmine dire absans li. Lè yon moun soustraksyon nan tan sa a tan li te pase vizite a, yon moun jwenn tan ki pase vwayaje ale ak tounen. Lè li ajoute mwatye tan ki pase sou wout la ak tan pou l kite envite yo, li jwenn opòtinite pou l chèche konnen lè arive lakay li epi ajiste men mont li kòmsadwa.

Jwenn lè sèvè a ap travay sou yon demann:

  1. Jwenn tan vwayaj la nan pake a soti nan kliyan an nan sèvè a: ((Arive – Origine) – (Transmèt – Resevwa)) / 2
  2. Jwenn diferans ki genyen ant tan kliyan ak sèvè:
    Resevwa - Origine - ((Arive - Origine) - (Transmèt - Resevwa)) / 2 =
    2 * Resevwa – 2 * Origine – Rive + Origine + Transmèt – Resevwa =
    Resevwa – Origine – Rive + Transmèt

Nou ajoute valè ki kapab lakòz nan tan lokal la epi jwi lavi.

Sòti nan rezilta a

time_different = answer.get_time_different(arrive_time)
result = "Time difference: {}nServer time: {}n{}".format(
    time_different,
    datetime.datetime.fromtimestamp(time.time() + time_different).strftime("%c"),
    answer.to_display())
print(result)

Itil lyen.

Sous: www.habr.com

Add nouvo kòmantè