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
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:
- Faigh am taistil an phaicéid ón gcliant go dtí an freastalaí: ((Sroich – Tionscnaimh) – (Tarchur – Faigh)) / 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)