A 'sgrìobhadh cleachdaiche NTP sìmplidh

Halo, Habrausers. An-diugh tha mi airson bruidhinn mu dheidhinn mar a sgrìobhas tu an neach-dèiligidh NTP sìmplidh agad fhèin. Gu bunaiteach, tionndaidh an còmhradh gu structar a’ phacaid agus an dòigh air am freagairt bhon fhrithealaiche NTP a ghiullachd. Thèid an còd a sgrìobhadh ann am Python, oir tha e coltach riumsa nach eil dìreach cànan nas fheàrr airson rudan mar sin. Bheir connoisseurs fa-near cho coltach sa tha an còd leis a’ chòd ntplib - bha mi “air mo bhrosnachadh” leis.

Mar sin dè a th’ ann an NTP co-dhiù? Tha NTP na phròtacal airson eadar-obrachadh le frithealaichean ùine cheart. Tha am protocol seo air a chleachdadh ann an iomadh inneal ùr-nodha. Mar eisimpleir, an t-seirbheis w32tm ann an uinneagan.

Tha 5 dreachan den phròtacal NTP gu h-iomlan. Thathas den bheachd gu bheil a’ chiad, dreach 0 (1985, RFC958)), air a dhol à bith. A-nis thathas a’ cleachdadh an fheadhainn as ùire, 1d (1988, RFC1059), 2na (1989, RFC1119), 3mh (1992, RFC1305) agus 4mh (1996, RFC2030). Tha dreachan 1-4 co-chòrdail ri chèile; chan eil iad eadar-dhealaichte ach ann an algorithms gnìomhachd an fhrithealaiche.

Cruth pacaid

A 'sgrìobhadh cleachdaiche NTP sìmplidh

Comharra leum (comharra ceartachaidh) àireamh a tha a’ comharrachadh an dàrna rabhadh leum. Ciall:

  • 0 - chan eil ceartachadh ann
  • 1 - tha 61 diogan anns a 'mhionaid mu dheireadh den latha
  • 2 - tha 59 diogan anns a’ mhionaid mu dheireadh den latha
  • 3 - fàiligeadh an fhrithealaiche (ùine a-mach à sioncranachadh)

Àireamh an tionndaidh (àireamh tionndaidh) - àireamh dreach protocol NTP (1-4).

fasan (modh) - modh obrachaidh neach-cuiridh a’ phacaid. Luach bho 0 gu 7, as cumanta:

  • 3 - neach-dèiligidh
  • 4 - frithealaiche
  • 5 - modh craolaidh

strath (ìre còmhdachaidh) - an àireamh de shreathan eadar-mheadhanach eadar an frithealaiche agus an gleoc iomraidh (1 - bidh am frithealaiche a’ toirt dàta gu dìreach bhon ghleoc iomraidh, 2 - bidh am frithealaiche a’ toirt dàta bho fhrithealaiche le còmhdach 1, msaa).
Pool na shlàn-chunntas soidhnichte a’ riochdachadh an ùine as motha eadar teachdaireachdan leantainneach. Bidh an neach-dèiligidh NTP a’ sònrachadh an seo an eadar-ama aig a bheil dùil aige bhòtadh air an fhrithealaiche, agus tha am frithealaiche NTP a’ sònrachadh an eadar-ama aig a bheil dùil gun tèid a bhòtadh. Tha an luach co-ionann ris an logarithm binary de dhiog.
Precison (mion-chinnt) na shlàn-chunntas soidhnichte a’ riochdachadh cruinneas gleoc an t-siostaim. Tha an luach co-ionann ris an logarithm binary de dhiog.
Root dàil (dàil an fhrithealaiche) - an ùine a bheir e airson leughaidhean a’ ghleoc an t-seirbheisiche NTP a ruighinn, mar àireamh puing stèidhichte de dhiog.
Sgaoileadh root (sgaoileadh frithealaiche) - sgaoileadh leughaidhean gleoc frithealaiche NTP mar grunn diogan le puing stèidhichte.
Ref id (stòr id) – faire id. Ma tha stratum 1 aig an fhrithealaiche, is e ref id ainm a’ ghleoc atamach (4 caractaran ASCII). Ma chleachdas am frithealaiche frithealaiche eile, bidh seòladh an fhrithealaiche seo anns an id ref.
Tha na 4 raointean mu dheireadh a 'riochdachadh na h-ùine - 32 pìosan - am pàirt iomlan, 32 pìosan - am pàirt bloighteach.
Fiosrachadh - an uaireadair as ùire air an fhrithealaiche.
Tùs - àm nuair a chaidh am pasgan a chuir (air a lìonadh a-steach leis an fhrithealaiche - barrachd air seo gu h-ìosal).
Faigh - àm nuair a fhuair am frithealaiche am pasgan.
sgaoileadh - àm airson a’ phacaid a chuir bhon t-seirbheisiche chun neach-dèiligidh (air a lìonadh a-steach leis an neach-dèiligidh, barrachd air seo gu h-ìosal).

