简单的SNTP python脚本

时间:2016-09-13 09:39:02

标签: python

我需要帮助才能完成以下脚本:

import socket
import struct
import sys
import time

NTP_SERVER = '0.uk.pool.ntp.org'
TIME1970 = 2208988800L

def sntp_client():
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = str.encode('\xlb' + 47 * '\0')
    client.sendto(data, (NTP_SERVER, 123))
    data, addr = client.recvfrom(1024)
    if data:
        print('Response received from:', addr)
    t = struct.unpack('!12I', data)[10]
    t -= TIME1970
    print('\tTime: %s' % time.ctime(t))

if __name__ == '__main__':
    sntp_client()

预期产出:

Response received from: ('80.82.244.120', 123)
      Time: Tue Sep 13 14:49:38 2016

问题是该程序没有提供任何输出。看起来它似乎停留在:

data, addr = client.recvfrom(1024)

我希望有人能帮助我。

3 个答案:

答案 0 :(得分:3)

以下是 Python Network Programming Cookbook 第二版的工作脚本:

import socket, struct, sys, time

NTP_SERVER = '0.uk.pool.ntp.org'
TIME1970 = 2208988800

def sntp_client():
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = '\x1b' + 47 * '\0'
    client.sendto(data.encode('utf-8'), (NTP_SERVER, 123))
    data, address = client.recvfrom(1024)
    if data: print('Response received from:', address)
    t = struct.unpack('!12I', data)[10] - TIME1970
    print('\tTime = %s' % time.ctime(t))

if __name__ == '__main__':
    sntp_client()

答案 1 :(得分:2)

应注意,accepted answer忽略了时间戳的分数部分,请参见例如IETF RFC5905,第13页。包括它的代码片段看起来像

import socket
import struct
import datetime

NTP_SERVER = '0.uk.pool.ntp.org'
NTP_DELTA = 2208988800 # given as system epoch (e.g. 1970-1-1) minus NTP epoch (1900-1-1) in [s]

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client:
    data = '\x1b' + 47 * '\0'
    client.sendto(data.encode('utf-8'), (NTP_SERVER, 123))
    data, _ = client.recvfrom(256)
    tx_s, tx_f = struct.unpack('!12I', data)[10:12] # seconds and fractional seconds
    tx_timestamp = (tx_s + float(tx_f)/2**32) - NTP_DELTA

print(datetime.datetime.fromtimestamp(tx_timestamp, datetime.timezone.utc)) 
# e.g.
# 2019-12-18 13:02:14.029521+00:00 # Note: UTC used here for output!

还请记住,这仅返回传输时间戳(tx;服务器发送数据包的时间)。如果您以毫秒为单位,则往返延迟可能会很重要(例如,请参见链接的RFC5905的第29页)。

旁注:socket docs建议在with上下文中打开套接字,以便在不再需要时可以正确关闭套接字。

答案 2 :(得分:1)

您编写的脚本没有任何问题,您需要查找服务器可能没有响应您的其他原因,例如防火墙设置。 我自己的python SNTP脚本几乎完全相同:

#!/bin/env python

import socket
import struct
import sys
import time

TIME1970 = 2208988800L      # Thanks to F.Lundh

pow2_31 = pow(2,31)
pow2_32 = pow(2,32)
pow2_16 = pow(2,16)

if len(sys.argv) < 2:
    sys.stderr.write("Usage : " + sys.argv[0] + " <SNTP server>")
    exit(1)

server = sys.argv[1]

client = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
data = '\x1b' + 47 * '\0'
time_start = time.time()
try:
    client.sendto( data, ( server, 123 ))
    client.settimeout(2)
except:
    print "server <%s> not recognized" % (server)
    exit(2)
try:
    data, address = client.recvfrom( 1024 )
except socket.timeout:
    print "timed out"
    exit(3)

if data:
    time_reply = (time.time() - time_start) * 1000
    print 'received %d bytes  from %s in %d ms :' % (len(data), address, time_reply)
    upacket = struct.unpack( '!48B', data )
    print upacket

用法: $ ./sntp_client.py 0.uk.pool.ntp.org

在154 ms内从('83 .170.75.28',123)收到48个字节: (28,3,3,236,0,0,1,171,0,0,3,0,20,139,208,232,219,177,86,148,230,192,1,15,0) ,0,0,0,0,0,0,0,219,177,88,27,60,214,85,212,219,177,88,27,60,238,157,39)