NTP

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


概述

本模块实现网络时间协议(NTP)客户端功能,通过与NTP服务器交互校准设备时钟,提供高精度时间同步能力。支持异步时间同步、客户端会话管理、网络自适应以及IPv4/IPv6双栈,适用于需要精确时间的嵌入式设备、物联网设备或网络应用场景。NTP协议遵循RFC 5905标准,确保与标准NTP服务器的兼容性。模块设计考虑了低资源占用,适合在资源受限的嵌入式环境中运行。

应用场景包括但不限于:

  • 物联网设备的时间同步,确保日志和事件时间戳准确。

  • 网络通信系统中需要高精度时间的服务。

  • 工业控制设备中需要统一时间基准的场景。

基本要素

  • 核心概念与作用:NTP(网络时间协议)是一种用于同步计算机系统时钟的协议,能够确保网络中各设备时间保持一致。

  • 协议:NTP属于应用层协议。使用UDP端口号123进行通信。

  • NTP服务器:提供准确时间信息的服务器,通常连接到原子钟或GPS等高精度时间源。海康威视或者阿里等厂商也提供专门的NTP服务器服务。

  • NTP客户端:需要同步时间的设备或系统,通常会通过向NTP服务器发送UDP请求来获取准确时间。

  • NTP服务器地址:可以是IP地址或域名,用于客户端与服务器建立连接。例如阿里提供的“ntp.aliyun.com”,默认端口号一般为123。

  • 时间同步机制:采用客户端-服务器模式,客户端定期向服务器请求时间信息并调整自身系统时间。

  • 网络连接:设备必须能够正常连接到互联网或局域网才能与NTP服务器通信。

  • 多服务器配置:为提高可靠性,通常可以配置多个NTP服务器地址,当一个服务器不可用时可以自动切换到其他服务器。

  • 时间校正算法:NTP使用特定算法处理网络延迟等因素,通过加权平均等方式减少时间同步误差。

  • 应用场景:主要应用于需要精确时间同步的场景,如金融交易、电信运营和科学实验等。

流程概述

  1. 初始化NTP请求,并获取对应NTP操作句柄。

  2. 等待注网成功,PDP通道激活。

  3. DNS域名解析。

  4. NTP客户端通过UDP协议(端口123)与时间源(服务器)建立socket连接。

  5. 客户端在T1时刻发送请求报文,携带自身时间戳T1。

  6. 服务器在T2时刻记录接收时间,并在T3时刻发送应答报文,携带T1、T2、T3三个时间戳。

  7. 客户端在T4时刻接收应答,解析NTP回应包,通过计算时间差校准自身时钟。

  8. 打印出计算得到的准确时钟,并通过回调将时间信息同步到应用层。

  9. 释放NTP客户端资源。

消息结构

NTP(Network Time Protocol,网络时间协议)消息是基于UDP的数据包,使用端口123。标准NTP v4消息的基本长度为48字节(头部),可能包含可选的扩展字段(如认证或额外数据)。消息结构用于时间同步,包括跃秒指示、版本、模式、层级、时间戳等信息。

根据标准RFC 5905(NTP v4),NTP消息的格式如下所述。以下我将使用Mermaid的classDiagram来表示消息结构,便于可视化。classDiagram展示了消息的整体布局,每个字段包括名称、位宽和简要说明。字段按偏移量(从0开始)组织。

以下是NTP消息结构的Mermaid代码。用户你可以使用Mermaid支持的工具(如Markdown渲染器或在线编辑器)来查看图形化效果。

image

