用C#(.NET8)开发一个NTP(SNTP)服务

news2024/12/21 9:11:12

完整源码,附工程下载,工程其实也就下面两个代码。
想在不能上网的服务器局域网中部署一个时间服务NTP,当然系统自带该服务,可以开启,本文只是分享一下该协议报文和能跑的源码。网上作为服务的源码不太常见,能见到不一定能跑起来,缺胳膊少脚的,分享代码一定要分享全。
先来效果图:
开启程序后,让win系统来同步本机时间效果如下:

添加图片注释,不超过 140 字(可选)

用通用客户端GuerrillaNtp来读,也没有问题:
在这里插入图片描述

添加图片注释,不超过 140 字(可选)
Fuck Shut Up,Show me the code
源码如下:
运行入口:

namespace NTP
{
	internal class Program
	{
		static void Main(string[] args)
		{
			Console.WriteLine("Hello, NTP(SNTP)!");
			Console.WriteLine("本程序占用 UDP 123 端口,如遇冲突,请关闭系统自带时间服务 W32Time(Windows Time)");
			new SNTPServer().Start();
			Console.ReadLine();
		}
	}
}

主程序代码:

using System.Net.Sockets;
using System.Net;
using System.Buffers.Binary;

namespace NTP
{
	/// <summary>
	/// 简单网络时间协议服务器
	/// </summary>
	public class SNTPServer
	{
		int port = 123;             //服务端口,NTP默认端口123
		bool stopFlag = false;      //通知后台线程停止消息循环的标识
		Thread tdServer;            //服务器后台监听线程

		/// <summary>
		/// 初始化一个简单网络时间协议服务器
		/// </summary>
		public SNTPServer()
			: this(123) { }
		/// <summary>
		/// 使用指定参数初始化一个简单网络时间协议服务器
		/// </summary>
		/// <param name="port">服务端口</param>
		public SNTPServer(int port)
		{
			this.port = port;
		}
		/// <summary>
		/// 获取和设置服务端口号
		/// </summary>
		public int Port
		{
			get { return this.port; }
			set { this.port = value; }
		}
		/// <summary>
		/// 启动服务器
		/// </summary>
		public void Start()
		{
			if (tdServer == null || (!tdServer.IsAlive))
			{
				tdServer = new Thread(bgWork);
				tdServer.IsBackground = true;
				stopFlag = false;
				tdServer.Start();
			}
		}
		/// <summary>
		/// 停止服务器
		/// </summary>
		public void Stop()
		{
			stopFlag = true;
		}

