μλ
νμΈμ, Habrausersμ
λλ€. μ€λμ κ°λ¨ν NTP ν΄λΌμ΄μΈνΈλ₯Ό μμ±νλ λ°©λ²μ λν΄ μ΄μΌκΈ°νκ³ μΆμ΅λλ€. κΈ°λ³Έμ μΌλ‘ λνλ ν¨ν·μ ꡬ쑰μ NTP μλ²μ μλ΅μ΄ μ²λ¦¬λλ λ°©μμΌλ‘ μ νλ©λλ€. μ½λλ PythonμΌλ‘ μμ±λ κ²μ
λλ€. μλνλ©΄ μ μκ°μλ κ·Έλ¬ν μμ
μ λ μ’μ μΈμ΄κ° μκΈ° λλ¬Έμ
λλ€. μ λ¬Έκ°λ€μ μ΄ μ½λμ ntplib μ½λμ μ μ¬μ±μ μ£Όλͺ©ν κ²μ
λλ€. μ λ μ΄ μ½λμμ "μκ°μ μ»μμ΅λλ€".
κ·Έλ λ€λ©΄ NTPλ 무μμΌκΉμ? NTPλ νμ μλ²μ ν΅μ νκΈ° μν νλ‘ν μ½μ
λλ€. μ΄ νλ‘ν μ½μ λ§μ μ΅μ κΈ°κ³μμ μ¬μ©λ©λλ€. μλ₯Ό λ€μ΄ Windowsμ w32tm μλΉμ€μ
λλ€.
NTP νλ‘ν μ½μλ μ΄ 5κ°μ§ λ²μ μ΄ μμ΅λλ€. 첫 λ²μ§Έ λ²μ μΈ λ²μ 0(1985λ , RFC958)μ νμ¬ μ¬μ©λμ§ μλ κ²μΌλ‘ κ°μ£Όλ©λλ€. νμ¬ μ¬μ© μ€μΈ μ΅μ λ²μ μ λ²μ 1(1988λ , RFC1059), λ²μ 2(1989λ , RFC1119), λ²μ 3(1992λ , RFC1305), κ·Έλ¦¬κ³ λ²μ 4(1996λ , RFC2030)μ λλ€. λ²μ 1λΆν° 4κΉμ§λ μλ‘ νΈνλλ©°, μλ μκ³ λ¦¬μ¦λ§ λ€λ¦ λλ€. μλ².
ν¨ν· νμ