图表解释

  • 布局:classDiagram将NTPMessage表示为一个结构体,每个字段用 + 前缀表示公共成员。括号内是位宽,后面是简要描述(包括可能的值)。

  • 偏移量细节(字节为单位,按32-bit字对齐):

    • 0-3字节:LI(比特0~1),VN(2~4),Mode(5~7),Stratum(8~15),Poll(16~23),Precision(24~31)。

    • 4-7字节:Root Delay(32 bits)。

    • 8-11字节:Root Dispersion(32 bits)。

    • 12-15字节:Reference ID(32 bits)。

    • 16-23字节:Reference Timestamp(64 bits)。

    • 24-31字节:Origin Timestamp(64 bits)。

    • 32-39字节:Receive Timestamp(64 bits)。

    • 40-47字节:Transmit Timestamp(64 bits)。

    • 48+字节:可选扩展(每个扩展至少40字节,包括长度、类型、值和填充),或认证字段(Key ID + Digest,总20字节)。

  • 时间戳格式:所有64-bit时间戳使用NTP格式:高32位为整数秒(自1900-01-01 00:00:00 UTC起),低32位为分数秒(分辨率约232皮秒)。

  • 可选部分:如果使用认证(如MD5),添加Key Identifier(32 bits)和Message Digest(128 bits)。NTPv4支持多个扩展字段,用于高级功能如自动密钥或跃秒表。

  • 模式值详解(Mode字段):

    • 1:Symmetric Active(对称主动,用于对等同步)。

    • 2:Symmetric Passive(对称被动)。

    • 3:Client(客户端请求)。

    • 4:Server(服务器响应)。

    • 5:Broadcast(广播模式,无需请求)。

    • 6:Control(控制消息,用于查询/配置,不是标准时间同步)。

  • 注意事项

    • NTP消息是无状态的,客户端/服务器通过时间戳计算偏移和延迟。

    • NTP协议使用32位计数器来表示自1900年1月1日以来的秒数,这将在2036年发生溢出,即Y2036问题。在实际实现中,使用64位时间戳存储NTP时间,其中前32位表示秒,后32位表示秒的小数部分。

API说明

头文件

qcm_ntp_app.h

NTP函数列表

函数

描述

qcm_ntp_client_new()

初始化NTP请求,并获取对应NTP操作句柄

qcm_ntp_client_free()

NTP释放客户端资源,实际释放会在NTP完成后会自动执行

qcm_ntp_sync_start()

启动NTP异步时间同步请求

qcm_ntp_sync_result_cb()

回调

NTP客户端创建和释放

qcm_ntp_client_new

函数原型

qcm_ntp_client_id qcm_ntp_client_new(void)

功能描述

初始化NTP请求,并获取对应NTP操作句柄。

参数说明

返回值说明

函数执行成功返回 client id 大于0,失败返回-1。

备注

注意: 资源在NTP的回调函数中通知后会自动释放,无需手动管理。​

qcm_ntp_client_free

函数原型

qcm_ntp_result_code qcm_ntp_client_free(qcm_ntp_client_id client_id)

功能描述

NTP释放函数,实际释放在NTP完成后会自动执行,无需手动调用。

参数说明

参数名

类型

是否必填

范围/单位

说明

client_id

qcm_ntp_client_id

1~3

对应客户端句柄ID

返回值说明

成功返回 QCM_NTP_SUCCESS,否则返回 qcm_ntp_result_code 枚举值(详见附录)。

备注

注意: 此处为方便API成对,实际释放在NTP完成后会自动执行。

NTP时间同步控制接口

qcm_ntp_sync_start

函数原型

qcm_ntp_result_code qcm_ntp_sync_start(qcm_ntp_client_id  client_id,    
                                       const char  *host_name,    
                                       qosa_uint16_t  host_port,    
                                       qcm_ntp_config_t  *ntp_options,    
                                       qcm_ntp_sync_result_cb  cb_fun,    
                                       void  *user_param)

功能描述

发起NTP异步请求。并通过回调的方式获取最终的结果。

参数说明

参数名

类型

是否必填

范围/单位

说明

client_id

qcm_ntp_client_id

1~3

对应从 qcm_ntp_client_new 函数中获取到的NTP Client ID

host_name

const char*

最大128字节

NTP服务器的域名或IP地址

host_port

qosa_uint16_t

1~65535

对应NTP服务器端口,一般默认为123

ntp_options

qcm_ntp_config_t*

参考 qcm_ntp_config_t

对应用户配置的参数

cb_fun

qcm_ntp_sync_result_cb

参考 qcm_ntp_sync_result_cb

对应结果返回回调cb函数

user_param

void*

用户携带的参数

返回值说明

成功返回 QCM_NTP_SUCCESS,否则返回 qcm_ntp_result_code 枚举值(详见附录)。

qcm_ntp_sync_result_cb

函数原型

typedef void (*qcm_ntp_sync_result_cb)(qcm_ntp_client_id client_id, 
                                       qcm_ntp_result_code result, 
                                       qosa_rtc_time_t *sync_time, 
                                       void *arg);

功能描述

NTP操作的结果回调函数,对NTP请求返回的结果进行判断及处理。

参数说明

参数名

类型

是否必填

范围/单位

说明

client_id

qcm_ntp_client_id

1~3

对应从 qcm_ntp_client_new 函数中获取到的NTP Client ID

result

qcm_ntp_result_code

参考 qcm_ntp_result_code

NTP完成结果

sync_time

