์๋
ํ์ธ์, 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 ์๋ฒ๋ ํด๋ง์ด ์์๋๋ ๊ฐ๊ฒฉ์ ์ง์ ํฉ๋๋ค. ๊ฐ์ ์ด์ ์ด์ง ๋ก๊ทธ์ ๊ฐ์ต๋๋ค.
Precision (์ ๋ฐ๋)๋ ์์คํ
์๊ณ์ ์ ํ๋๋ฅผ ๋ํ๋ด๋ ๋ถํธ ์๋ ์ ์์
๋๋ค. ๊ฐ์ ์ด์ ์ด์ง ๋ก๊ทธ์ ๊ฐ์ต๋๋ค.
๋ฃจํธ ์ง์ฐ (์๋ฒ ๋๊ธฐ ์๊ฐ)์ ์๊ณ๊ฐ 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)