본문 바로가기
임베디드 개발/펌웨어

NTP 서버에서 시간 받아오기

by eteo 2023. 3. 31.

 

 

 

UTC

UTC (Coordinated Universal Time)는 지구상의 시간 표준을 정의하기 위한 국제 표준 시간으로, 국제원자시(TAI)를 기반으로 하되, 지구의 자전 속도 등에 따라 발생하는 차이를 보정하기 위해 윤초(Leap Second)를 삽입하여 보정된다. 국제 원자시란 세슘 원자의 진동 주파수를 기준으로 시간을 계산하며, 세계 각지의 원자 시계들의 평균값을 취하여 UTC가 유지된다. UTC 24시간을 기준으로 하며, 1시간은 60분으로 구성되고, 1분은 다시 60초로 구성되어 있다. 이러한 시간 단위들은 세계 각지에서 일관되게 사용된다. 아래 Unix Time, Windows Time, NTP time 전부 UTC를 기반으로 한다.

 

 

 

Unix Time

Unix Time은 컴퓨터 시스템에서 날짜와 시간을 표현하고 계산하는 데 널리 사용되는 표현방식으로 1970 1 1 00:00:00 UTC부터 경과된 초 수 32비트 정수로 나타낸다. 이것을 POSIX 시간이라고도 한다.

 

 

 

Windows Time

Windows API에서 사용되는 Windows TimeUnix Time과 다른 방식을 사용하는데, 1601 1 1일부터 경과된 100나노초 단위 시간을 카운트하여 64비트 정수로 나타낸다

 

 

 

 

 

 

NTP

NTP (Network Time Protocol)는 인터넷상의 컴퓨터들이 정확한 시간을 유지할 수 있도록 돕는 프로토콜이다. 컴퓨터는 자체 RTC를 이용해서 동작 후 경과된 시간을 계산할 수 있는데, 이 시스템 시간을 실제 시간과 맞추기 위해 정확한 시간을 알고 있는 서버로부터 시간 정보를 받아와 동기화 시킨다. 이때 사용되는 프로토콜이 바로 NTP이다. NTP를 사용해 컴퓨터의 시간을 주기적으로 동기화하면 컴퓨터가 네트워크에서 독립적으로 작동하는 경우에도 정확한 시간을 유지할 수 있다. NTP는 응용계층으로 UDP/IP를 사용하고, UDP port 123번을 사용한다.

 

 

 

 

 

 

NTP 서버 계층 구조

https://nyyang.tistory.com/22

상위 계층일수록 시간원에 가깝다. 하지만 상위계급에 액세스하는 것보다 가까운 서버에 액세스하는 쪽이 정확도를 높일 수 있다. NTP 서버와 클라이언트의 사각차는 가는길과 오는길의 전파 시간이 같다는 것을 전제로하여 계산되므로 멀리 있을수록 양쪽의 시간이 달라질 확률이 높기 때문이다.

 

 

 

 

 

 

 