Cha tèid beachdachadh air an dà raon mu dheireadh.

Sgrìobhamaid ar pasgan:

Còd pacaid

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

Gus pasgan a chuir (agus fhaighinn) chun an fhrithealaiche, feumaidh sinn a bhith comasach air a thionndadh gu bhith na raon byte.
Airson an obrachaidh seo (agus air ais), sgrìobhaidh sinn dà ghnìomh - pasgan () agus dì-phapadh ():

gnìomh pacaid

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

gnìomh unpack

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

Do dhaoine leisg, mar thagradh - còd a thionndaidheas pasgan gu sreang bhrèagha

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)

A' cur pacaid dhan fhrithealaiche

Cuir pasgan le raointean lìonta chun an fhrithealaiche tionndadh, fasan и sgaoileadh. Tha a ' sgaoileadh feumaidh tu an ùine làithreach a shònrachadh air an inneal ionadail (an àireamh de dhiog bho 1 Faoilleach 1900), dreach - gin de 1-4, modh - 3 (modh teachdaiche).

Bidh am frithealaiche, às deidh dha an t-iarrtas fhaighinn, a’ lìonadh a h-uile raon sa phasgan NTP, a’ dèanamh lethbhreac dhan raon Tùs luach bho sgaoileadh, a thàinig anns an iarrtas. Tha e na dhìomhaireachd dhomh carson nach urrainn don neach-dèiligidh luach a chuid ùine san raon a lìonadh sa bhad Tùs. Mar thoradh air an sin, nuair a thig am pasgan air ais, tha 4 luachan ùine aig an neach-dèiligidh - an ùine a chaidh an t-iarrtas a chuir (Tùs), an ùine a fhuair am frithealaiche an t-iarrtas (Faigh), an ùine a chuir am frithealaiche am freagairt (sgaoileadh) agus an ùine a fhuair an neach-dèiligidh am freagairt - Thig (chan ann sa phacaid). A’ cleachdadh nan luachan sin is urrainn dhuinn an ùine cheart a shuidheachadh.

Còd cur agus faighinn pacaid

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

Làimhseachadh dàta bhon fhrithealaiche

Tha làimhseachadh dàta bhon t-seirbheisiche coltach ri gnìomhan an duine-uasal Sasannach bho sheann dhuilgheadas Raymond M. Smullyan (1978): “Cha robh uaireadair aig aon fhear, ach bha gleoc balla ceart aig an taigh, a dhìochuimhnich e uaireannan. a ghaoth. Aon latha, an dèidh dha dearmad a dhèanamh air an uaireadair aige a-rithist, chaidh e a chèilidh air a charaid, chuir e seachad am feasgar còmhla ris, agus nuair a thill e dhachaigh, chaidh aige air an uaireadair a shuidheachadh ceart. Ciamar a chaidh aige air seo a dhèanamh mura robh fios air an ùine siubhail ro-làimh? Is e am freagairt: “Nuair a dh’ fhàgas e an dachaigh, bidh neach a ’tionndadh an uaireadair aige agus a’ cuimhneachadh dè an suidheachadh anns a bheil na làmhan. An dèidh tighinn gu caraid agus na h-aoighean fhàgail, tha e a 'toirt fa-near an àm a thàinig e agus a dh' fhalbh e. Leigidh seo leis faighinn a-mach dè cho fada ‘s a bha e a’ tadhal. A 'tilleadh dhachaigh agus a' coimhead air a 'ghleoc, bidh neach a' dearbhadh fad a neo-làthaireachd. Le bhith a 'toirt air falbh bhon àm seo an ùine a chuir e seachad a' tadhal, bidh neach a 'faighinn a-mach an ùine a thathar a' cur seachad a 'siubhal an sin agus air ais. Le bhith a’ cur leth na h-ùine a chaidh a chaitheamh air an rathad ris an àm a dh’ fhàg e na h-aoighean, gheibh e an cothrom faighinn a-mach an àm a ràinig e dhachaigh agus làmhan an uaireadair aige atharrachadh a rèir sin.”

Lorg an ùine a bha am frithealaiche ag obair air an iarrtas:

  1. A 'lorg ùine siubhail a' phacaid bhon neach-dèiligidh chun an fhrithealaiche: ((Sig - Tòisich) - (Sguab às - Faigh)) / 2
  2. Lorg an diofar eadar àm teachdaiche agus frithealaiche:
    Faigh - Tòisich - ((Thig - Tòisich) - (Gluasad - Faigh)) / 2 =
    2 * Faigh - 2 * Tòiseachadh - Thig + Tòiseachadh + Tar-chuir - Faigh =
    Faigh - Tùs - Thig + Tar-chuir

Bidh sinn a’ cur an luach a thig às a sin ris an àm ionadail agus a’ faighinn tlachd à beatha.

Toradh an toraidh

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)

Feumail ceangal.

Source: www.habr.com

Cuir beachd ann