Linux下C/C++ SNTP网络时间协议实现

news2024/11/17 7:56:22

对于许多应用程序,特别是在小型计算机和微控制器上,不需要NTP的最终性能。便开发了简单网络时间协议(SNTP),为功能较弱的计算机提供时钟同步,而这些计算机不需要NTP的复杂性。

而简单网络时间协议(SNTP)是一种用于同步计算机网络时钟的互联网协议,是网络时间协议(NTP)的一个子集。与NTP相比,需要更少的内存和处理能力。它用于精确时钟同步不是关键的应用程序。使用TCP/IP协议套件,UDP端口123。

SNTP协议套件

SNTP基于TCP/IP协议套件。它是一个应用层时间协议,是网络时间协议基础协议的一部分。SNTP与NTP一起使用用户数据报协议(UDP)进行通信。默认情况下,使用UDP端口123。

新SNTP安装的一个常见疏忽是,任何防火墙中的UDP端口都必须保持打开状态,以允许通信传输。如果客户端无法与服务器同步,请检查UDP端口123在防火墙配置中是否打开。

SNTP可以在IPv4和IPv6网络上运行,并由RFC 4330定义。

SNTP的工作原理

SNTP协议是基于定时信息分组交换的客户端-服务器协议。通常,客户端以单播模式操作。SNTP客户端以周期性的间隔(通常为每64秒一次)向NTP服务器发送一个引用其IP地址的请求。服务器通常与诸如GPS之类的硬件参考时钟同步。

在接收到来自服务器的响应时,客户端可以计算其内部时钟和服务器时钟之间的系统时间偏移。通过计算分组往返延迟来实现增强的同步。

如果需要,可以在广播或多播模式下配置SNTP。在这种情况下,服务器以周期性的间隔不断地广播时间戳数据包。客户端不断监听数据包,并在收到数据包后相应地调整系统时钟。

NTP和SNTP之间的区别

NTP开发了具有校准技术的复杂统计算法,旨在过滤微小的差异,以实现高度的时钟同步。精细的时间调整是通过调整主机系统时钟的“滴答”频率来实现的。NTP试图通过倾斜时间调整来无缝地进行时间校正,以避免阶跃变化。

冗余是通过连续监测多个时间参考来实现的。复杂的选择算法确定了最可靠和最稳定的。可以监控多个时间源,包括硬件时钟和网络时间源的混合,有助于增强鲁棒性。

NTP中还实现了许多安全功能。安全的客户端-服务器身份验证是通过实现对称密钥加密来实现的。这使得客户端能够确定接收到的时间戳的来源,从而减轻欺骗。

相比之下,SNTP采用了一种更简单的网络时钟同步方法。NTP算法的许多复杂性已经被去除或简化。

许多SNTP客户端不是倾斜时间,而是逐步调整时间。但是,步进时间可能会导致事件排序出现问题。例如,如果在生成两个事务之间后退一步,则它们将不会有正确的顺序。

SNTP也缺乏监控和过滤多个时间参考的能力。该协议通常只能被配置为从单个时间源进行操作。如果实现多个冗余时间源,这不是一个好的选择。为了简单起见,许多SNTP客户端没有实现安全身份验证技术,这可能会使系统容易受到恶意用户的攻击。

SNTP协议报文分析

下图描述了NTP和SNTP消息格式在消息中的IP和UDP标头之后。此格式为与RFC 1305中描述的NTP消息格式相同以下描述的引用标识符字段除外。对于SNTP客户端消息,其中大多数字段为零或已初始化具有预先指定的数据。

NTP消息格式:

Leap Indicator (LI):这是一个即将发生的两位代码警告,在当前时间的最后一分钟插入/删除的闰秒
值的定义如下:

 LI Meaning
 ---------------------------------------------
 0 no warning
 1 last minute has 61 seconds
 2 last minute has 59 seconds
 3 alarm condition (clock not synchronized)

Version Number (VN): 表示NTP/SNTP版本号,目前为4。如有必要进行区分,在IPv4、IPv6和OSI之间。