NTP 프로토콜

 

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN  |Mode |    Stratum    |     Poll      |  Precision   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Root  Delay                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Root  Dispersion                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Reference Identifier                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                    Reference Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                    Originate Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                     Receive Timestamp (64)                     |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                     Transmit Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                  Key Identifier (optional)                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                                                               |
|                 Message Digest (optional)                      |
|                                                               |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

  • LI (Leap Indicator): 2비트. 윤초(Leap Second)의 발생 여부를 나타낸다. 00: 윤초 없음, 01: 양의 윤초 발생, 10: 음의 윤초 발생, 11:Reserved
  • VN (Version Number): 3비트. NTP 버전 번호를 나타낸다. 1~4
  • Mode: 3비트. NTP 모드를 나타낸다. 8개의 모드가 있는데 그 중 Client는 011, Server는 100이다.
  • Stratum: 8비트. NTP 서버의 계층 레벨을 나타낸다. Stratum 1~15
  • Poll: 8비트. 패킷 전송 주기를 나타낸다. 초 단위이며 2의 거듭제곱으로 나타내는데 통상적으로는 6(2^6=64초)에서 10(2^10=1024초)의 값을 갖는다.
  • Precision: 8비트. 서버의 정밀도를 나타낸다. 초 단위이며 소수점이하 의미가 있는 최소 비트 수에 마이너스를 붙인 값을 설정한다. 예를 들명, 소수점 이하 10비트째까지 의미가 있을때는 2^-10=0.0009765625초의 정밀도가 된다.
  • Root Delay: 32비트. 서버에서 Stratum1까지의 왕복 지연 합계를 나타낸다. 32비트 부호가 있는 고정소수점으로, 상위 16비트가 초이며, 하위 16비트가 소수점 이하이다.
  • Root Dispersion: 32비트. 서버에서 Stratum1까지의 명목적인 상대 오차 합계를 나타낸다. 32비트 부호 없는 고정소수점으로, 상위 16비트가 초이며, 하위 16비트가 소수점 이하이다.
  • Reference Identifier: 32비트. 시간원 서버의 ID를 나타낸다.
  • Reference Timestamp: 64비트. 시스템 시계가 마지막으로 동기화된 시간
  • Originate Timestamp: 64비트. 클라이언트의 요청 패킷이 생성된 시간
  • Receive Timestamp: 64비트. 서버가 클라이언트의 요청을 수신한 시간
  • Transmit Timestamp: 64비트. 서버에서 응답 패킷이 생성된 시간. 상위 32비트가 정수, 하위 32비트가 소수점 파트이다. 나머지 Timestamp 구조도 똑같다.
  • Key Identifier: 32비트. 인증 키를 나타내며 optioanl
  • Message Digest: 128비트. 패킷의 무결성 검증을 위한 optional 필드

 

 

 

 

import socket
import struct
import time

NTP_SERVER = "kr.pool.ntp.org"  # NTP 서버 주소

def get_ntp_time():
    # NTP 패킷 구성
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = b'\x1b' + 47 * b'\0'
    client.sendto(data, (NTP_SERVER, 123))

    # NTP 패킷 수신
    data, address = client.recvfrom(1024)
    if data:
        # NTP 패킷에서 시간 추출
        timestamp = struct.unpack("!12I", data)[10]
        timestamp -= 2208988800  # 1970년 1월 1일 기준으로 초 단위로 변환
        return time.ctime(timestamp)  # 문자열 형태로 변환하여 반환

if __name__ == '__main__':
    print(get_ntp_time())

 

  • UDP 소켓을 생성한다.
  • 패킷은 48byte를 생성하는데 앞의 1바이트는 0x1b로 채우고 뒷부분은 전부 0으로 채운다. NTP 서버 123번 포트에 요청 패킷을 송신한다.
  • recvfrom() 함수를 사용하여, 최대 1024byte까지 데이터를 수신해 data에 넣는다.
  • struct.unpack() 함수를 사용해 패킷 데이터에서 시간 정보를 추출한다. 인수인 "!12I" 형식으로 언패킹하는데 "!"는 네이트워크 바이트 순서로 읽으라는 의미이며 "12I"는 unsigned int(4byte) 변수가 12개라는 의미로, 총 48바이트를 읽어 12개의 unsinent int로 언패킹 한다.
  • 관심있는 부분은 Transmit Timestamp인데 40~47바이트에 해당하고, 그중에 정수부분은 40~43이므로 인덱스 10을 사용해 접근한다.
  • NTP time은 1900년 1월 1일 부터 경과한 시간을 표시하므로 이를 Unix Time으로 변환시키기 위해 추출한 시간정보에서 2208988800을 빼줌으로써 1970년 1월 1일 이후 경과한 시간으로 변환한다. (차이는 70년이고 중간에 윤년이 17개 있다.)
  • 마지막으로 time.ctime() 함수를 사용하여 변환된 Unix time 단위 시간을 문자열 형태로 반환한다. 이때 반환되는 형식은 "Fri Mar 10 22:44:49 2023"과 같은 형식이다.

 

Reference:

https://kwonkyo.tistory.com/428#gsc.tab=0%EF%BB%BF

http://magazine.hellot.net/magz/article/articleDetail.do?flag=all&showType=showType1&articleId=ARTI_000000000035076&articleAllListSortType=sort_1&page=1&selectYearMonth=200911&subCtgId=