简单时间协议SNTP及转化时间戳程序

工作原理

SNTP协议采用客户/服务器工作方式,服务器通过接收GPS信号或自带的原子钟作为系统的时间基准,客户机通过定期访问服务器提供的时间服务获得准确的时间信息,并调整自己的系统时钟,达到网络时间同步的目的。

系统时钟同步的工作过程如下:

Device A发送一个NTP报文给Device B,该报文带有它离开Device A时的时间戳,该时间戳为10:00:00am(T1)。

当此NTP报文到达Device B时,Device B加上自己的时间戳,该时间戳为11:00:01am(T2)。

当此NTP报文离开Device B时,Device B再加上自己的时间戳,该时间戳为11:00:02am(T3)。

当Device A接收到该响应报文时,Device A的本地时间为10:00:03am(T4)。

NTP报文的往返时延Delay=(T4-T1)-(T3-T2)=2秒

Device A相对Device B的时间差offset=((T2-T1)+(T3-T4))/2=1小时

报文格式

SNTP协议是UDP协议的客户,它利用UDP的123端口提供服务,SNTP客户在设置请求信息时要把UDP目的端口设置为该值,源端口可以为任何非零值,服务器在响应信息中对这些值进行交换。同其它应用层协议一样,SNTP协议的数据通信也是按数据帧的格式进行。

LI:当前时间闰秒标志。字段长度为2位整数,只在服务器端有效。

    LI=0:无警告;

    LI=1:最后一分钟是61秒;

    LI=2:最后一分钟是59秒;

LI=3:警告(时钟没有同步)

服务器在开始时,LI设置为3,一旦与主钟取得同步后就设置成其它值。

 

VN:版本号。字段长度为3位整数,当前版本号为4。

Mode:指示协议模式。字段长度为3位,取值定义为:

    Mode=0:保留

    Mode=1:对称主动;

    Mode=2:对称被动;

    Mode=3:客户;

Mode=4:服务器;

Mode=5:广播;

Mode=6:保留为NTP控制信息;

Mode=7:保留为用户定义;

    在单播和多播模式,客户在请求时把这个字段设置为3,服务器在响应时把这个字段设置为4。在广播模式下,服务器把这个字段设置为5。

 

Stratum:指示服务器工作的级别,该字段只在服务器端有效,字段长度为8位整数。取值定义为:

Stratum=0:故障信息;

Stratum=1:一级服务器;

Stratum=2-15:二级服务器;

Stratum=16-255:保留;

 

Poll Interval:指示数据包的最大时间间隔,以秒为单位,作为2的指数方的指数部分,该字段只在服务器端有效。字段长度为8位整数,取值范围从4-17,即16秒到131,072秒。

 

Precision:指示系统时钟的精确性,以秒为单位,作为2的指数方的指数部分,该字段只在服务器端有效。字段长度为8位符号整数,取值范围从-6到-20。

 

Root Delay:指示与主时钟参考源的总共往返延迟,以秒为单位,该字段只在服务器端有效。字段长度为32位浮点数,小数部分在16位以后,取值范围从负几毫秒到正几百毫秒。

 

Root Dispersion:指示与主时钟参考源的误差,以秒为单位,该字段只在服务器端有效。字段长度为32位浮点数,小数部分在16位以后,取值范围从零毫秒到正几百毫秒。

Reference Identifier:指示时钟参考源的标记,该字段只在服务器端有效。对于一级服务器,字段长度为4字节ASCII字符串,左对齐不足添零。对于二级服务器,在IPV4环境下,取值为一级服务器的IP地址,在IPV6环境下,是一级服务器的NSAP地址。

Reference Timestamp:指示系统时钟最后一次校准的时间,该字段只在服务器端有效,以前面所述64位时间戳格式表示。

Originate Timestamp:指示客户向服务器发起请求的时间,以前面所述64位时间戳格式表示。

Transmit Timestamp:指示服务器向客户发时间戳的时间,以前面所述64位时间戳格式表示。

Authenticator(可选):当需要进行SNTP认证时,该字段包含密钥和信息加密码

 

基本工作过程

最常用工作模式-单播模式

SNTP服务器在初始化时,Stratum字段设置为0,LI字段设置为3,Mode 字段设置为3,Reference Identifier字段设置为ASCII字符“INIT”,所有时间戳信息设置为0;

一旦SNTP服务器与外部时钟源取得同步后,进入工作状态,Stratum字段设置为1,LI字段设置为0,Reference Identifier字段设置为外部时钟源的ASCII字符,如“GPS”,Precision字段设置为-6到-20之间的一个数值,通常设置为-16。VN字段设置为客户端请求信息包的VN字段值,Root Delay和Root Dispersion字段通常设置为0,Reference Timestamp字段设置为从外部时钟源最新取得的时间,Originate Timestamp字段设置为客户请求包的Transmit Timestamp字段值,Transmit Timestamp字段设置为服务器发出时间戳给客户的时间。

SNTP服务器在工作过程中,如果与外部时钟源失去同步,Stratum字段设置为0,Reference Identifier字段设置为故障原因的ASCII字符,如:“LOST”,此时客户收到这个信息时,要丢弃服务器发给它的时间戳信息。 

两种时间戳转化的方法。 

 一、库函数实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef unsigned int u32_t;
typedef unsigned char u8_t;
typedef struct
{
	u32_t year;
	u32_t month;
	u32_t day;
	u32_t hour;
	u32_t minute;
	u32_t second;
}realtime;

