weixin_42346462 2024-08-11 14:20 采纳率: 55.6%
浏览 14
已结题

ntp客户端对时C语言

请教ntp对时方面的专家,最近做一个开发,需求如下:
通过 NTP 乒乓的方式实现对北斗时间同步装置及被授时设备对时偏差的监测,采用客户端(管理端)和服务器(被监测端)问答方式实现对时偏差的计算。为了提高对时偏差的精度,采用时钟装置做为监测的管理端,监测其它被授时设备,具体过程如下:
a) A 为管理端发送“监测时钟请求”的时标;
b) B 为被监测端收到“监测时钟请求”的时标;
c) C 为被监测端返回“监测时钟请求的结果”的时标;
d) D 为管理端收到“监测时钟请求的结果”的时标;
e) △t 为管理端时钟超前被监测装置内部时钟的钟差(正为相对超前,负代表相对滞后);
f) △t =[(D - C) + (A - B)]/2
问题1:我看了网络上一些代码,好像都没有具体取得以上A,B,C,D这四个时间的,各位可有相关源码,发我参考一下。

此外,我在网络上找到一段代码,源码和执行结果如下:

typedef struct
{
    uint8_t li_vn_mode;      // Leap indicator, version and mode
    uint8_t stratum;         // Stratum level of the local clock
    uint8_t poll;            // Maximum interval between successive messages
    uint8_t precision;       // Precision of the local clock
    uint32_t rootDelay;      // Total round trip delay time
    uint32_t rootDispersion; // Max error aloud from primary clock source
    uint32_t refId;          // Reference clock identifier  
    uint32_t refTm_s;        // Reference time-stamp seconds 参考时间戳  
    uint32_t refTm_f;        // Reference time-stamp fraction of a second
    uint32_t origTm_s;       // Originate time-stamp seconds  原始时间戳  
    uint32_t origTm_f;       // Originate time-stamp fraction of a second
    uint32_t rxTm_s;         // Received time-stamp seconds  接收时间戳
    uint32_t rxTm_f;         // Received time-stamp fraction of a second
    uint32_t txTm_s;         // Transmit time-stamp seconds  传送时间戳
    uint32_t txTm_f;         // Transmit time-stamp fraction of a second
} ntp_packet;

void main()
{
    m_client_sockid = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    struct sockaddr_in clientSockAdd;
    bzero((char *)&clientSockAdd, sizeof(clientSockAdd));
    clientSockAdd.sin_family = AF_INET;
    clientSockAdd.sin_port = htons(this->m_iPort);
    clientSockAdd.sin_addr.s_addr = inet_addr(this->m_IP.c_str());

    int iRet = -1;
    ntp_packet ntpPacket;
    ;
    memset(&ntpPacket, 0, sizeof(ntp_packet));
    ntpPacket.li_vn_mode = (0x3 << 6) | (0x3 << 3) | 0x3; // NTPv4, client mode
    iRet = sendto(m_client_sockid, (char *)&ntpPacket, sizeof(ntp_packet), 0, (struct sockaddr *)&clientSockAdd, sizeof(clientSockAdd));
    iRet = recvfrom(m_client_sockid, (char *)&ntpPacket, sizeof(ntp_packet), 0, NULL, NULL);

    cout << "iRet:" << to_string(iRet) << endl;
    cout << "refTm_s:" << to_string(ntpPacket.refTm_s) << endl;
    cout << "refTm_f:" << to_string(ntpPacket.refTm_f) << endl;
    cout << "origTm_s:" << to_string(ntpPacket.origTm_s) << endl;
    cout << "origTm_f:" << to_string(ntpPacket.origTm_f) << endl;
    cout << "rxTm_s:" << to_string(ntpPacket.rxTm_s) << endl;
    cout << "rxTm_f:" << to_string(ntpPacket.rxTm_f) << endl;
    cout << "txTm_s:" << to_string(ntpPacket.txTm_s) << endl;
    cout << "txTm_f:" << to_string(ntpPacket.txTm_f) << endl;

    ntpPacket.refTm_s = ntohl(ntpPacket.refTm_s);
    ntpPacket.refTm_f = ntohl(ntpPacket.refTm_f);
    time_t tmTemp = (time_t)(ntpPacket.refTm_s - NTP_TIMESTAMP_DELTA);
    printf("refTm: %s", ctime((const time_t *)&tmTemp));

    ntpPacket.rxTm_s = ntohl(ntpPacket.rxTm_s);p
    ntpPacket.rxTm_f = ntohl(ntpPacket.rxTm_f);
    tmTemp = (time_t)(ntpPacket.rxTm_s - NTP_TIMESTAMP_DELTA);
    printf("rxTm: %s", ctime((const time_t *)&tmTemp));

    ntpPacket.txTm_s = ntohl(ntpPacket.txTm_s);
    ntpPacket.txTm_f = ntohl(ntpPacket.txTm_f);
    tmTemp = (time_t)(ntpPacket.txTm_s - NTP_TIMESTAMP_DELTA);
    printf("txTm: %s", ctime((const time_t *)&tmTemp));
}

执行结果如下:

img

