د ساده NTP پیرودونکي لیکل

سلام هابروزر. نن زه غواړم د دې په اړه وغږیږم چې څنګه خپل ساده NTP پیرودونکي ولیکئ. اساسا، خبرې اترې به د پاکټ جوړښت او د NTP سرور څخه د ځواب پروسس کولو طریقې ته وګرځي. کوډ به په Python کې ولیکل شي، ځکه چې ماته داسې ښکاري چې د داسې شیانو لپاره په ساده ډول کومه غوره ژبه نشته. پوهان به د ntplib کوډ سره د کوډ ورته والی یادونه وکړي - زه له دې څخه "الهام شوی" وم.

نو په حقیقت کې NTP څه شی دی؟ NTP د دقیق وخت سرورونو سره د متقابل عمل پروتوکول دی. دا پروتوکول په ډیری عصري ماشینونو کې کارول کیږي. د مثال په توګه، په وینډوز کې د w32tm خدمت.

په مجموع کې د NTP پروتوکول 5 نسخې شتون لري. لومړی، نسخه 0 (1985، RFC958))، اوس مهال متروک ګڼل کیږي. اوس نوي کارول کیږي، لومړی (1، RFC1988)، دوهم (1059، RFC2)، دریم (1989، RFC1119) او څلورم (3، RFC1992). 1305-4 نسخې د یو بل سره مطابقت لري؛ دوی یوازې د سرور عملیات الګوریتم کې توپیر لري.

د بسته بندۍ بڼه

د ساده NTP پیرودونکي لیکل

د لیپ شاخص (د سمون شاخص) - یوه شمیره چې د دویمې همغږۍ په اړه خبرداری څرګندوي. مانا:

  • 0 - هیڅ سمون نشته
  • ۱- د ورځې وروستۍ دقیقې ۶۱ ثانیې لري
  • ۲ – د ورځې وروستۍ دقیقې ۵۹ ثانیې لري
  • 3 - د سرور خرابوالی (وخت همغږي نه دی)

نسخه شمیره (نسخه شمیره) – د NTP پروتوکول نسخه شمیره (1-4).

فېشن (موډ) - د پاکټ لیږونکي عملیاتي حالت. ارزښت له 0 څخه تر 7 پورې، خورا عام:

  • ۳ – مشتري
  • ۴- سرور
  • 5 - د خپرونې حالت

سټراټم (د پرت کولو کچه) - د سرور او حوالې ساعت تر مینځ د مینځنۍ پرتونو شمیر (1 - سرور مستقیم د حوالې ساعت څخه ډیټا اخلي ، 2 - سرور د سرور څخه ډیټا اخلي د 1 پرت سره ، او داسې نور).
حوض یو لاسلیک شوی عدد دی چې د پرله پسې پیغامونو تر منځ د اعظمي وقفې استازیتوب کوي. د NTP پیرودونکی دلته هغه وقفه مشخصوي په کوم کې چې دا تمه لري چې سرور ته رایه ورکړي، او د NTP سرور هغه وقفه مشخصوي چې په هغه کې یې د رایې ورکولو تمه لري. ارزښت د ثانیو د بائنری لوګاریتم سره مساوي دی.
Precision (دقت) یو لاسلیک شوی عدد دی چې د سیسټم ساعت دقت استازیتوب کوي. ارزښت د ثانیو د بائنری لوګاریتم سره مساوي دی.
د روټ ځنډ (د سرور ځنډ) – هغه وخت چې د ساعت لوستلو لپاره د NTP سرور ته د رسیدو لپاره وخت نیسي، د ثانیو د ټاکل شوي ټکي په توګه.
د ریښو خپریدل (د سرور خپریدل) - د NTP سرور ساعت لوستلو خپرول د یو ټاکلي نقطې سره د څو ثانیو په توګه.
Ref id (سرچینه پیژندونکی) - د ساعت ID. که چیرې سرور 1 stratum ولري، نو د ref id د اټومي ساعت نوم دی (4 ASCII حروف). که سرور بل سرور کاروي، نو بیا د ریف ID د دې سرور پته لري.
وروستي 4 ساحې د وخت نمایندګي کوي - 32 بټونه - د عدد برخه، 32 بټونه - جزوی برخه.
ماخذ - په سرور کې وروستي ساعت لوستل.
پیدا کول - هغه وخت کله چې پاکټ لیږل شوی و (د سرور لخوا ډک شوی - پدې لاندې نور).
ترلاسه کول - هغه وخت چې بسته د سرور لخوا ترلاسه شوه.
ولېږدوي - پیرودونکي ته د سرور څخه د کڅوړې لیږلو وخت (د پیرودونکي لخوا ډک شوی، په لاندې کې نور).

موږ به وروستي دوه ساحې په پام کې ونه نیسو.

