Scribens simplex NTP client

Salve, Habrausers. Hodie loqui volo quomodo in simplicitate tua NTP client scribere. Plerumque colloquium vertet ad structuram fasciculi et modum responsionis expediendi a servo NTP. Codex Pythone scribetur, quia mihi videtur nullam esse simpliciter linguam pro talibus. Connoisseurs similitudinem codicis notabo cum codice ntplib - eo "inspiratus sum".

Quidnam igitur est NTP? NTP est protocollum pro commercio exacto tempore servientibus. Haec tabula in multis machinis modernis adhibetur. Verbi gratia, w32tm officium in fenestris.

Sunt 5 versiones NTP protocolli in totalibus. Prima, versio 0 (1985, RFC958)), nunc obsoleta censetur. Nunc recentiora adhibentur, 1 (1988, RFC1059), 2nd (1989, RFC1119), 3 (1992, RFC1305) et IV (4, RFC1996). Versiones 2030-1 inter se compatiuntur, in operatione servientis algorithms tantum differunt.

Forma sarcina

Scribens simplex NTP client

Leap indicator numerus demonstrans admonitionem secundae coordinationis. Sensus:

  • 0 - nulla correptio
  • I - ultimum minute diei continet LXI seconds
  • II - ultimum minute diei continet LIX seconds
  • III - server malfunction (tempus non synchronised)

versio numerus (numerus versionis) – NTP protocollum versionis numeri (1-4).

modum (modus) - modum conleci mittente operandi. Valorem ab 0 ad 7, frequentissimum;

  • III - client
  • IV - server
  • V - passim modus

stratum (gradus iaciens) - numerus stratorum intermediorum inter servientis et horologii relationem (1 - ministrans notitias directe accipit a relatione horologii, 2 - ministrans notitias accipit a servo cum lavacro 1, etc.).
Lacus signatum est integer repraesentans maximum intervallum inter continuos nuntios. Cliens NTP hic specificat spatium ad quod expectat ut servo tondendum, et NTP servo designat spatium ad quod expectat ut tonderetur. Valor aequalis est logarithmo binario secundorum.
precision (accuratio) signatum est integrum repraesentans subtiliter horologii systematis. Valor aequalis est logarithmo binario secundorum.
radix mora tempus accipit pro lectionum horologii NTP ad servo, sicut punctum certum numerum secundorum.
Radix dissipatio (disgregatio server) - Lectionum horologii NTP pervulgata sicut numerus secundorum cum puncto certo.
Ref id horologium id. Si servo stratum 1, tunc ref id est nomen horologii atomici (4 ASCII). Si servo alio servo utatur, tunc electronica inscriptio huius ministri continet.
Novissima 4 rura repraesentant tempus - 32 minutas - partem integram, 32 bits - partem fractam.
Reference β€” lectiones horologii novissimae in calculonis servi.
Originate - tempus cum fasciculus missus est (repletus a servo - plura de hoc infra).
accipite - Tempus fasciculum a servo receptum est.
RENUNTIO – tempus mittendi fasciculum a servo ad clientem (repletum per clientem, plura de hoc infra).

Duos proximos agros non consideremus.

Sarcina nostra scribamus:

Package code

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

Ut servo fasciculum mittere (ac recipere) debeamus in byte ordinatum vertere posse.
Ad hanc operationem (et contra) scribemus duas functiones - pack() et unpack();

stipant munus

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))

unpack munus

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

Pro pigris hominibus, sicut in applicatione - codice qui sarcinam in chorda pulchra vertit

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)

Mittens a sarcina in calculonis servi

Fasciculus repletis agris mitti debet cum servo Version, modum ΠΈ RENUNTIO. Quod RENUNTIO Tempus hodiernum in machina locali denotare debes (numerus secundorum a die 1 ianuarii 1900), versio cuiuslibet 1-4, modi - 3 (modus cliens).

Servus, accepta petitione, omnes agros in NTP conleci implet, describens in campum Originate valorem ex * RENUNTIOque venit in rogatu. Mysterium est mihi cur cliens non statim impleat aestimationem sui temporis in agro Originate. Quam ob rem, cum fasciculus revertitur, client tempus 4 valoris habet - tempus postulationis missum est.Originate) Tempus petitionem accepit servo (accipite) Tempus responsum misit servo (RENUNTIO) Et tempus responsum accepit client - perveniet (non in sarcina). His valoribus utendo possumus rectam tempus constituere.

Sarcina mittendis vicissimque accipiendis codice

# 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)

Processus notitia ex servo

Processus notitiae a servo similis est factis Anglici viri ex veteri problemate Raymundi M. Smullyan (1978): β€œUnus homo non habebat bracchium, sed erat horologium murus accuratus domi, cuius aliquando oblitus ad ventum. Quadam die, oblitus iterum vigiliarum ventorum, ivit ad visitandum amicum suum, vesperi cum eo consumpsit, et cum domum rediit, vigilias recte ordinare curavit. Quomodo hoc procuravit si peregrinatione tempore praecognita non fuit? Responsio est: β€œCum domo exeundo, aliquis vigilias agit et meminit in quo loco manus sint. Ad amicum veniens hospites relicto, tempus adventus et profectionis notat. Hoc permittit invenire quousque invisit. Domum reversus et horologium spectans, homo durationem suae absentiae constituit. Subtrahendo ex hoc tempore quod visitavit, invenit tempus eundi et retro. Addendo dimidium tempus in via ad tempus hospitum relinquendi, opportunitatem accipit tempus adveniendi domum cognoscendi et vigiliarum manus accommodandi.

Invenire tempus in calculonis servi laborat:

  1. Invenire tempus itinerarium fasciculum a cliente ad servo; ((Pervenie - Originate) - (Transmit - Accipite)) / 2
  2. Discrimen invenire inter clientem et servo tempore:
    Accipe - Originate - ((Pervenie - Originate) - (Transmitte - Accipe)) / 2 =
    2 * Accipite - 2 * Originate - Veni + Originate + Transmit - Accipite =
    Accipe - Originate - veni + Transmit

Valorem consequentem addimus tempori locali et vita frui.

Output eventus

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)

Utilis Link.

Source: www.habr.com