		private void bgWork()
		{
			IPEndPoint iep;
			UdpClient udpclient;
			try
			{
				iep = new IPEndPoint(IPAddress.Any, port);
				udpclient = new UdpClient(iep);
				while (!stopFlag)
				{
					if (udpclient.Available > 0)
					{
						Console.WriteLine("收到一个请求:" + iep.Address + " 端口:" + iep.Port);

						byte[] buffer;
						IPEndPoint remoteipEP = new IPEndPoint(IPAddress.Any, port);
						buffer = udpclient.Receive(ref remoteipEP);
						if (buffer.Length >= 48)
						{
							var bytesReceive = buffer.AsSpan();
							Console.WriteLine("收到数据[" + buffer.Length + "]:" + BitConverter.ToString(buffer));
							//记录收到请求的时间
							DateTime ReceiveTimestamp = DateTime.Now;

							//对方请求发出的时间,拿来显示看看
							DateTime? OriginateTimestamp = Decode(BinaryPrimitives.ReadInt64BigEndian(bytesReceive[40..]));
							//传输的都是国际时间,这里显示方便转成北京时间
							Console.WriteLine("对方请求发出时间:" + OriginateTimestamp?.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"));

							var Mode = 4;
							var VersionNumber = 3;
							var LeapIndicator = 0;
							var Stratum = 4;//Stratum 0是最高层级,代表原子钟、GPS接收器或其他高精度的时间源。
							var Poll = 0x0A;//NTP客户端向远程服务器发送时间请求的间隔(以秒为单位)。
							var Precision = 0xE9;

							buffer = new byte[48];
							var bytes = buffer.AsSpan();
							//LI(Leap Indicator):长度为2比特,值为“11”时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。
							//VN(Version Number):长度为3比特,表示NTP的版本号,目前的最新版本为3。
							//Mode:长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。
							//0x1C = 00 011 100
							bytes[0] = (byte)(((uint)LeapIndicator << 6) | ((uint)VersionNumber << 3) | (uint)Mode);
							bytes[1] = (byte)Stratum;
							bytes[2] = (byte)Poll;
							bytes[3] = (byte)Precision;
							BinaryPrimitives.WriteInt32BigEndian(bytes[4..], Encode(TimeSpan.Zero));//RootDelay = 0; Root Delay‌是指从主参考时钟到NTP服务器之间的往返时间延迟。它表示从主参考时钟到NTP服务器再返回的总体时间,通常以毫秒为单位。
							BinaryPrimitives.WriteInt32BigEndian(bytes[8..], Encode(TimeSpan.Zero));//RootDispersion = 0; Root Dispersion‌是指本地时钟相对于主参考时钟的最大误差。它表示NTP服务器时钟与主参考时钟之间的最大时间偏差,通常以毫秒为单位。
							BinaryPrimitives.WriteUInt32BigEndian(bytes[12..], BitConverter.ToUInt32(new byte[] { 0x41, 0x43, 0x54, 0x53 }, 0));//ReferenceIdentifier
							BinaryPrimitives.WriteInt64BigEndian(bytes[16..], Encode(DateTime.Now.ToUniversalTime()));//ReferenceTimestamp  系统时钟最后一次被设定或更新的时间
							//对方请求发出的时间 转成datetime 再转字节 会造成精度损失,【请求端会不认该报文】,应该返回原装字节,																						  //BinaryPrimitives.WriteInt64BigEndian(bytes[24..], Encode(OriginateTimestamp));
							bytes[24] = bytesReceive[40];
							bytes[25] = bytesReceive[41];
							bytes[26] = bytesReceive[42];
							bytes[27] = bytesReceive[43];
							bytes[28] = bytesReceive[44];
							bytes[29] = bytesReceive[45];
							bytes[30] = bytesReceive[46];
							bytes[31] = bytesReceive[47];

							BinaryPrimitives.WriteInt64BigEndian(bytes[32..], Encode(ReceiveTimestamp.ToUniversalTime()));//ReceiveTimestamp
							BinaryPrimitives.WriteInt64BigEndian(bytes[40..], Encode(DateTime.Now.ToUniversalTime()));//TransmitTimestamp

							Console.WriteLine("返回数据[" + buffer.Length + "]:" + BitConverter.ToString(buffer));
							//系统NTP真实返回报文示例
							//buffer = new byte[] { 0x1C,0x04,0x00,0xE9,0x00,0x00,0x21,0xAA,0x00,0x00,0x16,0x0C,0x34,0xE7,0x72,0xB7,0xEB,0x0E, 0x62, 0x72,0xF7,0xCF,0x33,0xAF,0xEB,0x0E, 0x66, 0x3E, 0x79, 0x8B,0xB0,0x00,0xEB,0x0E, 0x66, 0x3E, 0x97, 0xCE,0xE6,0x82,0xEB,0x0E, 0x66, 0x3E, 0x97, 0xCF,0x51,0xE2 };
							udpclient.Send(buffer, buffer.Length, remoteipEP);
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
			}
		}
		private const double FACTOR = 1L << 32;
		static readonly DateTime epoch = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc) + TimeSpan.FromSeconds(1L << 32);
		static int Encode(TimeSpan time) => (int)(time.TotalSeconds * (1 << 16));
		static long Encode(DateTime? time) => time == null ? 0 : Convert.ToInt64((time.Value - epoch).TotalSeconds * (1L << 32));
		static DateTime? Decode(long bits) => bits == 0 ? null : epoch + TimeSpan.FromSeconds(bits / FACTOR);
	}
}

本代码亲自测试,木有问题.
工程下载:download.csdn.net/download/wudizhukk/90159750

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

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

相关文章

Connection lease request time out 问题分析

Connection lease request time out 问题分析 问题背景 使用apache的HttpClient&#xff0c;我们知道可以通过setConnectionRequestTimeout()配置从连接池获取链接的超时时间&#xff0c;而Connection lease request time out正是从连接池获取链接超时的报错&#xff0c;这通常…

【课程论文系列实战】:随机对照实验驱动的电商落地页优化

数据与代码见文末 摘要 随机对照试验&#xff08;Randomized Controlled Trial&#xff0c;RCT&#xff09;被认为是因果推断的“金标准”方法。通过随机分配实验参与者至不同组别&#xff0c;确保了组间可比性&#xff0c;RCT能够有效地消除样本选择偏差和混杂变量问题。本文…

UML 建模实验

文章目录 实验一 用例图一、安装并熟悉软件EnterpriseArchitect16二、用例图建模 实验二 类图、包图、对象图类图第一题第二题 包图对象图第一题第二题 实验三 顺序图、通信图顺序图银行系统学生指纹考勤系统饮料自动销售系统“买到饮料”“饮料已售完”“无法找零”完整版 通信…

高质量翻译如何影响软件用户体验 (UX)

在软件开发领域&#xff0c;用户体验 (UX) 是决定产品成败的关键因素之一。一个流畅、吸引人且直观的用户体验可以决定一款软件的成功与否。在影响优秀用户体验的众多因素中&#xff0c;高质量翻译尤为重要&#xff0c;尤其是在当今全球化的市场环境中。确保软件为不同语言和文…

ArcGIS Pro 3.4新功能2:Spatial Analyst新特性,密度、距离、水文、太阳能、表面、区域分析

Spatial Analyst 扩展模块在 ArcGIS Pro 3.4 中引入了新功能和增强功能。此版本为您提供了用于表面和区域分析的新工具以及改进的密度和距离分析功能&#xff0c;多种用于水文分析的工具性能的提高&#xff0c;一些新的太阳能分析功能。 目录 1.密度分析 2.距离分析 3.水文…

Linux C 程序 【05】异步写文件

1.开发背景 Linux 系统提供了各种外设的控制方式&#xff0c;其中包括文件的读写&#xff0c;存储文件的介质可以是 SSD 固态硬盘或者是 EMMC 等。 其中常用的写文件方式是同步写操作&#xff0c;但是如果是写大文件会对 CPU 造成比较大的负荷&#xff0c;采用异步写的方式比较…

凯酷全科技抖音电商服务的卓越践行者

在数字经济蓬勃发展的今天&#xff0c;电子商务已成为企业增长的新引擎。随着短视频平台的崛起&#xff0c;抖音作为全球领先的短视频社交平台&#xff0c;不仅改变了人们的娱乐方式&#xff0c;也为品牌和商家提供了全新的营销渠道。厦门凯酷全科技有限公司&#xff08;以下简…

精准提升:从94.5%到99.4%——目标检测调优全纪录

&#x1f680; 目标检测模型调优过程记录 在进行目标检测模型的训练过程中&#xff0c;我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化&#xff0c;每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试&#…

STM8单片机学习笔记·GPIO的片上外设寄存器

目录 前言 IC基本定义 三极管基础知识 单片机引脚电路作用 STM8GPIO工作模式 GPIO外设寄存器 寄存器含义用法 CR1&#xff1a;Control Register 1 CR2&#xff1a;Control Register 2 ODR&#xff1a;Output Data Register IDR&#xff1a;Input Data Register 赋值…

国标GB28181平台EasyGBS在安防视频监控中的信号传输(电源/视频/音频)特性及差异

在现代安防视频监控系统中&#xff0c;国标GB28181协议作为公共安全视频监控联网系统的国家标准&#xff0c;该协议不仅规范了视频监控系统的信息传输、交换和控制技术要求&#xff0c;还为不同厂商设备之间的互联互通提供了统一的框架。EasyGBS平台基于GB28181协议&#xff0c…

如何使用checkBox组件实现复选框

文章目录 概念介绍使用方法示例代码我们在上一章回中介绍了DatePickerDialog Widget相关的内容,本章回中将介绍Checkbox Widget.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的Checkbox也是叫复选框,没有选中时是一个正方形边框,边框内容是空白的,选中时会…

基于“2+1 链动模式商城小程序”的微商服务营销策略探究

摘要&#xff1a;本文探讨在竞争激烈的市场经济与移动互联网时代背景下&#xff0c;微商面临的机遇与挑战。着重分析“21 链动模式商城小程序”如何助力微商改变思路&#xff0c;通过重视服务、提升服务质量&#xff0c;以服务营销放大利润&#xff0c;实现从传统微商模式向更具…

1-1 STM32-0.96寸OLED显示与控制

1.0 模块原理图 2.0 0.96OLED简介 资料下载&#xff1a;https://jiangxiekeji.com/download.html 程序介绍&#xff1a;https://jiangxiekeji.com/tutorial/oled.html SSD1306是一款OLED/PLED点阵显示屏的控制器&#xff0c;可以嵌入在屏幕中&#xff0c;用于执行接收数据、显…

在Visual Studio 2022中配置C++计算机视觉库Opencv

本文主要介绍下载OpenCV库以及在Visual Studio 2022中配置、编译C计算机视觉库OpenCv的方法 1.Opencv库安装 ​ 首先&#xff0c;我们需要安装OpenCV库&#xff0c;作为一个开源库&#xff0c;我们可以直接在其官网下载Releases - OpenCV&#xff0c;如果官网下载过慢&#x…

QT:QDEBUG输出重定向和命令行参数QCommandLineParser

qInstallMessageHandler函数简介 QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) qInstallMessageHandler 是 Qt 框架中的一个函数&#xff0c;用于安装一个全局的消息处理函数&#xff0c;以替代默认的消息输出机制。这个函数允许开发者自定义 Qt 应用…

网站灰度发布?Tomcat的8005、8009、8080三个端口的作用什么是CDNLVS、Nginx和Haproxy的优缺点服务器无法开机时

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

OpenGL ES 03 加载3张图片并做混合处理

OpenGL ES 02 加载3张图片并做混合处理 什么是纹理单元纹理单元的作用使用纹理单元的步骤详细解释加载图片并绑定到到GPU纹理单元采样器的设置1.设置采样器变量的纹理单元编号&#xff0c;目的是为了告诉纹理采样器&#xff0c;从哪个纹理单元采集数据2.如果你没有显式地设置采…

临床外显子组测序分析中的那些坑(上)

大规模并行测序技术或下一代测序已成为基因诊断和研究的标准技术&#xff0c;尤其是外显子组和基因组测序现在已经在世界范围内广泛应用于患者的分子诊断。在过去几年中&#xff0c;许多实验室都在努力应对基于全新技术建立基因检测工作流程的挑战。测序技术中持续引入新的仪器…

【Tomcat】第六站(最后一站啦!):数据的返回

1. 引言 前端资源比如html页面&#xff0c;进行返回。截止到目前我们写的项目架构不支持前端页面&#xff08;静态资源 &#xff09;。 2. 数据的返回 2.1 准备 为了能够写前端页面&#xff0c;新建一个项目。选择Maven项目&#xff0c;下一步&#xff0c;下一步。 加载完…

ai论文生成器:分享8款AI一键生成论文的写作软件

在撰写毕业论文的过程中&#xff0c;高效利用各类软件工具可以极大地提升写作效率与质量。以下是八个免费的神器软件工具&#xff0c;它们各自在论文撰写、文献管理、语法校对、数据可视化等方面发挥着重要作用。希望这些推荐能帮助你顺利完成毕业论文的写作。 千笔AI论文&…