qosa_rtc_time_t*

参考 qosa_rtc_time_t

返回NTP时间同步成功后的时间数据

arg

void*

用户传入的用户参数

返回值说明

结构体定义

qcm_ntp_config_t

/**
 * @struct qcm_ntp_config_t
 * @brief 使用NTP获取时间时用户可配置的参数
 */
typedef struct
{
    qosa_uint8_t  sim_id;            /*!< Corresponding SIM card ID sequence number */
    qosa_uint8_t  pdp_id;            /*!< Corresponding PDP activation scenario sequence number */
    qosa_uint32_t num_data_bytes;    /*!< Length of NTP sending data packet */
    qosa_bool_t   sync_local_time;   /*!< Whether to automatically set the synchronized time as local time */
    qosa_uint8_t  retry_cnt;         /*!< NTP data sending retry count */
    qosa_uint16_t retry_interval_tm; /*!< NTP data sending retry interval time */
} qcm_ntp_config_t;

参数

类型

说明

sim_id

qosa_uint8_t

对应SIM卡的ID序号

pdp_id

qosa_uint8_t

对应PDP激活场景的序号

num_data_bytes

qosa_uint32_t

NTP发送数据包的长度

sync_local_time

qosa_bool_t

是否自动将同步时间设置为本地时间

retry_cnt

qosa_uint8_t

NTP数据发送重试次数

retry_interval_tm

qosa_uint16_t

NTP数据发送重试间隔时间

qosa_rtc_time_t

/**
 * @struct qosa_rtc_time_t
 * @brief RTC时间结构,用于表示分解时间(年、月、日、小时、分钟、秒等)
 */
typedef struct
{
    int tm_sec;   // seconds [0,59]
    int tm_min;   // minutes [0,59]
    int tm_hour;  // hour [0,23]
    int tm_mday;  // day of month [1,31]
    int tm_mon;   // month of year [0,11],0=January,11=December
    int tm_year;  // year starting from 1900, 2024 → 124 (i.e. tm_year + 1900 = 2024)
    int tm_wday;  // wday [0-6], sunday = 0,6=SAT
} qosa_rtc_time_t;

参数

类型

说明

tm_sec

int

秒;范围:0~59

tm_min

int

分钟;范围:0~59

tm_hour

int

小时;范围:0~23(24小时制)

tm_mday

int

月份中的日期;范围:1~31

tm_mon

int

年份中的月份;范围:0~11(0表示1月,11表示12月)

tm_year

int

年份(从1900开始计算) (例如:2024年表示为124,即 tm_year + 1900 = 2024)

tm_wday

int

星期几;范围: 0~6(0表示星期日,6表示星期六)

枚举定义

qcm_ntp_result_code

/**
 * @enum  qcm_ntp_result_code
 * @brief NTP返回的结果码
 */
typedef enum
{
    QCM_NTP_ERR_WOULDBLOCK = -2,
    QCM_NTP_ERR_NEGATIVE = -1,
    QCM_NTP_SUCCESS = 0,
    QCM_NTP_ERR_UNKNOW = (QOSA_COMPONENT_NTP << 16) | 550 /*!< Unknow ERROR   */,
    QCM_NTP_ERR_OPERATION_BLOCK = (QOSA_COMPONENT_NTP << 16) | 551 /*!< Operation blocked   */,
    QCM_NTP_ERR_INVALID_PARAM = (QOSA_COMPONENT_NTP << 16) | 552 /*!< Invalid parameters   */,
    QCM_NTP_ERR_MEMORY = (QOSA_COMPONENT_NTP << 16) | 553 /*!<  Memory not enough   */,
    QCM_NTP_ERR_SOCKET_NEW_FAILURE = (QOSA_COMPONENT_NTP << 16) | 554 /*!< Create socket failed */,
    QCM_NTP_ERR_SOCKET_BIND_FAILURE = (QOSA_COMPONENT_NTP << 16) | 556 /*!< Socket bind failed   */,
    QCM_NTP_ERR_SOCKET_WRITE_FAILURE = (QOSA_COMPONENT_NTP << 16) | 558 /*!< Socket write failed   */,
    QCM_NTP_ERR_SOCKET_READ_FAILURE = (QOSA_COMPONENT_NTP << 16) | 559 /*!< Socket read failed     */,
    QCM_NTP_ERR_PDP_ACTIVE_FAILURE = (QOSA_COMPONENT_NTP << 16) | 561 /*!< PDP context opening failed     */,
    QCM_NTP_ERR_DNS_FAILURE = (QOSA_COMPONENT_NTP << 16) | 565 /*!< DNS parse failed     */,
    QCM_NTP_ERR_SOCKET_CONNECT_FAILURE = (QOSA_COMPONENT_NTP << 16) | 566 /*!< Socket connect failed   */,
    QCM_NTP_ERR_TIMEOUT = (QOSA_COMPONENT_NTP << 16) | 569 /*!< Operation timeout    */,
} qcm_ntp_result_code;