问题2:这个例子中的参考时间戳、原始时间戳、接收时间戳、传送时间戳与问题1中的A,B,C,D可有对应关系,如果没有,它们分别是什么意思呢?执行结果中的原始时间为什么是0,参考时间为什么是3天前的一个日期?
问题3:客户端作为发送方,其请求发出的时间和收到回复的时间都可以自己记录,看上面的程序,这两个时间好像ntp协议会自动填充到结构体中一样,不知我的理解是否正确?

  • 写回答

17条回答 默认 最新

  • 专家-皆知 2024-08-11 14:24
    关注

    参考免费微信小程序:皆我百晓生 作答,经由专家-皆知整理回复,如问题解决,望采纳;如仍有疑问,请评论回复。

    针对您的问题,我会尽力提供详尽的解答,并给出相应的代码示例。

    问题1:如何获取A, B, C, D四个时间点

    在NTP协议中,您提到的A、B、C、D时间点实际上可以通过NTP包中的时间戳字段间接计算出来。根据您的描述,我们可以这样理解:

    • A 对应于管理端发送NTP请求的时刻,这通常是我们发起请求的本地时间,在C语言中可以直接用clock()gettimeofday()等函数获取。
    • B 是被监测端接收到请求的时刻,对应于NTP响应包中的rxTm_srxTm_f字段,表示服务器接收到客户端请求的时间。
    • C 是被监测端处理完请求并准备发送响应的时刻,这在NTP响应中没有直接提供,但可以根据响应构造时的本地时间近似估计。
    • D 是管理端接收到响应的时刻,这同样可以使用clock()gettimeofday()等函数在收到响应时记录。

    问题2:NTP包中时间戳的含义及执行结果疑问

    • refTm_srefTm_f是参考时间戳,通常表示服务器的时钟参考源(比如GPS或原子钟)的时间,这里显示的是一个较早的日期,可能是因为服务器设置或配置的问题,导致它报告了一个错误或过去的参考时间。
    • origTm_sorigTm_f是原始时间戳,理论上应为客户端发送请求时的时间,但在您提供的代码和输出中始终为0,这可能是因为NTP协议的某些实现中,客户端不填充这部分信息。
    • rxTm_srxTm_f是接收时间戳,即服务器接收到客户端请求的时间点B。
    • txTm_stxTm_f是传送时间戳,即服务器发送响应的时间,近似等于C时间点。

    问题3:客户端记录请求和响应时间

    您的理解基本正确。NTP协议确实会自动在响应中包含接收和传送时间戳,客户端可以记录发送请求的本地时间(A),然后从响应中提取rxTm_stxTm_s来间接获得B和C(或至少是C的近似值)。D时间则需要客户端在实际接收到响应时自行记录。

    示例代码改进

    考虑到上述解释,下面是一个简化的C语言示例,展示如何记录A、B(通过NTP响应)、D,并尝试估算C的时间点。注意,由于C时间点的精确值不可直接获得,我们使用当前时间作为近似。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <time.h>
    
    typedef struct {
        // ... (原结构体定义保持不变)
    } ntp_packet;
    
    int main() {
        // ... (原来的socket创建和配置代码)
    
        ntp_packet ntpPacket;
        memset(&ntpPacket, 0, sizeof(ntp_packet));
        ntpPacket.li_vn_mode = (0x3 << 6) | (0x3 << 3) | 0x3; // NTPv4, client mode
    
        // 记录请求发送时间A
        struct timeval tv;
        gettimeofday(&tv, NULL);
        double requestSendTime = tv.tv_sec + tv.tv_usec / 1e6;
    
        // 发送请求
        int iRet = sendto(...);
    
        // 接收响应
        iRet = recvfrom(...);
    
        // 记录响应接收时间D
        gettimeofday(&tv, NULL);
        double responseRecvTime = tv.tv_sec + tv.tv_usec / 1e6;
    
        // C时间点近似为当前时间,因为服务器处理和发送几乎同时
        double approximateCTime = responseRecvTime;
    
        // 解析并打印NTP响应中的时间戳
        // 注意:此处省略了转换和打印代码,直接使用之前的方法
    
        // 根据公式计算△t
        double Dt = ((responseRecvTime - ntpPacket.txTm_s) + (requestSendTime - ntpPacket.rxTm_s)) / 2.0;
        printf("Estimated time difference △t: %f seconds\n", Dt);
    
        return 0;
    }
    

    请注意,此代码仅为示例,实际应用中可能需要更精确的时钟同步逻辑和错误处理机制。特别是在计算△t时,考虑到了网络延迟和处理时间的影响,但这是一个非常简化的模型,真实场景下的误差分析会更加复杂。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(16条)

报告相同问题?

问题事件

  • 系统已结题 8月20日
  • 已采纳回答 8月12日
  • 赞助了问题酬金15元 8月11日
  • 创建了问题 8月11日

悬赏问题

  • ¥15 CCF-CSP 2023 第三题 解压缩(50%)
  • ¥30 comfyui openpose报错
  • ¥20 Wpf Datarid单元格闪烁效果的实现
  • ¥15 图像分割、图像边缘提取
  • ¥15 sqlserver执行存储过程报错
  • ¥100 nuxt、uniapp、ruoyi-vue 相关发布问题
  • ¥15 浮窗和全屏应用同时存在,全屏应用输入法无法弹出
  • ¥100 matlab2009 32位一直初始化
  • ¥15 Expected type 'str | PathLike[str]…… bytes' instead
  • ¥15 三极管电路求解,已知电阻电压和三级关放大倍数