Cliant NTP simplí a scríobh

Dia duit, Habrausers. Sa lá atá inniu ba mhaith liom labhairt faoi conas do chliant NTP simplí féin a scríobh. Go bunúsach, casfaidh an comhrá le struchtúr an phaicéid agus an modh chun an freagra a phróiseáil ón bhfreastalaí NTP. Scríobhfar an cód i Python, mar feictear dom nach bhfuil aon teanga níos fearr le haghaidh rudaí den sórt sin. Tabharfaidh Connoisseurs faoi deara cosúlacht an chóid leis an gcód ntplib - bhí mé “spreagtha” aige.

Mar sin, cad é go díreach NTP? Is prótacal é NTP chun idirghníomhú le freastalaithe ama cruinn. Úsáidtear an prótacal seo i go leor meaisíní nua-aimseartha. Mar shampla, an tseirbhís w32tm i fuinneoga.

Tá 5 leagan den phrótacal NTP san iomlán. Meastar go bhfuil an chéad cheann, leagan 0 (1985, RFC958)), imithe i léig faoi láthair. Anois úsáidtear na cinn níos nuaí, 1ú (1988, RFC1059), 2ú (1989, RFC1119), 3ú (1992, RFC1305) agus 4ú (1996, RFC2030). Tá leaganacha 1-4 comhoiriúnach lena chéile; ní hionann iad agus algartaim oibríochta an fhreastalaí amháin.

Formáid pacáiste

Cliant NTP simplí a scríobh

Léim táscaire (táscaire ceartúcháin) - uimhir a léiríonn rabhadh faoin gcomhordú sa dara háit. Brí:

  • 0 – gan aon cheartú
  • 1 – bíonn 61 soicind i nóiméad deireanach an lae
  • 2 – bíonn 59 soicind i nóiméad deireanach an lae
  • 3 - mífheidhmiú freastalaí (níl an t-am sioncronaithe)

Uimhir na leagan (uimhir leagain) – uimhir leagain prótacail NTP (1-4).

Mód (modh) — modh oibriúcháin an tseoltóra paicéid. Luach ó 0 go 7, is coitianta:

  • 3 - cliant
  • 4 - freastalaí
  • 5 – mód craolta

strataim (leibhéal srathaithe) – líon na sraitheanna idirmheánacha idir an freastalaí agus an clog tagartha (1 – glacann an freastalaí sonraí go díreach ón gclog tagartha, 2 – glacann an freastalaí sonraí ó fhreastalaí ar a bhfuil ciseal 1, etc.).
Linn is slánuimhir sínithe é a sheasann don uastréimhse idir teachtaireachtaí comhleanúnacha. Sonraíonn an cliant NTP anseo an t-eatramh ag a bhfuil sé ag súil le vótaíocht a dhéanamh ar an bhfreastalaí, agus sonraíonn an freastalaí NTP an t-eatramh ag a bhfuil sé ag súil le vótaíocht. Tá an luach comhionann leis an logarithm dénártha soicind.
Beachtas (cruinneas) slánuimhir sínithe a léiríonn cruinneas chlog an chórais. Tá an luach comhionann leis an logarithm dénártha soicind.
Moill fréimhe (moill ar an bhfreastalaí) – an t-am a thógann sé ar na léamha cloig an freastalaí NTP a shroicheadh, mar líon seasta soicindí.
Scaipeadh fréamhacha (leathadh freastalaí) - scaipeadh léamha clog freastalaí NTP mar roinnt soicind le pointe seasta.
ID tag (aitheantóir foinse) – clog id. Má tá strata 1 ag an bhfreastalaí, is é an t-aitheantas tagartha ainm an chlog adamhach (4 charachtar ASCII). Má úsáideann an freastalaí freastalaí eile, beidh seoladh an fhreastalaí seo san aitheantas tag.
Léiríonn na 4 réimse dheireanacha an t-am - 32 giotán - an chuid slánuimhir, 32 giotán - an chuid codánach.
Tagairt — na léamha cloig is déanaí ar an bhfreastalaí.
Bunaidh – an t-am ar seoladh an paicéad (arna líonadh ag an bhfreastalaí - tuilleadh faoi seo thíos).
Faigh – an t-am a fuair an freastalaí an paicéad.
A tharchur – an t-am a seoltar an paicéad ón bhfreastalaí chuig an gcliant (líon an cliant é, tuilleadh faoi seo thíos).

Ní mheasfaimid an dá réimse dheireanacha.