Mode:表示协议模式。这个值的定义如下:

 Mode Meaning
 ------------------------------------
 0 reserved
 1 symmetric active
 2 symmetric passive
 3 client
 4 server
 5 broadcast
 6 reserved for NTP control message
 7 reserved for private use

Stratum:表示层该字段仅在SNTP服务器消息中是重要的,其中,这些值定义如下:

 Stratum Meaning
 ----------------------------------------------
 0 kiss-o’-death message (see below)
 1 primary reference (e.g., synchronized by radio clock)
 2-15 secondary reference (synchronized by NTP or SNTP)
 16-255 reserved

Poll Interval: 用作二的指数,其中得到的值是最大间隔,在以秒为单位的连续消息之间。该领域意义重大,仅在SNTP服务器消息中,其中的值范围从4(16秒)到17(131072秒-约36小时)。

Precision:用作的指数二,其中得到的值是系统时钟的精度以秒为单位。此字段仅在服务器消息中有效,其中该值的范围从市电频率时钟的-6到市电频率的-20在一些工作站中发现微秒时钟。

Root Delay: 指示到主要参考源的总往返延迟,以秒为单位其中分数点在比特15和16之间。请注意变量可以同时具有正值和负值,具体取决于相对时间和频率偏移。该领域意义重大仅在服务器消息中,其中的值范围为负值几毫秒到几百的正值毫秒。

Root Dispersion:指示由于时钟频率容差引起的最大误差秒,小数点在比特15和16之间。此字段仅在服务器消息中有效,其中的值范围为零到几百毫秒。

Reference Identifier: 用于标识特定参考源。该领域仅在服务器消息,其中用于层0和1(主服务器),值为四个字符的ASCII字符串,左侧对齐并零填充到32位。对于IPv4辅助服务器,该值是同步源的32位IPv4地址。对于IPv6和OSI辅助服务器,该值为同步的IPv6或NSAP地址的MD5哈希

Reference Timestamp: 此字段是系统时钟最后的时间以64位时间戳格式设置或校正。

Originate Timestamp: 这是请求离开的时间服务器的客户端,采用64位时间戳格式。

Receive Timestamp: 这是请求到达的时间服务器或回复到达客户端,时间戳为64位。

Transmit Timestamp:这是请求离开的时间客户端或回复离开服务器,时间戳为64位。

SNTP消息结构:

typedef struct {
  uint8_t flags;
  /* 八位标志-跳跃指示器、版本号和模式 
   0 1 2 3 4 5 6 7
  +-+-+-+-+-+-+-+-+
  |LI | VN  |Mode |
  +-+-+-+-+-+-+-+-+
  */
  uint8_t stratum;         
  uint8_t pollInterval;     
  uint8_t precision;           
  uint32_t rootDelay;
  uint32_t rootDispersion;
  uint32_t refIdentifier;
  ntp_timestamp refTimestamp;
  ntp_timestamp orgTimestamp;
  ntp_timestamp recvTimestamp;
  ntp_timestamp transmitTimestamp;

} ntp_packet;

Wireshark 抓ntp包:

在

sntp 客户端与服务器代码C/C++实现

client:

创建并初始化NTP数据包,将传输时间戳设置为客户端一天中的时间,将数据包发送到NTP服务器。

void print_unix_time(struct timeval *tv);

void print_ntp_time(ntp_timestamp *ntp);

void print_ntp_packet(ntp_packet *p);

void convert_ntp_to_unix(ntp_timestamp *ntp, struct timeval *unix_time);

void convert_unix_to_ntp(struct timeval *unix_time, ntp_timestamp *ntp);

void host_to_network(ntp_packet *p);

void network_to_host(ntp_packet *p);
void set_client_request(ntp_packet *p);

void print_sntp_output(ntp_packet *p, double offset, double delay,
                       struct sockaddr_in their_addr, char *host);

void check_reply(ntp_packet *p, ntp_packet *r);

