19

I've written a ntp client in python to query a time server and display the time and the program executes but does not give me any results. I'm using python's 2.7.3 integrated development environment and my OS is Windows 7. Here is the code:

# File: Ntpclient.py
from socket import AF_INET, SOCK_DGRAM
import sys
import socket
import struct, time

# # Set the socket parameters 

host = "pool.ntp.org"
port = 123
buf = 1024
address = (host,port)
msg = 'time'


# reference time (in seconds since 1900-01-01 00:00:00)
TIME1970 = 2208988800L # 1970-01-01 00:00:00

# connect to server
client = socket.socket( AF_INET, SOCK_DGRAM)
client.sendto(msg, address)
msg, address = client.recvfrom( buf )

t = struct.unpack( "!12I", data )[10]
t -= TIME1970
print "\tTime=%s" % time.ctime(t)
fuxia
  • 62,923
  • 6
  • 54
  • 62
Howard Hugh
  • 199
  • 1
  • 2
  • 5

4 Answers4

26

Use ntplib:

The following should work on both Python 2 and 3:

import ntplib
from time import ctime
c = ntplib.NTPClient()
response = c.request('pool.ntp.org')
print(ctime(response.tx_time))

Output:

Fri Jul 28 01:30:53 2017
wovano
  • 4,543
  • 5
  • 22
  • 49
Anuj Gupta
  • 10,056
  • 3
  • 28
  • 32
20

Here is a fix for the above solution, which adds fractions of seconds to the implementation and closes the socket properly. As it's actually just a handful lines of code, I didn't want to add another dependency to my project, though ntplib admittedly is probably the way to go in most cases.

#!/usr/bin/env python
from contextlib import closing
from socket import socket, AF_INET, SOCK_DGRAM
import struct
import time

NTP_PACKET_FORMAT = "!12I"
NTP_DELTA = 2208988800  # 1970-01-01 00:00:00
NTP_QUERY = b'\x1b' + 47 * b'\0'  

def ntp_time(host="pool.ntp.org", port=123):
    with closing(socket( AF_INET, SOCK_DGRAM)) as s:
        s.sendto(NTP_QUERY, (host, port))
        msg, address = s.recvfrom(1024)
    unpacked = struct.unpack(NTP_PACKET_FORMAT,
                   msg[0:struct.calcsize(NTP_PACKET_FORMAT)])
    return unpacked[10] + float(unpacked[11]) / 2**32 - NTP_DELTA


if __name__ == "__main__":
    print time.ctime(ntp_time()).replace("  ", " ")
hynekcer
  • 14,942
  • 6
  • 61
  • 99
Michael
  • 7,316
  • 1
  • 37
  • 63
  • Old thread, but this is a good, quick solution for me instead of trying to figure out how to add modules into my build system (Petalinux). – Tom Myddeltyn Feb 07 '17 at 13:32
  • 3
    If you wonder what `'\x1b' + 47 * '\0'` stands for: https://stackoverflow.com/a/26938508/1422096 – Basj Jun 04 '18 at 14:14
  • The UDP socket is connectionless and therefore the is nothing that could or need be closed. The fractions of seconds work of course also with the original solution with `ntplib`. The advantage is only the removed dependency – hynekcer Feb 29 '20 at 18:33
  • @hynekcer Thanks for the update to modern times! About the socket: To my understanding it should be closed anyways, just to save local resources. The amount of open sockets is limited, and for long running processes, they shouldn't be leaked. – Michael Mar 05 '20 at 11:31
  • @Michael You are right that close() is better. It is a more complicated question for UDP (e.g. [this](https://stackoverflow.com/a/19820043/448474)) and I'm not sure now. A stateless socked is never *open*, even while `recvfrom()` is waiting for a response. (That can be verified by `netstat -nu` or `ss -nu`.) Examples for UDP use `.close()` in Python tutorials. If a stateful firewall is used e.g. to reject packets that are not related to previous outgoing traffic then some resources are allocated in a firewall for approximately two minutes after a connection. – hynekcer Mar 05 '20 at 14:07
4

It should be

msg = '\x1b' + 47 * '\0' 

Instead of

msg = 'time'

But as Maksym said you should use ntplib instead.

Chengy
  • 619
  • 1
  • 7
  • 17
0
msg = '\x1b' + 47 * '\0'
.......
t = struct.unpack( "!12I", msg )[10]
  • 1
    Although this may answer the question (I am not sure), it is usually better to include some explanation as to why (and how) this solves the problem. This especially applies when answering a question that was asked over a year ago and already has [a similar answer](http://stackoverflow.com/a/12664634/466862) – Mark Rotteveel Jun 01 '14 at 15:23