Scríobhaimis ár bpacáiste:

Cóid pacáiste

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

Chun paicéad a sheoladh (agus a fháil) chuig an bhfreastalaí, ní mór dúinn a bheith in ann é a thiontú ina eagar beart.
Don oibríocht seo (agus droim ar ais), scríobhfaimid dhá fheidhm - pacáiste () agus díphacáil ():

feidhm pacáiste

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

feidhm díphacáil

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

I gcás daoine leisciúil, mar iarratas - cód a chasann pacáiste isteach i teaghrán álainn

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)

Ag seoladh pacáiste chuig an bhfreastalaí

Ní mór paicéad le réimsí líonta a sheoladh chuig an bhfreastalaí Leagan, Mód и A tharchur. I A tharchur ní mór duit an t-am atá ann faoi láthair a shonrú ar an meaisín áitiúil (líon na soicind ó 1 Eanáir, 1900), leagan - aon cheann de 1-4, modh - 3 (modh cliant).

Tar éis don fhreastalaí glacadh leis an iarratas, líonann sé na réimsí go léir sa phaicéad NTP, ag cóipeáil isteach sa réimse Bunaidh luach ó A tharchur, a tháinig san iarraidh. Is rúndiamhair dom cén fáth nach féidir leis an gcliant luach a chuid ama sa réimse a líonadh isteach láithreach Bunaidh. Mar thoradh air sin, nuair a thagann an paicéad ar ais, tá 4 luach ama ag an gcliant - an t-am a seoladh an t-iarratas (Bunaidh), an t-am a fuair an freastalaí an t-iarratas (Faigh), an t-am a sheol an freastalaí an freagra (A tharchur) agus an t-am a fuair an cliant an freagra – Teacht (nach bhfuil sa phacáiste). Ag baint úsáide as na luachanna seo is féidir linn an t-am ceart a shocrú.

Cód seolta agus glactha pacáiste

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

Sonraí a phróiseáil ón bhfreastalaí

Tá próiseáil sonraí ón bhfreastalaí cosúil le gníomhartha an fhir uasal Shasana ó sheanfhadhb Raymond M. Smullyan (1978): “Ní raibh uaireadóir ag fear amháin, ach bhí clog balla cruinn sa bhaile, rud a ndearna sé dearmad air uaireanta. a ghaoth. Lá amháin, tar éis dó dearmad a dhéanamh ar a uaireadóir a fhoirceannadh arís, chuaigh sé chun cuairt a thabhairt ar a chara, chaith sé an tráthnóna leis, agus nuair a d'fhill sé abhaile, d'éirigh leis an uaireadóir a shocrú i gceart. Conas a d’éirigh leis é seo a dhéanamh mura raibh an t-am taistil ar eolas roimh ré? Is é an freagra: “Nuair a fhágann sé an baile, casann duine a uaireadóir agus cuimhníonn sé ar an áit ina bhfuil na lámha. Tar éis dó teacht chuig cara agus na haíonna a fhágáil, tugann sé faoi deara an t-am a tháinig sé agus a fhágann sé. Ligeann sé seo dó a fháil amach cé chomh fada agus a bhí sé ar cuairt. Ag filleadh abhaile agus ag féachaint ar an gclog, cinneann duine fad a neamhláithreachta. Trí an t-am a chaith sé ar cuairt a dhealú ón am seo, faigheann duine amach an t-am a chaitear ag taisteal ann agus ar ais. Trí leath an ama a chaitear ar an mbóthar a chur leis an am a fhágann sé na haíonna, faigheann sé an deis a fháil amach cén t-am a tháinig sé abhaile agus a lámha a choigeartú dá réir.”

Faigh an t-am a bhfuil an freastalaí ag obair ar iarratas:

  1. Faigh am taistil an phaicéid ón gcliant go dtí an freastalaí: ((Sroich – Tionscnaimh) – (Tarchur – Faigh)) / 2
  2. Faigh an difríocht idir am cliant agus freastalaí:
    Faigh - Origin - ((Sroich - Origin) - (Tarchur - Faigh)) / 2 =
    2 * Faigh – 2 * Bunús – Teacht + Tionscnaimh + Tarchuir – Faigh =
    Faigh – Tionscnaimh – Teacht + Tarchuir

Cuireann muid an luach a thagann as sin leis an am áitiúil agus bainimid taitneamh as an saol.

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

Úsáideach nasc.

Foinse: will.com

Add a comment