realtime unixTimeStamp_to_beijingTime(u32_t time)
{
	time_t t_unix;				
	struct tm *lt;
	realtime t_location;	//本地时间
	t_unix=time;			//Unix时间+8小时为北京时间
	lt=localtime(&t_unix);
	t_location.year=lt->tm_year+1900;
	t_location.month=lt->tm_mon+1;
	t_location.day=lt->tm_mday;
	t_location.hour =lt->tm_hour;
	t_location.minute=lt->tm_min;
	t_location.second=lt->tm_sec;
	return t_location;
}

int main()
{
	realtime temp;
	u32_t timestamp;
	printf("please input timestamp = ");
	scanf("%d",&timestamp);
	temp = unixTimeStamp_to_beijingTime(timestamp);
	printf("beijing time = %d/%d/%d %d:%02d:%02d \n", temp.year, temp.month, temp.day, temp.hour, temp.minute, temp.second);
	return 0;
}

二、 编程运算实现

#include <stdio.h>
#include <stdbool.h>

static int DAYMS = 24*3600;
static int FOURYEARS = 365*3 + 366;
static int norMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static int leapMonth[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

void getHMS(int times)
{
	int nHour = times/3600;
	int nMin = times%3600/60;
	int nSec = times%3600%60;
	printf("%d:%d:%d\n",nHour, nMin, nSec);
}

void getMonAndDay(int nDays, int *nMonth, int *nDay, bool IsLeapYear)
{
	//printf("%d\n", nDays);
	int i = 0;
	int *pMonths = IsLeapYear?leapMonth:norMonth;
	for( i = 0; i < 12; i++)
	{
		int nTemp = nDays - pMonths[i];
		if(nTemp <= 0)
		{
			*nMonth = i + 1;
			if(nTemp == 0)
			{
				*nDay = pMonths[i];
			}
			else
			{
				*nDay = nDays;
			}
			break;
		}
		nDays = nTemp;
	    //printf("%d\n", nDays);
	}
}

int main()
{
	int temp;
	scanf("%d", &temp);
	temp += 28800;
	int nDays = temp/DAYMS + 1;
	int nYear4 = nDays/FOURYEARS;
	int nRemain = nDays%FOURYEARS;
	int nDecYear = 1970 + nYear4 * 4;
	int nDecMonth = 0;
	int nDecDay = 0;
	bool bLeapYear = false;

	if(nRemain < 365)
	{
	}
	else if(nRemain < 365 * 2)
	{
		nDecYear += 1;
		nRemain -= 365;
	}
	else if(nRemain < 365 * 3)
	{
		nDecYear += 2;
		nRemain -= (365 + 365);
	}
	else
	{
		nDecYear += 3;
		nRemain -= (365 + 365 + 365);
		bLeapYear = true;
	}
	getMonAndDay(nRemain, &nDecMonth, &nDecDay, bLeapYear);
	printf("%d/%d/%d\n", nDecYear, nDecMonth, nDecDay);
	getHMS(temp%DAYMS);


	return 0;
}

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
安信可wifi模块esp8266获取互联网时间,精确到小时分钟/秒/年月日等。 static void ICACHE_FLASH_ATTR SetDsTime() { uint32_t time = sntp_get_current_timestamp(); if(time>0) { os_timer_disarm(&SetdsTimer;); os_printf("time:%d\r\n",time); os_printf("date:%s\r\n",sntp_get_real_time(time));//传入time参数给sntp_get_real_time char* tttt="Wed Dec 07 16:34:45 2016"; os_strcpy(tttt,sntp_get_real_time(time)); //因为后期使用的os_strncpy函数不能自动将字符串中的\0字符串结束符号一并拷贝进来,所以这里根据年月日的长度构造函数,并初始化后各位有\0符号,不至于函数崩溃 char chsec[3]={""}; char chmin[3]={""}; char chhour[3]={""}; char chwday[4]={""}; char chmday[3]={""}; char chmon[4]={""}; char chyear[5]={""}; //待截取字符串指针名即为字符串的起始地址,移动多少个后即为第I位的起始地址,也就是截取的起点 os_strncpy(chsec,tttt+17,2); os_strncpy(chmin,tttt+14,2); os_strncpy(chhour,tttt+11,2); os_strncpy(chwday,tttt,3); os_strncpy(chmday,tttt+8,2); os_strncpy(chmon,tttt+4,3); os_strncpy(chyear,tttt+20,4); os_printf("\r\nSubdate:%s,%s,%s,%s,%s,%s-----%s\r\n",chyear,chmon,chmday,chhour,chmin,chsec,chwday);//传入time参数给sntp_get_real_time //赋值给时间设置变量 timedate.tm_year=(chyear[2]-48)*10+(chyear[3]-48); if(strcmp(chmon,"Jan")==0) timedate.tm_mon=1; else if(strcmp(chmon,"Feb")==0) timedate.tm_mon=2; else if(strcmp(chmon,"Mar")==0) timedate.tm_mon=3; else if(strcmp(chmon,"Apr")==0) timedate.tm_mon=4; else if(strcmp(chmon,"May")==0) timedate.tm_mon=5; else if(strcmp(chmon,"Jun")==0) timedate.tm_mon=6; else if(strcmp(chmon,"Jul")==0) timedate.tm_mon=7; else if
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值