راځئ چې زموږ بسته ولیکئ:

د بسته کوډ

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

سرور ته د پاکټ لیږلو (او ترلاسه کولو) لپاره، موږ باید وړتیا ولرو چې دا په بایټ سري بدل کړو.
د دې (او ریورس) عملیاتو لپاره، موږ به دوه افعال ولیکئ - pack() او unpack():

د بسته بندۍ فعالیت

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

د خلاصولو فعالیت

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

د سست خلکو لپاره ، د غوښتنلیک په توګه - کوډ چې کڅوړه په ښکلي تار بدلوي

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)

سرور ته د کڅوړې لیږل

د ډکو ساحو سره یوه کڅوړه باید سرور ته واستول شي نسخه, فېشن и ولېږدوي. د ولېږدوي تاسو باید په محلي ماشین کې اوسنی وخت مشخص کړئ (د جنوري 1، 1900 راهیسې د ثانیو شمیر)، نسخه - د 1-4 څخه هر یو، حالت - 3 (د پیرودونکي حالت).

سرور، د غوښتنې په منلو سره، د NTP پاکټ ټولې ساحې ډکوي، په ساحه کې کاپي کوي پیدا کول ارزښت څخه ولېږدوي، کوم چې په غوښتنه کې راغلی. دا زما لپاره یو راز دی چې ولې پیرودونکي نشي کولی سمدلاسه په ساحه کې د خپل وخت ارزښت ډک کړي پیدا کول. د پایلې په توګه، کله چې کڅوړه بیرته راشي، پیرودونکي د 4 وخت ارزښت لري - هغه وخت چې غوښتنه لیږل شوې وه (پیدا کولهغه وخت چې سرور غوښتنه ترلاسه کړه (ترلاسه کول)، هغه وخت چې سرور ځواب لیږلی (ولېږدوي) او کله چې پیرودونکي ځواب ترلاسه کړ - را رسيدل (په بسته کې نه). د دې ارزښتونو په کارولو سره موږ کولی شو سم وخت وټاکو.

د بسته بندي لیږلو او ترلاسه کولو کوډ

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

د سرور څخه د معلوماتو پروسس کول

د سرور څخه د معلوماتو پروسس کول د ریمونډ ایم سمولیان (1978) د زړې ستونزې څخه د انګلیسي سړي کړنو سره ورته دي: "یو سړی د لاس ساعت نه درلود، مګر په کور کې یو دقیق دیوال ساعت و، چې هغه کله کله هیر کړی و. باد ته یوه ورځ بیا د ساعت تیریدل هیر کړل، د خپل ملګري لیدو ته لاړ، ماښام یې ورسره تیر کړ او کله چې کور ته راستون شو، ساعت یې په سمه توګه ترتیب کړ. هغه څنګه دا اداره کوله که چیرې د سفر وخت مخکې نه و پیژندل شوی؟ ځواب دا دی: "کله چې له کوره وځي ، یو سړی خپل ساعت تیروي او په یاد لري چې لاسونه په کوم حالت کې دي. د یو ملګري د راتګ او د میلمنو په وتلو سره، هغه د هغه د راتګ او وتلو وخت یادوي. دا هغه ته اجازه ورکوي چې معلومه کړي چې هغه څومره وخت لیدلی و. کور ته راستنیدل او ساعت ته ګوري، یو سړی د هغه د نشتوالي موده ټاکي. د دې وخت څخه د هغه وخت په راټیټولو سره چې هغه په ​​​​زیارت کې تیر کړی، یو سړی هغه وخت پیدا کوي چې هلته او بیرته سفر کې تیر کړي. د میلمنو د وتلو په وخت کې د سړک نیمایي وخت اضافه کولو سره، هغه دا فرصت ترلاسه کوي چې کور ته د رسیدو وخت ومومي او د خپل ساعت لاسونه د هغې مطابق تنظیم کړي.

هغه وخت ومومئ چې سرور په غوښتنه کار کوي:

  1. د پیرودونکي څخه سرور ته د کڅوړې سفر وخت ومومئ: ((رسیدل – پیل کول) – (لیږدول – ترلاسه کول)) / 2
  2. د پیرودونکي او سرور وخت ترمنځ توپیر ومومئ:
    ترلاسه کول - پیل کول - ((رسیدل - پیل کول) - (لیږدول - ترلاسه کول)) / 2 =
    2 * ترلاسه کول - 2 * پیل کول - رسیدل + پیل کول + لیږدول - ترلاسه کول =
    ترلاسه کول - پیل کول - رسیدل + لیږدول

موږ په محلي وخت کې د پایلې ارزښت اضافه کوو او له ژوند څخه خوند اخلو.

د پایلې پایله

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)

ګټور لینک.

سرچینه: www.habr.com

Add a comment