Rubuta abokin ciniki mai sauƙi na NTP

Sannu habrausers. A yau ina so in yi magana game da yadda ake rubuta naku mai sauƙi abokin ciniki na NTP. Ainihin, tattaunawar za ta juya zuwa tsarin fakitin da kuma yadda ake sarrafa martani daga uwar garken NTP. Za a rubuta lambar a cikin Python, saboda, a ganina, babu wani yare mafi kyau ga irin waɗannan abubuwa. Connoisseurs za su kula da kamancen lambar tare da lambar ntplib - Na kasance "wahayi" da shi.

To menene NTP ko ta yaya? NTP yarjejeniya ce don sadarwa tare da sabar lokaci. Ana amfani da wannan ka'ida a cikin injinan zamani da yawa. Misali, sabis na w32tm akan windows.

Akwai nau'ikan ka'idojin NTP guda 5 gabaɗaya. Na farko, sigar 0 (1985, RFC958) a halin yanzu ana ɗaukarsa mara amfani. A halin yanzu ana amfani da sababbi, 1st (1988, RFC1059), 2nd (1989, RFC1119), 3rd (1992, RFC1305) da 4th (1996, RFC2030). Siffofin 1-4 sun dace da juna, sun bambanta kawai a cikin algorithms na sabobin.

Tsarin fakiti

Rubuta abokin ciniki mai sauƙi na NTP

Alamar tsalle (alamar gyara) lamba ce da ke nuna faɗakarwa ta biyu. Ma'ana:

  • 0 - babu gyara
  • 1- minti na karshe na ranar yana dauke da dakika 61
  • 2- minti na karshe na ranar yana dauke da dakika 59
  • 3- gazawar uwar garken (lokacin ya ƙare aiki)

Lambar sigar (lambar sigar) - lambar sigar yarjejeniya ta NTP (1-4).

yanayin (yanayin) - yanayin aiki na mai aikawa fakiti. Darajar daga 0 zuwa 7, mafi yawanci:

  • 3 - abokin ciniki
  • 4 - uwar garken
  • 5 - yanayin watsa shirye-shirye

stratum (matakin Layer) - adadin matsakaicin yadudduka tsakanin uwar garken da agogon tunani (1 - uwar garken yana ɗaukar bayanai kai tsaye daga agogon tunani, 2 - uwar garken yana ɗaukar bayanai daga uwar garken tare da matakin 1, da sauransu).
pool lamba ce da aka sanya hannu wacce ke wakiltar matsakaicin tazara tsakanin saƙonnin jere. Abokin ciniki na NTP yana ƙayyadaddun tazarar da yake tsammanin za a yi zabe a uwar garken, kuma uwar garken NTP ta ƙayyade tazarar da take tsammanin za a yi zabe. Ƙimar tana daidai da binary logarithm na daƙiƙa.
daidaici (daidaici) lamba ce da aka sanya hannu wacce ke wakiltar daidaiton agogon tsarin. Ƙimar tana daidai da binary logarithm na daƙiƙa.
tushen jinkiri (latency uwar garken) shine lokacin da ake ɗaukar agogo don isa uwar garken NTP, a matsayin ƙayyadadden adadin daƙiƙai.
tushen watsawa (watsewar uwar garken) - Watsawar agogon uwar garken NTP a matsayin ƙayyadadden adadin sakanni.
Ref id (source id) - kallon id. Idan uwar garken yana da stratum 1, to ref id shine sunan agogon atomic (haruffa 4 ASCII). Idan uwar garken ta yi amfani da wata uwar garken, to, ref id ya ƙunshi adireshin wannan uwar garken.
Filaye 4 na ƙarshe shine lokacin - 32 ragowa - ɓangaren lamba, 32 rago - ɓangaren juzu'i.
reference - sabon agogon akan uwar garken.
Asali - lokacin da aka aika fakitin (cika ta uwar garken - ƙari akan wannan ƙasa).
Karba – lokacin da sabar ta karɓi fakitin.
Aika da - lokacin da aka aika fakitin daga uwar garken zuwa abokin ciniki (wanda abokin ciniki ya cika, ƙari akan abin da ke ƙasa).

