Skip to main content

ntp_timer/
ntp.rs

1use crate::config::Config;
2use crate::error::TimerError;
3use std::net::UdpSocket;
4use std::time::Duration;
5use tracing::{debug, error, warn};
6
7/// 从 NTP 服务器获取秒级时间戳
8pub fn get_ntp_timestamp(config: &Config) -> Result<i64, TimerError> {
9    debug!("开始从 NTP 服务器获取时间戳");
10
11    for (idx, server) in config.ntp_servers.iter().enumerate() {
12        debug!(server = %server, index = idx, "尝试连接 NTP 服务器");
13
14        for attempt in 0..config.max_retries {
15            match fetch_ntp_time(server, config.socket_timeout_secs) {
16                Ok(timestamp) => {
17                    debug!(server = %server, timestamp = timestamp, "成功获取 NTP 时间戳");
18                    return Ok(timestamp);
19                }
20                Err(e) => {
21                    if attempt < config.max_retries - 1 {
22                        warn!(
23                            server = %server,
24                            attempt = attempt + 1,
25                            max_retries = config.max_retries,
26                            error = %e,
27                            "NTP 请求失败,准备重试"
28                        );
29                    }
30                }
31            }
32        }
33    }
34
35    error!("所有 NTP 服务器均不可用");
36    Err(TimerError::NtpError(
37        "无法从任何 NTP 服务器获取时间戳".to_string(),
38    ))
39}
40
41fn fetch_ntp_time(server: &str, timeout_secs: u64) -> Result<i64, TimerError> {
42    let server_addr = format!("{}:123", server);
43    let socket = UdpSocket::bind("0.0.0.0:0")
44        .map_err(|e| TimerError::SocketError(format!("创建套接字失败: {}", e)))?;
45
46    let timeout = Duration::from_secs(timeout_secs);
47    socket
48        .set_read_timeout(Some(timeout))
49        .map_err(|e| TimerError::SocketError(format!("设置读超时失败: {}", e)))?;
50    socket
51        .set_write_timeout(Some(timeout))
52        .map_err(|e| TimerError::SocketError(format!("设置写超时失败: {}", e)))?;
53
54    let mut ntp_request = [0u8; 48];
55    ntp_request[0] = 0x1b;
56
57    socket
58        .send_to(&ntp_request, &server_addr)
59        .map_err(|e| TimerError::SocketError(format!("发送 NTP 请求失败: {}", e)))?;
60
61    let mut ntp_response = [0u8; 48];
62    socket
63        .recv(&mut ntp_response)
64        .map_err(|e| TimerError::SocketError(format!("接收 NTP 响应失败: {}", e)))?;
65
66    let seconds = u32::from_be_bytes([
67        ntp_response[40],
68        ntp_response[41],
69        ntp_response[42],
70        ntp_response[43],
71    ]);
72
73    const NTP_UNIX_EPOCH_DELTA: u32 = 2208988800;
74    if seconds < NTP_UNIX_EPOCH_DELTA {
75        return Err(TimerError::NtpError("NTP 时间戳异常".to_string()));
76    }
77
78    let unix_timestamp = (seconds - NTP_UNIX_EPOCH_DELTA) as i64;
79    debug!(server = %server, ntp_secs = seconds, unix_timestamp = unix_timestamp, "从 NTP 服务器获取时间戳成功");
80
81    Ok(unix_timestamp)
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_ntp_config() {
90        let config = Config::default();
91        assert!(!config.ntp_servers.is_empty());
92        assert_eq!(config.socket_timeout_secs, 5);
93    }
94}