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

完整源码,附工程下载,工程其实也就下面两个代码。
想在不能上网的服务器局域网中部署一个时间服务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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[机器学习]XGBoost(3)——确定树的结构

XGBoost的目标函数详见[机器学习]XGBoost&#xff08;2&#xff09;——目标函数&#xff08;公式详解&#xff09; 确定树的结构 之前在关于目标函数的计算中&#xff0c;均假设树的结构是确定的&#xff0c;但实际上&#xff0c;当划分条件不同时&#xff0c;叶子节点包含的…

51c视觉~合集33

我自己的原文哦~ https://blog.51cto.com/whaosoft/12163849 #Robin3D 3D场景的大语言模型&#xff1a;在鲁棒数据训练下的3DLLM新SOTA! 论文地址&#xff1a;https://arxiv.org/abs/2410.00255代码将开源&#xff1a;https://github.com/WeitaiKang/Robin3D 介绍 多模态…

【游戏中orika完成一个Entity的复制及其Entity异步落地的实现】 1.ctrl+shift+a是飞书下的截图 2.落地实现

一、orika工具使用 1)工具类 package com.xinyue.game.utils;import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory;/*** author 王广帅* since 2022/2/8 22:37*/ public class XyBeanCopyUtil {private static MapperFactory mappe…

黑马Redis数据结构学习笔记

Redis数据结构 动态字符串 Intset Dict ZipList QuickList SkipList 类似倍增 RedisObject 五种数据类型 String List Set ZSet Hash

GTID详解

概念和组成 1&#xff0c;全局事务表示&#xff1a;global transaction identifiers 2, GTID和事务一一对应&#xff0c;并且全局唯一 3&#xff0c;一个GTID在一个服务器上只执行一次 4&#xff0c;mysql 5.6.5开始支持 组成 GTID server_uuid:transaction_id 如&#xf…

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法

怎么将pdf中的某一个提取出来&#xff1f;传统上&#xff0c;我们可能通过手动截取屏幕或使用PDF阅读器的复制功能来提取信息&#xff0c;但这种方法往往不够精确&#xff0c;且无法保留原文档的排版和格式。此外&#xff0c;很多时候我们需要提取的内容可能涉及多个页面、多个…

RTU 通信模块赋能智慧路灯远程开关管理,点亮智慧城市节能增效

RTU&#xff08;Remote Terminal Unit&#xff09;远端测控单元在智慧路灯远程开关管理系统中主要负责数据通信和开关控制。能够实现对路灯设备的远程监测和控制&#xff0c;将路灯的状态信息&#xff08;如开关状态、故障信息、亮度参数等&#xff09;上传到管理平台&#xff…

【Canvas与艺术】红色3号桌球

【注】 此图立体感还差点&#xff0c;以后改进吧。 【成图】 120*120的png图标&#xff1a; 大小图&#xff1a; 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8&q…

从源码分析swift GCD_DispatchGroup

前言&#xff1a; 最近在写需求的时候用到了DispatchGroup&#xff0c;一直没有深入去学习&#xff0c;既然遇到了那么就总结下吧。。。。 基本介绍&#xff1a; 任务组&#xff08;DispatchGroup&#xff09; DispatchGroup 可以将多个任务组合在一起并且监听它们的完成状态。…

线性代数基础与应用:基底 (Basis) 与现金流及单期贷款模型(中英双语)

具体请参考&#xff1a;https://web.stanford.edu/~boyd/vmls/ 下面的例子来源于这本书。 线性代数基础与应用&#xff1a;基底 (Basis) 与现金流及单期贷款模型 在线性代数中&#xff0c;基底&#xff08;Basis&#xff09;是一个重要的概念&#xff0c;广泛应用于信号处理、…

【python】OpenCV—Image Moments

文章目录 1、功能描述2、图像矩3、代码实现4、效果展示5、完整代码6、涉及到的库函数cv2.moments 7、参考 1、功能描述 计算图像的矩&#xff0c;以质心为例 2、图像矩 什么叫图像的矩&#xff0c;在数字图像处理中有什么作用&#xff1f; - 谢博琛的回答 - 知乎 https://ww…

【漏洞复现】CVE-2022-45206 CVE-2023-38905 SQL Injection

漏洞信息 NVD - CVE-2022-45206 Jeecg-boot v3.4.3 was discovered to contain a SQL injection vulnerability via the component /sys/duplicate/check. NVD - CVE-2023-38905 SQL injection vulnerability in Jeecg-boot v.3.5.0 and before allows a local attacker to…

现代风格VUE3易支付用户控制中心

适用系统 彩虹易支付 技术栈 vitevue3elementuiplusphp 亮点 独立前端代码,扩展开发,不改动系统文件,不影响原版升级 支持功能订制 界面预览

开发技术-Java改变图片格式

图片上传页未做控制&#xff0c;导致上传的是GIF格式&#xff0c;导致图片识别失败。需要将GIF格式转为JPEG格式。 代码&#xff0c;是找AI写的&#xff0c;记录一下&#xff1a; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; im…

【计算机视觉基础CV】03-深度学习图像分类实战:鲜花数据集加载与预处理详解

本文将深入介绍鲜花分类数据集的加载与处理方式&#xff0c;同时详细解释代码的每一步骤并给出更丰富的实践建议和拓展思路。以实用为导向&#xff0c;为读者提供从数据组织、预处理、加载到可视化展示的完整过程&#xff0c;并为后续模型训练打下基础。 前言 在计算机视觉的深…

Unity-Editor扩展GUI基本实现一个可拖拉放的格子列表

短短几百行代码,好吧,又是“参考”了国外的月亮 操作,还真地挺自然的。。。。。。国外的实现有点小牛 拖拉,增加+ 一个Element 鼠标左键长按,可以出提示 鼠标右键,清除Element, 有点小bug,不是很自然地完全清除, using System.Collections; using System.Collecti…

修改vscode中emmet中jsx和tsx语法中className的扩展符号从单引号到双引号 - HTML代码补全 - 单引号双引号

效果图 实现步骤 文件 > 首选项 > 设置搜索“”在settings.json中修改&#xff0c;增加 "emmet.syntaxProfiles": {"html": {"attr_quotes": "single"},"jsx": {"attr_quotes": "double","…

首批|云轴科技ZStack成为开放智算产业联盟首批会员单位

近日 &#xff0c;在Linux基金会AI & Data及中国开源软件推进联盟的指导之下&#xff0c;开放智算产业联盟成立大会在北京成功召开。在大会上&#xff0c;联盟首次公布了组织架构并颁发了首批会员单位证书。凭借ZStack AIOS平台智塔和在智算领域的技术创新&#xff0c;云轴…

HTN 78A3 6V~140V输入,3A实地异步降压变换器

1、特征 3A降压&#xff0c;内置250mΩ高侧功率管 输入电压范围:6V~140V 脉冲跳跃模式使得轻载下高效率 最高1MHZ可编程开关频率 COT纹波电压控制架构 欠压保护、过流保护和过热关断保护 无铅封装&#xff0c;ESOP8 2、应用 二轮电瓶车 太阳能系统 高压电池组 …

以太网帧、IP数据报图解

注&#xff1a;本文为 “以太网帧、IP数据报”图解相关文章合辑。 未整理去重。 以太网帧、IP数据报的图解格式&#xff08;包含相关例题讲解&#xff09; Rebecca.Yan已于 2023-05-27 14:13:19 修改 一、基础知识 UDP 段、IP 数据包&#xff0c;以太网帧图示 通信过程中&…