成员

说明

QCM_NTP_ERR_WOULDBLOCK

操作会阻塞(非阻塞模式下返回)

QCM_NTP_ERR_NEGATIVE

通用负数错误

QCM_NTP_SUCCESS

操作成功

QCM_NTP_ERR_UNKNOW

未知错误

QCM_NTP_ERR_OPERATION_BLOCK

操作被阻塞

QCM_NTP_ERR_INVALID_PARAM

无效的参数

QCM_NTP_ERR_MEMORY

内存不足

QCM_NTP_ERR_SOCKET_NEW_FAILURE

创建套接字失败

QCM_NTP_ERR_SOCKET_BIND_FAILURE

套接字绑定失败

QCM_NTP_ERR_SOCKET_WRITE_FAILURE

套接字写入失败

QCM_NTP_ERR_SOCKET_READ_FAILURE

套接字读取失败

QCM_NTP_ERR_PDP_ACTIVE_FAILURE

PDP上下文激活失败

QCM_NTP_ERR_DNS_FAILURE

DNS解析失败

QCM_NTP_ERR_SOCKET_CONNECT_FAILURE

套接字连接失败

QCM_NTP_ERR_TIMEOUT

操作超时

应用示例

NTP 流程图

../../../_images/board_CqkSwgPsnhFVFybDWetckt4anCg.jpg

NTP示例Demo

请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/ntp/ntp_demo.c

常见问题排查指南

NTP使用配置建议

NTP配置通过 qcm_ntp_config_t 结构体传递给 qcm_ntp_sync_start() 函数。这些参数影响时间同步的可靠性、网络开销和资源消耗。在物联网设备中,需考虑网络稳定性、功耗和重试机制。以下是针对每个参数的详细建议,结合典型场景(如设备自检、网络心跳或日志同步)。

  1. num_data_bytes(NTP发送数据包的长度,单位:字节)

    • 推荐值范围:48到1472字节(NTP标准头部为48字节)。

    • 默认建议:48字节(最小有效负载,平衡效率)。

    • 解释与理由:定义NTP请求包的大小。标准NTP包为48字节;较大包可用于扩展字段(如认证),但增加网络开销。避免超过MTU(Maximum Transmission Unit,通常1500字节)以防碎片化。

    • 优化策略:对于基本同步,使用48字节降低功耗;若需认证扩展,增至128字节。示例中设置为48,适合一般诊断。

  2. sync_local_time(是否自动将同步时间设置为本地时间)

    • 推荐值范围:QOSA_FALSE或QOSA_TRUE。

    • 默认建议:QOSA_FALSE(手动处理时间)。

    • 解释与理由:若为QOSA_TRUETRUE,模块自动更新设备RTC(Real-Time Clock);为QOSA_FALSEFALSE,则仅通过回调返回时间,用户可自定义处理(如日志记录而不更新系统时钟)。

    • 优化策略:在需要统一时间基准的场景(如工业控制)设为QOSA_TRUETRUE;在测试或多时区设备中设为FALSE,手动调整时区。示例中设置为QOSA_FALSE,避免意外覆盖本地时间。

  3. retry_cnt(NTP数据发送重试次数)

    • 推荐值范围:1到5次。

    • 默认建议:3次(平衡可靠性和时间)。

    • 解释与理由:控制请求失败后的重试次数。在不稳定网络中,提高重试可提升成功率,但增加延迟。

    • 优化策略:弱信号环境设为3~5;稳定网络设为1~2。示例中设置为3,适合移动网络。

  4. retry_interval_tm(NTP数据发送重试间隔时间,单位:秒)

    • 推荐值范围:5到60秒。

    • 默认建议:15秒(适中间隔)。

    • 解释与理由:重试之间的等待时间。过短可能导致网络拥塞;过长延长同步时间。

    • 优化策略:根据网络延迟调整,若平均RTT高,增大间隔;示例中设置为15,适用于典型物联网场景。