double ntp_to_double(ntp_timestamp *p);

double calculate_offset(ntp_packet *p, ntp_timestamp *t);

double calculate_delay(ntp_packet *p, ntp_timestamp *t);

int main(int argc, char *argv[]) 
{

...

  if (argc == 3) 
  {
    strcpy(host, argv[1]);
    portno = atoi(argv[2]);
  }
  else if (argc == 1) 
  {
    strcpy(host, SERVER);
    portno = PORT;
    printf("Server and port not specified.\n");
    printf("Using default server: %s\nUsing default port: %d", host, portno);
  }
  else
  {
    fprintf(stderr, "usage: %s hostname port\n", argv[0]);
    exit(1);
  }

  if((he = gethostbyname(host)) == NULL) 
  {
    perror("client gethostbyname");
    exit(1);
  }

  if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
  {
    perror("client socket");
    exit(1);
  }

  tv.tv_sec = 10;
  tv.tv_usec = 0;
  if((setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) < 0) 
  {
    printf("socket timeout error no: %d", errno);
  }

  printf("\nSending..\n");

  memset(&their_addr, 0, sizeof(their_addr));
  their_addr.sin_family = AF_INET;
  their_addr.sin_port = htons(portno);
  their_addr.sin_addr = *((struct in_addr *)he->h_addr);

  ntp_packet packet;
  memset(&packet, 0, sizeof(packet));
  ntp_packet recvBuf;
  memset(&recvBuf, 0, sizeof(recvBuf));

  set_client_request(&packet);
...

  socklen_t addr_len = (socklen_t)sizeof(struct sockaddr);
  if((numbytes = recvfrom(sockfd, &recvBuf, sizeof(ntp_packet), 0,
  (struct sockaddr *) &their_addr, &addr_len)) == -1) 
  {
    perror("server recvfrom");
    exit(1);
  }


  network_to_host(&recvBuf);

  printf("\n\nReceived:\n");

  /* 数据包到达时创建的目标时间戳,用于偏移和延迟 */
  ntp_timestamp destTimestamp = getCurrentTimestamp();

  /* 执行基本检查以检查服务器答复的有效性 */
  check_reply(&packet, &recvBuf);

  /* 使用接收到的数据包和目的地时间戳计算偏移量和延迟 */
  double offset = calculate_offset(&recvBuf, &destTimestamp);
  double delay = calculate_delay(&recvBuf, &destTimestamp);

  /* 打印格式化输出 */
  print_sntp_output(&recvBuf, offset, delay, their_addr, host);

  print_ntp_packet(&recvBuf);

...
}

server:
接收客户端请求,正确设置服务器答复,向客户端发送回复。