Ba za a yi la'akari da filayen biyu na ƙarshe ba.

Bari mu rubuta kunshin mu:

Lambar fakiti

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

Don aika (da karɓa) fakiti zuwa uwar garken, dole ne mu iya juya shi zuwa tsararrun bytes.
Don wannan (da kuma baya) aiki, za mu rubuta ayyuka guda biyu - fakitin () da cire kaya ():

shirya aiki

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

cire kayan aiki

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

Ga mutane masu kasala, a matsayin aikace-aikace - lambar da ke juya kunshin zuwa kyakkyawan kirtani

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)

Aika kunshin zuwa uwar garken

Aika fakiti mai cike da filayen zuwa uwar garken version, yanayin и Aika da. A Aika da Dole ne ku ƙayyade lokacin yanzu akan na'ura na gida (yawan seconds tun daga Janairu 1, 1900), sigar - kowane na 1-4, yanayin - 3 (yanayin abokin ciniki).

Sabar, bayan karɓar buƙatar, ta cika dukkan filayen da ke cikin fakitin NTP, yin kwafin cikin filin Asali darajar daga Aika da, wanda ya zo a cikin bukatar. Yana da ban mamaki a gare ni dalilin da ya sa abokin ciniki ba zai iya cika darajar lokacinsa a filin nan da nan ba Asali. A sakamakon haka, lokacin da fakitin ya dawo, abokin ciniki yana da ƙimar lokaci 4 - lokacin da aka aiko da buƙatar (Asali), lokacin da uwar garken ya karɓi buƙatun (Karba), lokacin da uwar garken ya aika da amsa (Aika da) da lokacin karɓar amsa ta abokin ciniki - Yi zuwa (ba a cikin kunshin ba). Tare da waɗannan ƙimar za mu iya saita lokacin daidai.

Kunshin aikawa da lambar karɓa

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

sarrafa bayanai daga uwar garken

Gudanar da bayanai daga uwar garken yana kama da ayyukan ɗan Ingilishi daga tsohuwar matsalar Raymond M. Smallian (1978): “Mutum ɗaya ba shi da agogon hannu, amma akwai agogon bango daidai a gida, wanda ya wani lokacin manta da iska. Watarana ya manta ya sake fara agogo, sai yaje ya ziyarci abokinsa, ya kwana da shi, bayan ya dawo gida sai ya samu ya saita agogon daidai. Ta yaya ya yi hakan idan ba a san lokacin tafiya ba tukuna? Amsar ita ce: “Ban barin gida, mutum ya ɗaga agogo kuma ya tuna da matsayin hannaye. Zuwan abokinsa da barin baƙi, ya lura da lokacin isowa da tashi. Wannan ya ba shi damar gano tsawon lokacin da ya tafi. Komawa gida da duba agogo, mutum yana ƙayyade tsawon lokacin rashinsa. Ya rage daga wannan lokacin da ya yi ziyara, mutum ya gano lokacin da ya yi a hanya a can da dawowa. Ta hanyar ƙara rabin lokacin da aka kashe akan hanya zuwa lokacin barin baƙi, yana samun damar gano lokacin isowa gida kuma ya daidaita hannayen agogonsa daidai.

Nemo lokacin da uwar garken ke aiki akan buƙatar:

  1. Nemo lokacin tafiya fakiti daga abokin ciniki zuwa uwar garken: ((Iso - Asalin) - (Mai watsawa - Karɓa)) / 2
  2. Nemo bambanci tsakanin abokin ciniki da lokacin uwar garken:
    Karba - Asalin - ((Iso - Asalin) - (Transmit - Karɓa)) / 2 =
    2 * Karɓa - 2 * Asalin - iso + Asalin + watsa - Karɓa =
    Karɓa - Asalin - iso + watsa

Muna ƙara ƙimar da aka karɓa zuwa lokacin gida kuma muna jin daɗin rayuwa.

Sakamakon Sakamako

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)

Mai amfani mahada.

source: www.habr.com

Add a comment