通用配置指南与最佳实践

  • 场景适配:开机自检使用最小配置(retry_cnt = 1, retry_interval_tm = 5);周期性同步(如每小时)设为 retry_cnt = 3以确保可靠性。

  • 资源考虑:在资源受限设备中,优先小包和低重试以节省电量;测试前确保网络注网(PDP激活)。

错误码

问题现象(错误码)

可能原因

建议解决方案

QCM_NTP_ERR_INVALID_PARAM(无效的参数)

输入参数为空、超出范围(如 retry_cnt = 0)或格式错误

检查所有参数:确保 host_name 非空、ntp_options 结构体完整初始化;验证 sim_id = 0/1、pdp_id ≥ 1。

QCM_NTP_ERR_MEMORY(内存不足)

系统内存分配失败,可能是任务栈或堆空间不足

优化代码内存使用;增加任务栈大小(如 UNIR_NTP_DEMO_TASK_STACK_SIZE);检查是否存在内存泄露。

QCM_NTP_ERR_SOCKET_NEW_FAILURE(创建socket失败)

网络栈未初始化或socket资源耗尽

确保PDP上下文已激活;重启网络模块;检查系统socket限制。

QCM_NTP_ERR_SOCKET_BIND_FAILURE(套接字绑定失败)

绑定本地地址/端口冲突或权限不足

避免使用保留端口(NTP默认123)。是否有频繁断连的现象。

QCM_NTP_ERR_SOCKET_WRITE_FAILURE(套接字写入失败)

网络连接中断或缓冲区满

检查网络信号强度;增加 retry_cnt;验证服务器 host_name 可达(先Ping测试)。

QCM_NTP_ERR_SOCKET_READ_FAILURE(套接字读取失败)

响应包丢失或读取超时

调整 retry_interval_tm 值;检查网络稳定性;启用日志捕获UDP包。

QCM_NTP_ERR_PDP_ACTIVE_FAILURE(PDP上下文激活失败)

SIM卡未就绪、网络未注册或APN配置错误

验证SIM卡插入并有效;检查AT命令注网状态;配置正确APN。

QCM_NTP_ERR_DNS_FAILURE(DNS解析失败)

域名无效、网络无DNS服务器或解析超时

使用IP地址代替域名测试(如"203.107.6.88" for ntp.aliyun.com);检查DNS服务器设置;验证网络连接。

QCM_NTP_ERR_SOCKET_CONNECT_FAILURE(套接字连接失败)

目标不可达、防火墙阻挡或路由问题

切换NTP服务器(如"pool.ntp.org")。

QCM_NTP_ERR_TIMEOUT(操作超时)

响应超时时间过短或网络延迟高

增大 retry_interval_tm(推荐>10 s);测试更小的 num_data_bytes;优化网络环境。

QCM_NTP_ERR_OPERATION_BLOCK(操作被阻塞)

非阻塞操作中,当前无法完成

检查socket模式;增加重试或等待时间;确保任务不阻塞。

QCM_NTP_ERR_UNKNOW(未知错误)

未分类错误,可能为底层系统问题

-

QCM_NTP_ERR_WOULDBLOCK(操作会阻塞)

非阻塞模式下操作未就绪

在循环中重试操作;或切换到阻塞模式。

QCM_NTP_ERR_NEGATIVE(通用负数错误)

通用失败,通常为参数或状态错误

检查 client_id 有效性;确保 qcm_ntp_client_new 成功后调用。

无错误但时间偏移大/同步失败

网络延迟高、服务器负载或时区未调整

分析 sync_time 结构体;手动调整时区;使用多个NTP服务器重试。

回调未触发(无result返回)

任务/消息队列未正确初始化或阻塞

检查 qosa_msgq_createqosa_task_create 调用;确保 qcm_ntp_sync_result_cb 注册正确;避免无限循环。

参考附录

缩写

全称

说明

NTP

Network Time Protocol

网络时间协议,用于时间同步

UDP

User Datagram Protocol

用户数据报协议,NTP传输层协议

RTC

Real-Time Clock

实时时钟,设备本地时钟硬件

PDP

Packet Data Protocol

分组数据协议,用于移动网络激活

DNS

Domain Name System

域名系统,用于NTP服务器地址解析

IP

Internet Protocol

互联网协议

IPv4

Internet Protocol version 4

互联网协议第4版

IPv6

Internet Protocol version 6

互联网协议第6版

LI

Leap Indicator

跃秒指示器

VN

Version Number

版本号

RTT

Round-Trip Time

往返时延,NTP计算延迟

SIM

Subscriber Identity Module

用户身份模块(SIM卡)