...
int main(int argc, char *argv[]) 
{
...

  ntp_packet packet;
  memset(&packet, 0, sizeof(ntp_packet));

  if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
  {
	  perror("server socket");
	  exit(1);
  }

  memset(&my_addr, 0, sizeof(my_addr)); 
  my_addr.sin_family = AF_INET;         
  my_addr.sin_port = htons(MYPORT);    
  my_addr.sin_addr.s_addr = INADDR_ANY;

  if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) 
  {
    perror("server bind");
    exit(1);
  }

  socklen_t addr_len = (socklen_t)sizeof(struct sockaddr);

  while(1)
  {
...

    set_server_reply(&packet);

    /* send packet */
    if((numbytes = sendto(sockfd, &packet, sizeof(ntp_packet), 0,
      (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) 
      {
        perror("client sendto");
        exit(1);
    }
    ...
  }

...
}

运行结果:


If you need the complete source code, please add the WeChat number (c17865354792)

总结

简单网络时间协议(SNTP)是为内存和处理能力有限的小型计算机和微控制器开发的。它为不需要全面NTP精度的应用程序提供时钟同步。NTP更适合于同步大型计算机集群,例如公司网络。其校准算法的稳健性有助于大型计算机系统可靠地保持精确的时间同步。

Welcome to follow WeChat official account【程序猿编码

参考:RFC 4330、RFC 768、RFC 5905

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简易糖尿病胰岛素注射量推荐系统运行记录(github项目)

前言 在github上找案例推理相关实现代码&#xff0c;找到这个项目&#xff0c;记录一下运行过程。项目地址&#xff1a;https://github.com/jcf-junior/Diabetes-CBR 运行记录 运行项目的前提是已经装好的所有request的包&#xff0c;电脑里已经安装过mongodb数据库。 原项目…

Linux XFS文件系统的备份与还原

文章目录Linux XFS文件系统的备份与还原XFS文件系统备份xfsdump语法xfsdump备份完整的文件系统用xfsdump进行增量备份XFS文件系统还原xfsrestore语法用xfsrestore观察和xfsdump后的备份数据内容简单恢复level 0 的文件系统恢复增量备份数据仅还原部分文件到xfsrestore交互模式L…

深入剖析Android视图层次结构,为什么UI界面如此多样化?

简述 在Android Framework中&#xff0c;渲染机制是指如何为应用程序的用户界面绘制和布局视图&#xff08;View&#xff09;。Android的视图层次结构&#xff08;View Hierarchy&#xff09;是由视图树中的每个节点表示的 。当更新视图树时&#xff0c;Android会执行以下流程…

记一次内存泄漏问题的排查

阶段一&#xff1a; 前段时间&#xff0c;突然发现服务在毫无征兆的情况下发生了重启。去看了一下容器退出的日志&#xff0c;发现内存利用率超过了100%&#xff0c;导致容器重启&#xff0c;进一步看了skyWalking&#xff0c;发现heap内存超了&#xff0c;当时只是简单的以为是…

HTML2.1列表标签

列表标签种类 无序列表 有序列表 自定义列表 使用场景&#xff1a;在列表中按照行展示关联性内容。 特点&#xff1a;按照行的形式&#xff0c;整齐显示内容。 一、无序列表 标签名说明ul无序列表整体&#xff0c;用于包裹li标签li表示无序列表的每一项&#xff0c;用于包…

【iOS】—— 消息传递和消息转发

消息传递和消息转发 文章目录消息传递和消息转发消息传递&#xff08;方法调用&#xff09;IMP指针IMP与SEL的区别与联系SEL是通过表取对应关系的IMP&#xff0c;进行方法的调用快速查找imp过程汇编代码查找过程总结消息发送快速查找imp(汇编):方法缓存慢速查找总结慢速查找消息…

全链路日志追踪

背景 最近线上的日志全局追踪 traceId 不好使了&#xff0c;不同请求经常出现重复的 traceId&#xff0c;或者通过某个请求的 traceId 追踪搜索&#xff0c;检索出了与该请求完全不相干的日志。我领导叫我去排查解决这个问题&#xff0c;这里我把我排查的过程思路以及如何解决…

真题详解(单元测试)-软件设计(五十)

真题详解(0/1背包)-软件设计&#xff08;四十九)https://blog.csdn.net/ke1ying/article/details/130163955 单元测试 五个特征&#xff1a;模块接口、局部数据结构、重要执行路径、出错处理、边界条件。 模块接口&#xff0c;保证测试模块数据流正确的流入和流出。 测试模块用…

大数据相关知识

1、大数据整体简介 1、1 简介 百度百科这样写道 大数据&#xff08;big data&#xff09;&#xff0c;IT行业术语&#xff0c;是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发现力和流程优…

C++类的学习1

类的定义一般包括两部分&#xff0c;一是类的属性&#xff0c;二是他所拥有的方法。类的实例化是指给类的加载并初始化过程&#xff0c;比如一个people类&#xff0c;我们具体到每一个人就是类的实例化&#xff0c;此外一个类可以在此类上进行扩展。比如people类&#xff0c;我…

vue项目使用luckyexcel预览excel表格

场景 最近工作中在开发文档上传并能在新窗口打开预览的功能。在此记录下心路历程。 方法 我总共尝试了2种方法预览excel&#xff0c;均可实现。还发现一种方法可以实现&#xff0c;需要后端配合&#xff0c;叫做KKfileview。 1.使用luckyexcel插件实现xlsx的预览 2.使用fi…

组合式升降压PFC的分析方法

1. 组合式升降压PFC的基本原理 组合式升降压PFC采用两组储能元件&#xff0c;基本单元为Cuk&#xff0c;Sepic和Zeta。参考论文《New Efficient Bridgeless Cuk Rectifiers for PFC Applications》中的三种拓扑进行分析。   Cuk型PFC的TypeI如下图所示&#xff0c;正半周Dp一…

Jef-log-tail日志采集工具使用说明

介绍 jef-log-tail是一款基于netty实现的日志采集工具&#xff0c;支持指定目录、指定文件、指定后缀的动态持续日志采集&#xff0c;日常使用场景如&#xff1a;集群部署后将多台主机的日志集中存放到一台日志服务器上&#xff0c;或者将日志统一输出到数据库、redis、kafka、…

STM32实战项目-温湿度传感器

程序功能&#xff1a; 1、软件模拟I2C协议与SHT30数字温湿度传感器通讯&#xff1b; 2、数码管显示环境温湿度&#xff1b; 3、串口打印环境温湿度。 目录 一、硬件电路 二、技术讲解 2.1IIC简介 2.2 IIC总线协议 2.2设备接入 三、SHT30数字温湿度传感器 3.1性能介绍 …

Rollup 实践:插件生态和实用技巧(续)

在前面的文章中&#xff0c;我们已经了解了 Rollup 的性能优化和高级用法。本篇文章将继续探讨 Rollup 的插件生态和实用技巧。 插件生态 Rollup 拥有一个丰富的插件生态&#xff0c;下面我们介绍几个实用的插件&#xff1a; rollup-plugin-terser&#xff1a;使用 Terser 压…

FlowForge 使用教程 团队资源管理

前言 本篇文章结合FF的操作来给大家解释一下,FF在团队管理上都与那些资源可以操作。 团队创建 使用超管第一次登录FF平台,默认什么资源都没有,你只能先去创建一个团队才能继续往下操作。 在FF平台上,团队就代表一个租户,也是一种资源隔离的手段。 创建团队可以通过右上…

Rollup 实践:性能优化和高级用法(续)

在前面的文章中&#xff0c;我们已经了解了 Rollup 的基本概念和配置。本篇文章将继续探讨 Rollup 的性能优化和高级用法。 懒加载 通过 Rollup 的代码分割功能&#xff0c;我们可以实现懒加载&#xff0c;从而减小初始页面加载时间。假设我们有一个动态导入的模块 dynamic.j…

vue项目用后端返回的文件流实现docx和pdf文件预览

前端docx和pdf文件预览实现效果图docx-preview文件预览pdf文件预览写这篇文章的目的&#xff0c;是因为我比较懒&#xff0c;想把代码记录一下&#xff0c;方便日后使用&#xff1b;哈哈&#xff0c;如果你也需要&#xff0c;也可以复制粘贴啊&#xff0c;为了方便自己和需要的…

windows10开发环境下部署kafka消息服务

下载kafka&#xff0c;官方地址https://kafka.apache.org/downloads 百度网盘链接&#xff1a;https://pan.baidu.com/s/1h3iXtfzEIBoajGPId5Dcag?pwd0000 提取码&#xff1a;0000直接把下载的文件解压到某个盘的根目录&#xff0c;要不然后面的命令就会遇到“命令行过长”的报…

linux 系统的一些使用小技巧

实现RedHat非正常关机的自动磁盘修复 先登录到服务器&#xff0c;然后在/etc/sysconfig里增加一个文件autofsck,内容如下&#xff1a; AUTOFSCK_DEF_CHECKyes PROMPTyes 改变文件或目录之最后修改时间(变为当前时间) 执行格式&#xff1a;touch name ( name 可为文件或目录名称…