λμ½ νμκΈ° (μμ νμ)λ μ€μ΄ κ²½κ³ λ₯Ό λνλ΄λ μ«μμ
λλ€. μλ―Έ:
- 0 - μμ μμ
- 1 - ν루μ λ§μ§λ§ 61λΆμ XNUMXμ΄μ λλ€.
- 2 - ν루μ λ§μ§λ§ 59λΆμ XNUMXμ΄μ λλ€.
- 3 - μλ² μ€λ₯(λκΈ°ν μκ° μ΄κ³Ό)
λ²μ λ²νΈ (λ²μ λ²νΈ) β NTP νλ‘ν μ½ λ²μ λ²νΈ(1-4)μ λλ€.
λͺ¨λ (λͺ¨λ) β ν¨ν· μ‘μ μμ μλ λͺ¨λμ λλ€. 0λΆν° 7κΉμ§μ κ°(κ°μ₯ μΌλ°μ μ):
- 3 - ν΄λΌμ΄μΈνΈ
- 4 - μλ²
- 5 - λ°©μ‘ λͺ¨λ
μ§μΈ΅ (κ³μΈ΅ν μμ€) - μλ²μ μ°Έμ‘° ν΄λ‘ μ¬μ΄μ μ€κ° κ³μΈ΅ μ(1 - μλ²κ° μ°Έμ‘° ν΄λ‘μμ μ§μ λ°μ΄ν°λ₯Ό κ°μ Έμ΄, 2 - μλ²κ° λ 벨 1μ μλ²μμ λ°μ΄ν°λ₯Ό κ°μ Έμ΄ λ±).
ν μ°μ λ©μμ§ μ¬μ΄μ μ΅λ κ°κ²©μ λνλ΄λ λΆνΈ μλ μ μμ
λλ€. NTP ν΄λΌμ΄μΈνΈλ μ¬κΈ°μμ μλ²λ₯Ό ν΄λ§ν κ²μΌλ‘ μμλλ κ°κ²©μ μ§μ νκ³ , NTP μλ²λ ν΄λ§μ΄ μμλλ κ°κ²©μ μ§μ ν©λλ€. κ°μ μ΄μ μ΄μ§ λ‘κ·Έμ κ°μ΅λλ€.
μ λ°μ± (μ λ°λ)λ μμ€ν
μκ³μ μ νλλ₯Ό λνλ΄λ λΆνΈ μλ μ μμ
λλ€. κ°μ μ΄μ μ΄μ§ λ‘κ·Έμ κ°μ΅λλ€.
λ£¨νΈ μ§μ° (μλ² λκΈ° μκ°)μ μκ³κ° NTP μλ²μ λλ¬νλ λ° κ±Έλ¦¬λ μκ°(μ΄ λ¨μ)μ
λλ€.
λΏλ¦¬ λΆμ° (μλ² λΆμ°) - NTP μλ² μκ³μ κ³ μ μμμ μ(μ΄) λΆμ°μ
λλ€.
μ°Έμ‘° ID (μμ€ ID) - μκ³ IDμ
λλ€. μλ²μ Stratum 1μ΄ μλ κ²½μ° ref idλ μμ μκ³μ μ΄λ¦(ASCII λ¬Έμ 4κ°)μ
λλ€. μλ²κ° λ€λ₯Έ μλ²λ₯Ό μ¬μ©νλ κ²½μ° μ°Έμ‘° 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 μ€ νλ, λͺ¨λ - XNUMX(ν΄λΌμ΄μΈνΈ λͺ¨λ)μ μ§μ ν΄μΌ ν©λλ€.
μμ²μ λ°μ μλ²λ 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)
μλ²μμ λ°μ΄ν° μ²λ¦¬
μλ²μ λ°μ΄ν° μ²λ¦¬λ Raymond M. Smallian(1978)μ μ€λλ λ¬Έμ μ λν μκ΅ μ μ¬μ νλκ³Ό μ μ¬ν©λλ€. βν μ¬λμ μλͺ©μκ³κ° μμμ§λ§ μ§μλ μ νν λ²½μκ³κ° μμλλ° λλλ‘ μμ΄λ²λ Έμ΅λλ€. λ°λμ νΌμ°λ€. μ΄λ λ κ·Έλ μκ³λ₯Ό λ€μ μμνλ κ²μ μμ΄λ²λ¦¬κ³ μΉκ΅¬λ₯Ό λ§λλ¬ κ°μ κ·Έμ ν¨κ» μ λ μ 보λ΄κ³ μ§μ λμμμ λ μκ³λ₯Ό μ ννκ² λ§μΆμμ΅λλ€. μ¬ν μκ°μ 미리 μμ§ λͺ»νλ€λ©΄ κ·Έλ μ΄λ»κ² μ΄κ²μ ν μ μμμ΅λκΉ? λλ΅μ μ΄λ μ΅λλ€. βμ§μ λμ€λ©΄ μ¬λμ΄ μκ³ νμ½μ κ°κ³ λ°λμ μμΉλ₯Ό ββκΈ°μ΅ν©λλ€. μΉκ΅¬μκ² μμ μλμ λ λλ©΄μ κ·Έλ λμ°© λ° μΆλ° μκ°μ κΈ°λ‘ν©λλ€. μ΄λ₯Ό ν΅ν΄ κ·Έκ° μΌλ§λ μ€λ«λμ μ리λ₯Ό λΉμ λμ§ μ μ μμ΅λλ€. μ§μ λμμ μκ³λ₯Ό 보면 λΆμ¬ κΈ°κ°μ κ²°μ ν©λλ€. μ΄ μκ°μμ μμ μ΄ λ°©λ¬Έν μκ°μ λΉΌλ©΄ κ·Έκ³³μ μ€κ°λ κΈΈμ λ³΄λΈ μκ°μ μ μ μλ€. μλμ λ λ μκ°μ κΈΈμμ λ³΄λΈ μκ°μ μ λ°μ μΆκ°ν¨μΌλ‘μ¨ κ·Έλ μ§μ λμ°©νλ μκ°μ νμΈνκ³ μ΄μ λ°λΌ μκ³ λ°λμ μ‘°μ ν μ μλ κΈ°νλ₯Ό μ»μ΅λλ€.
μλ²κ° μμ²μ μ²λ¦¬ν μκ°μ μ°ΎμΌμΈμ.
- ν΄λΌμ΄μΈνΈμμ μλ²κΉμ§μ ν¨ν· μ΄λ μκ° μ°ΎκΈ°: ((λμ°© - λ°μ ) - (μ‘μ - μμ )) / 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)
μ λ₯ν .
μΆμ² : habr.com
