简介:这是一份基于NTP/SNTP协议实现网络时间同步的服务器程序源码包,面向需要理解SNTP协议实现原理,或希望快速搭建简易时间同步服务的开发者。代码以C语言编写,覆盖时间同步算法、网络接口、配置管理等核心模块,通过UDP 123端口与客户端交互,适用于学习网络协议编程以及嵌入式设备、局域网的轻量级时钟同步场景。
包内共6个文件,包括3个C源文件、2个头文件和1个Makefile,分别承担协议逻辑处理、接口声明与编译构建功能,压缩包整体仅4KB,结构紧凑,便于逐文件阅读和二次修改。目前已有189人学习下载。
资源的主要价值在于可对照源码理解SNTP报文解析、时钟校准和服务器响应流程,同时借助Makefile快速编译部署;在此基础上,还能为逐步扩展支持完整NTP功能,或将其集成到自身项目中提供一份可直接使用的代码参考。
1. 把时间服务器装回内网:SNTP 服务器程序到底解决什么
如果你维护过 Windows 域环境、园区网络或者一整片脱网设备,大概率撞过“时间跳了”的报修:设备日志里的时间戳倒着排,证书校验报有效期未开始,数据库主从复制因为时间差直接拒绝写入。问题根源大多是内网里没有一个统一时间基准,公网的 NTP 服务器对很多生产网段根本不可达,或者被安全策略挡在门外。SNTP 服务器程序要解决的就是这件事:在内网起一个最小的、兼容 NTP 客户端的时间服务,让所有设备先能把时间对上。它适合不想引入完整 NTP 治理方案、又必须做基础时间收敛的场景,解压、配置、验证三步就能跑通。
2. ntp.rar 这类发布包里装的是什么:SNTP 与 NTP 的关系和选型
拿到一个以 ntp.rar 命名的压缩包,别被文件名里的“ntp”误导。绝大多数这类源码包实现的其实是 SNTP,而不是完整 NTP。这不是偷工减料,是作者比谁都清楚:内网时间服务器的目标是让客户端时间快速收敛,而不是在高抖动链路上做微秒级同步。在动手之前,先把协议边界摸清楚,才能判断这个包拿过来能不能直接用,用多久不出问题。
2.1 SNTP 不是 NTP 的精简版,而是“够用版”
SNTP 的全称是 Simple Network Time Protocol,RFC 4330 定义了它,脱胎于更早的 RFC 2030。它和 NTP 复用同一个 UDP 端口 123,报文格式也完全一致,所以 SNTP 服务器可以和标准 NTP 客户端互通,Windows 自带的 W32Time 服务、Linux 的 ntpdate、各种网络摄像头都能直接对接。
区别在状态机。完整 NTP 实现了复杂的时钟过滤算法、抖动评估、候选服务器选择和时钟抑制逻辑,能在大范围网络延迟变化中保持高精度。SNTP 把它们全部砍掉,只保留最基本的时间戳交换和单次偏移计算。SNTP 客户端拿到服务器返回的时间戳后,做一次加减法就完成同步;SNTP 服务器也没有复杂的选择算法,它自己同步到上游,再把本地时钟的时间戳照抄给下游。
所以判断标准很简单:如果你的网络环境相对干净,设备数量几十台到几百台,时间误差控制在秒级甚至毫秒级就够用,SNTP 完全胜任。如果你在建金融交易系统、电力调度或者科研采集,需要亚毫秒级对齐并且要完整审计链,才需要讨论完整 NTP。最常见的误用是用 SNTP 去解决应该在 NTP 层面解决的问题,比如客户端数量巨大、公网链路抖动剧烈、或者需要 RFC 5905 的 Autokey 认证。这时候问题不在 SNTP 本身,而是工具选错了。
2.2 自建 SNTP 服务器的三种主流形态,你该选哪一种
我在实际项目里见过三类 SNTP 服务器,覆盖了绝大多数场景。
第一类是 Windows 自带的 W32Time 服务。在 Windows Server 2008 和 2012 时代,这个服务严格说只实现了 SNTP 级别的功能,并不是后来 Windows Server 2016 以后那种增强过的版本。它的价值在于零额外安装成本,域内机器天然会向域控请求时间。你可以把一台 Windows Server 配置成 SNTP 服务器,让内网 Windows 设备都指向它。很多老项目里的“win ser 2008 ntp 配置”指的就是这条路。
第二类是 Linux 上的 ntpd 或 chronyd。chronyd 是现在的主力,它既可以完整 NTP 模式运行,也可以降级为 SNTP 式简单响应。大多数情况下我建议直接用 chrony 的默认配置,严谨地说它不是“SnTP 程序”,但对内网客户端表现出的行为就是 SNTP:收到请求,返回时间戳,不维护复杂对等关系。
第三类才是真正对应 ntp.rar 标题的东西:一个独立的、可移植的 SNTP 服务器程序。可能是 C 写的几十 KB 可执行文件,也可能是几十行 Python 脚本。它的典型价值是能塞进嵌入式设备、路由器、或一台没有包管理权限的旧服务器。发布形态经常是压缩包,解压后直接运行,不需要装依赖。我后面会给出一个能直接照抄的 Python 版本,让你彻底搞懂包内部在交换什么。
2.3 为什么内网用 SNTP 而不是直接跑完整 NTP
完整 NTP 代价不在于 CPU,而在于运维复杂度。要跑出高精度,你得为它维护上游时间源列表、监控每个上游的延迟和抖动、理解并处理时钟跳变告警。一台几十台规模的内网,要的只是“所有人看的是同一个时间”,而不是“这个时间离真实 UTC 有多近”。SNTP 把复杂度压缩到了极致:上游一个地址,本地一个端口,客户端来问就答,不来问就不管。
另一个理由是可移植性。内网经常有老设备,系统是裁剪过的 Linux,或者锁死的 Windows 嵌入式版本,装不了 chrony、编译不了 ntpd。一个静态编译的 SNTP 服务端丢进去就能跑,这是它至今还在被持续寻找的原因。我见过一台跑自动化产线的工控机,系统是 Windows XP 定制镜像,唯一绕过去的方式就是在另一台机器上起 SNTP 服务,然后给工控机配置 time.windows.com 指向内网 IP,这个方案稳定运行了五年没有动过。
3. 动手搭一个 SNTP 服务器程序:Windows 配置与 Python 自实现
3.1 Windows 系统 NTP 时间服务器搭建:Win Server 2008 NTP 配置(含注册表)
先看 Windows 这条路,尤其是老项目里还在服役的 Windows Server 2008。在 2008 上,时间服务注册表路径和 2012、2016 基本一致,但要注意:默认情况下 W32Time 虽然自动启动,不代表它对外提供时间服务。必须把 Type 改成 NTP,并且每次改完都要重启服务才会生效。
用管理员权限打开命令行,依次执行下面几段。先配置服务和上游参数:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" /v Type /t REG_SZ /d NTP /f reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" /v NtpServer /t REG_SZ /d "ntp.aliyun.com,0x1" /f reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" /v AnnounceFlags /t REG_DWORD /d 5 /f第一行把服务模式改为 NTP。改完后这台机器既是时间客户端又是时间服务器。第二行指定上游服务器,注意这里的格式:域名后面跟逗号,再跟一个十六进制标志。0x1 表示使用注册表里的 SpecialPollInterval 作为轮询间隔,而不是走默认的域控发现机制。第三行的 AnnounceFlags 设为 5,意思是这台机器可以作为可靠时间源对外宣告,否则部分 Windows 客户端可能在发现时间服务器时跳过它。
改完注册表后重启服务再强制同步一次:
net stop w32time net start w32time w32tm /resync /rediscover w32tm /query /status /verbose最后一条命令是验证。看输出里的 Stratum 字段,如果显示 1 到 5 之间,说明服务正常工作;如果显示 16,说明上游不可达。这里容易踩一个坑:2008 上的 W32Time 对上游的响应质量很敏感,上游不可达时它不会自动切换到备用服务器,所以 NtpServer 里可以多写几个地址,用空格分隔,例如:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" /v NtpServer /t REG_SZ /d "ntp.aliyun.com,0x1 cn.pool.ntp.org,0x1" /f我把这个配置贴在很多内网项目里,Windows 7、Windows 10 客户端的同步效果都不错,误差通常能稳定在几百毫秒内。如果你的网络完全脱网,没有任何上游,那 NtpServer 这一项可以不给,但这时候服务器的时间是错的,下游得到的也是错时间。脱网环境必须搭配硬件时钟源,后面第 4 章细说。
3.2 用 Python 写一个最简 SNTP 服务器程序
我自己做过一次的完整方案,是四十行 Python 写了个 SNTP 服务端,跑在一台只有 Python 2.7 的旧 CentOS 上,给生产网段设备供时。整个过程很简单,但前提是你得理解 48 字节报文是怎么组装的。下面这个版本兼容 Python 3,关键字段都有注释。
import socket import struct import time # NTP 时间戳从 1900 年 1 月 1 日起算,Unix 时间戳从 1970 年起算 NTP_UNIX_OFFSET = 2208988800 def current_ntp_timestamp(): ts = time.time() seconds = int(ts) + NTP_UNIX_OFFSET fraction = int((ts - int(ts)) * (1 << 32)) return seconds, fraction def main(host='0.0.0.0', port=123): # SOCK_DGRAM 对应 UDP,SNTP 客户端默认访问 123/udp with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as srv: srv.bind((host, port)) while True: data, addr = srv.recvfrom(1024) if len(data) < 48: continue # SNTP 头部固定 48 字节,短包直接丢弃 req = bytearray(data[:48]) # 拿到客户端请求 resp = bytearray(48) resp[0] = 0x1C # LI=0, VN=4, Mode=4,表示服务器响应 resp[1] = 4 # Stratum 4,本地普通服务器,不直接挂原子钟 resp[2] = 6 # Poll 间隔指示,2^6 = 64 秒 resp[3] = 0xEC # Precision,补码表示 2^-20 秒 sec, frac = current_ntp_timestamp() # Reference Timestamp,表示本服务器最后一次同步时刻 resp[16:20] = struct.pack('>I', sec) resp[20:24] = struct.pack('>I', frac) # Originate Timestamp 要回填客户端请求里的 Transmit Timestamp resp[24:28] = req[40:44] resp[28:32] = req[44:48] # Receive Timestamp 和 Transmit Timestamp 都填处理时刻 resp[32:36] = struct.pack('>I', sec) resp[36:40] = struct.pack('>I', frac) resp[40:44] = struct.pack('>I', sec) resp[44:48] = struct.pack('>I', frac) srv.sendto(resp, addr) if __name__ == '__main__': main()运行方式很简单:在 Linux 上用 root 执行python3 sntp_server.py,在 Windows 上用管理员身份运行。绑定 123 端口必须要有管理员或 root 权限,这是第一个常被忽略的问题。
逐段解释关键设计。第一字节0x1C是 SNTP 报文的状态标记:二进制001 100 100,从左往右分别是 2 位闰秒指示、3 位版本号、3 位操作模式。001表示没有闰秒调整,100是 NTP v4 版本号,最后的100是服务器模式。客户端发来的请求首字节是0x1B,对应模式011,也就是客户端模式。
Stratum 字段设为 4 意味着“我没有直接连接 GPS 或者原子钟,我是网络同步出来的普通服务器”。如果你在这台机器上跑了一个上游 NTP 客户端并且同步成功,可以改小为 2。但要注意,设置 Stratum 为 1 是不允许的,因为那是给参考时钟预留的位置。
Poll 字段填 6,表示建议客户端轮询间隔为 2 的 6 次方等于 64 秒。这不是强制值,Windows 客户端会参考这个字段,但也会结合自己的偏差累积情况调整。Precision 字段0xEC是补码表示的 -20,代表系统时钟精度约为 2 的负 20 次方秒,大概一微秒。这个值对大多数机器都是一个合理估计。
核心部分是resp[24:32]:把客户端请求里 Transmit Timestamp(偏移 40 到 47)回填到服务端响应的 Originate Timestamp 字段。客户端就是靠这四个时间戳计算出网络延迟和时钟偏移的。如果你不回填,很多严格校验的客户端会直接丢弃响应,这也就是“包都收到了但同步失败”的一个隐藏原因。
这个程序有个天然短板:它用自己的系统时间作为时间源。如果系统时间本身是错的,下游全错。所以我的习惯是,生产环境用这个独立程序之前,先手动同步一次系统时间,比如ntpdate -u ntp.aliyun.com,再启动服务。启动之后,还可以配一个 crontab 每隔十分钟让系统自身跟公网时间源再对一次,让本地偏差保持在可接受范围。
3.3 Linux 下用 chrony 顶替自研程序,什么时候换
如果机器有 root 权限、能装包,我通常不会用自研的 Python 版本,而是直接用 chrony。它既兼容完整 NTP,也能以更轻量的姿态承担 SNTP 服务器职责。尤其当你需要“本地时钟不准也能对外服务”的时候,chrony 的local stratum指令就是为脱网环境设计的。
安装后修改/etc/chrony/chrony.conf:
# 上游时间源,iburst 表示启动时快速完成第一次同步 server ntp.aliyun.com iburst # 当上游不可用时,仍然对外提供本地时间,并声明层级为 10 local stratum 10 # 只允许内网网段访问 allow 192.168.0.0/24 # 本机时间偏差超过 1 秒时不调整,避免系统时间一次跳变过大 makestep 1 3配置完成后的启动命令:
systemctl enable --now chronyd chronyc sources -v chronyc trackinglocal stratum 10是这里的关键:它告诉下游客户端“如果连不上公网,我的时间也还是有效的,层级为 10”。这样即使整条公网链路断开,内网设备之间仍然保持同一时间。makestep 1 3的含义是,如果偏差超过 1 秒,并且已经启动了三次以上,就直接让时间跳变而不是缓慢调频。对于日志和证书校验场景,快速收敛比平滑调整重要得多。如果你拿 chrony 和自研 Python 程序对比,选 chrony 的代价是它不是一个“程序包”,而是一个系统服务,但换来的是完整的时钟驯服逻辑和等精度。
4. 把参数调对:时间源、轮询间隔与监听策略
4.1 上游时间源怎么选,公网 NTP 服务器怎么测试
运行 SNTP 服务器的机器自己需要知道“现在几点了”。它的上游可以是公网 NTP 服务器、GPS/北斗授时设备、或者另一台内网时间服务器。对绝大多数业务内网,公网时间源就够用,比如ntp.aliyun.com、ntp.tencent.com以及各地电信、教育网维护的服务器。
公网 NTP 服务器怎么测试,这个问题我几乎每次搭建都被问一次。不要用ping判断 NTP 服务器是否可用,因为 NTP 服务器可能禁 ICMP,但 UDP 123 完全正常。正确做法是使用专门的协议测试命令。
在 Linux 上:
ntpdate -q ntp.aliyun.com chronyc sources -v ntp.aliyun.com第一条命令的-q表示只查询不同步,输出最后一行会给出offset和delay。offset 是以秒为单位的本机时间与服务器时间的差值,正常范围应该在正负几十毫秒左右;delay 是往返时间,上百毫秒也能接受。如果你看到 offset 动辄几秒到几分钟,说明本机时间漂移严重,需要先用ntpdate手动对一下再继续。
在 Windows 上对应的是:
w32tm /stripchart /computer:ntp.aliyun.com /samples:5 /dataonly/stripchart会连续采样,/samples:5表示取 5 个样本,/dataonly关闭绘图输出,只显示延迟和偏移。输出里出现 RTT 和 offset 就是通了,出现 “The RPC server is unavailable” 或者无响应,要检查 UDP 123 出站是否被防火墙拦了。
公网时间源的响应质量也不是恒定的。我会同时配置两到三个不同服务商的上游,避免单一源抖动或者故障导致整条链路失效。Windows 的 W32Time 和 chrony 都支持多上游配置。还有一个细节是:不要迷信pool.ntp.org在部分网络环境下响应好,国内生产网络实测往往不如运营商和云厂商节点稳定。选源时看 delay 而不是看服务器名气,这是最靠谱的标准。
4.2 三个必调参数:poll 间隔、监听地址、直接丢弃的包
第一个必调参数是 poll 间隔。SNTP 客户端轮询时间服务器的间隔太小,比如 16 秒一次,几十台设备同时轮询也可能造成小规模突发流量;间隔太大,比如 1024 秒一次,客户端时间漂移量又可能积累到无法接受的量级。我的经验是:普通办公网用 64 到 256 秒,生产控制网用 32 到 64 秒。这个值既写在服务端建议上,还需要你在客户端单独配置。Windows 客户端可以改HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config下的SpecialPollInterval,单位是秒,改动后重启 W32Time 生效:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" /v SpecialPollInterval /t REG_DWORD /d 64 /f net stop w32time && net start w32time第二个必调参数是监听地址。SNTP 服务器默认监听 0.0.0.0:123,也就是对所有网卡生效。如果你只希望一个内网接口提供时间服务,就应该绑定到具体 IP,避免把时间服务暴露到外部网卡。在 Python 版本里改main('192.168.1.10', 123)即可;在 chrony 里你用bindaddress 192.168.1.10;在 Windows 服务上则需要配合防火墙入站规则限定来源 IP 范围。
第三个参数是包过滤策略。SNTP 报文非常小,但如果你监听在公网地址,经常会收到扫描器发来的畸形请求。防御做法很朴素:丢弃长度不是 48 字节的包,丢弃模式字段不是客户端请求(首字节低三位不是 0x3)的包。在 Python 代码里就是加一层判断:
if len(data) != 48 or (data[0] & 0x07) != 0x03: continue这能挡掉大部分错误请求和一部分扫描流量。完整 NTP 实现里还有速率限制(rate limit)机制,SNTP 没有,所以别拿它去处理不可信网络上的大规模流量。
5. SNTP 服务器踩坑排查:时间怎么又跳回去了
5.1 现象:内网客户端同步成功,但时间仍然差几分钟
这是我自己第一次搭建时就翻过车的问题。客户端w32tm /resync明明显示成功,但机器本地时间还是和真实时间差三百多秒。一开始以为是客户端同步逻辑问题,后来查服务端才发现,SNTP 服务器后台进程读取的就是它自己的系统时间,而这台服务器已经三个月没同步过上游,系统时间本身偏差了三百秒。它对外撒谎撒得很自然,客户端还以为拿到了正确时间。
原因就是 SNTP 服务器不是一个“时钟”,它只是一个“时钟的搬运工”。它没有 GPS,没有原子钟,上游断了它就是瞎子。解决方法是:要么在服务端配置可靠上游并确保同步链路正常,要么接一个硬件授时源。我后来在生产环境里给这台服务器加了一个 GPS 授时模块,通过串口接入,SNTP 服务端就不依赖任何公网节点了。内网设备最终同步到的精度受限于这台服务器的本地时钟漂移,用 GPS 校准时误差可以压到毫秒级。
5.2 现象:防火墙已经放行 123 端口,客户端还是 No response
这是一个典型的黑白匣子问题。你检查了服务器防火墙,入站规则 UDP 123 已放行,客户端telnet ip 123也提示端口开启,但w32tm /resync一直报 “The request is invalid. (0x800706A1)” 或者无响应。
原因有两层。第一层:telnet测的是 TCP,SNTP 用的是 UDP,一个 TCP 端口能通说明不了 UDP 能通。第二层:Windows 自带的防火墙放行之后,云安全组或者上层交换机 ACL 还会有第二道、第三道规则。我遇到过一个实例,服务器防火墙全放行,客户端本地防火墙也允许出站,最后还是不通,查到最后是虚拟化平台的安全组把 UDP 123 从其它网段的入站丢弃了。
解决方法是分段排查。在服务端用 tcpdump 或 Wireshark 抓 UDP 123 的包,看有没有请求进来、有没有响应出去:
tcpdump -i eth0 udp port 123 -nn如果看到请求进来但没响应出去,查服务端绑定和权限;如果响应出去了但客户端没收到,查中间链路。还有一种隐蔽情况是服务端进程绑定了 127.0.0.1,对外看起来进程在跑,实际需求包根本到达不了。
5.3 现象:虚机里的时间服务反复跳变,关了 NTP 反而稳定
VMware、VirtualBox 这些虚拟机里的时间服务问题,是圈子里公认的“玄学”重灾区。现象是:SNTP 服务器跑在虚机上,内网客户端同步完,过五分钟又跳回原来的错误值;反过来,把虚机上的时间同步服务全关掉,时间反而稳定了。
原因要分两边看。第一边,VMware Tools、Hyper-V Integration Services 默认带了宿主机时间同步功能,它会定期把宿主机时间推到虚机里,和虚机内的 SNTP 服务产生竞争。两边各调各的,结果就是时间反复横跳。第二边,宿主机本身如果没有同步过时间,虚机再怎么调也是往错误目标靠。
解决分三步。第一步,关掉虚拟化平台的时间同步接口,VMware Tools 里取消勾选“时间同步”,或者在宿主机配置里加入time.synchronize.continue = FALSE和time.synchronize.tools.enable = FALSE。第二步,给宿主机本身配一个正常工作的 NTP 服务,让宿主机的时钟正确。第三步,在虚机内再把 SNTP 服务启动,这时候一般就稳定了。如果还是跳变,检查内核时钟源,Linux 可以用chronyd配合hwtimestamp指令微调,Windows 则看电源管理里是否禁用了“唤醒定时器”。
5.4 现象:Win Server 2008 配置 NTP 后 w32tm 报“未找到可用的时间源”
很多老系统管理员在 2008 上配置完net start w32time后,执行w32tm /resync报错“找不到可用的时间源”。这不是服务没起来,而是配置写法不对。最常见的低级错误是 NtpServer 值里用了逗号分隔多个服务器,但每个服务器条目和标志位之间必须用空格:
错误写法:ntp.aliyun.com,ntp.tencent.com,0x1 正确写法:ntp.aliyun.com,0x1 ntp.tencent.com,0x1第二个高概率原因是上游不可达。2008 的 W32Time 在上游 DNS 解析失败或者 UDP 123 出站被挡时,不会给你明显的告警,它只会默默把 Stratum 置为 16。解决办法是先检查出站连通性,单独在 2008 上跑一次w32tm /stripchart /computer:ntp.aliyun.com /samples:3 /dataonly,通了再w32tm /resync /rediscover。还有一个容易忽略的点:AnnounceFlags没设置时,这台服务器即使同步成功,也不会主动向客户端宣告自己是可靠源。注册表里加上了AnnounceFlags = 5,再重启服务,这个问题基本就消失了。
6. 验证与进阶:用 w32tm/chronyc 做核心链路验证
6.1 公网 NTP 服务器怎么测试:当内网服务器也有公网依赖时
内网 SNTP 服务器如果依赖公网上游,你最需要做的不是上线后慌慌张张查故障,而是建一个固定的验证流程。我的常用命令组合在 Windows 和 Linux 各有一套。
Windows 上,验证本机是否成功同步到上游:
w32tm /query /status /verbose w32tm /stripchart /computer:192.168.1.10 /samples:5 /dataonly第一条看本机 Stratum 和 Last Successful Sync Time,第二条看内网这台 SNTP 服务器的响应质量。我在验收时要求 offset 的绝对值小于 1 秒,延迟小于 100 毫秒,连续 5 次采样没有超时。
Linux 上对应的是chronyc和ntpdate:
chronyc tracking chronyc sources -v ntpdate -q 192.168.1.10chronyc tracking输出的System time字段就是本机时钟与上游的偏差,稳定后应趋于一个百万分之一阶的小数。如果这个值在几十毫秒量级反复横跳,说明上游链路抖动或者本机时钟源有问题,需要回去检查第 4 章里的 poll 间隔设置。
6.2 从 SNTP 升级到完整 NTP:触发信号和最小变更
当出现下面三个信号时,我建议把 SNTP 服务器升级成完整 NTP 方案:一是客户端数量超过几百台并且同步频率较高,SNTP 的简单响应开始出现丢包或延迟波动;二是业务开始要求毫秒级一致性,比如工业控制、日志审计联合检索;三是对时间校验有安全要求,需要 NTP 的认证能力。最小变更不是把 Python 程序换成 C 语言重写,而是直接把内网时间服务迁到 chrony 或者 ntpd,让本机继续当服务器,客户端配置一行不改。
升级过程中我吃过一次亏:旧 Python 版 SNTP 服务可能还占着 UDP 123,新服务启动时报Address already in use,但源服务又查不到进程。最后发现是旧服务以nohup跑在后台,ps -ef过滤工具名没过滤到 Python 的父进程,实际是僵尸进程。kill -9之后干净启动才恢复。这类问题多了以后,我现在的习惯是:所有独立 SNTP 脚本必须提供--stop参数来清理进程和 PID 文件,不要再靠手工找进程树。
时间服务这类东西,看着越简单越容易在细节上出问题。早期为了图省事,我把一个临时测试用的 SNTP 脚本直接挂进了生产环境,结果上游断连之后整整一周内网时间都对不上,排查时又要从协议字段开始重新翻。从那以后,我给自己定了三个规矩:任何内网时间服务器必须有明确的上游和回退策略,服务端和客户端之间必须有自动巡检脚本,所有验证命令都要写成文档留在服务器上。这套习惯让我在后来的十几个项目里再没有因为“时间不对”被半夜叫醒过。希望这些路径和踩坑记录能帮你少走这段路,祝顺利。
本文还有配套的精品资源,点击获取