OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 10262|回复: 21

获取sntp服务器时间同步本地时间

[复制链接]

8

主题

102

帖子

1

精华

高级会员

Rank: 4

积分
991
金钱
991
注册时间
2014-11-19
在线时间
357 小时
发表于 2016-8-8 18:25:45 | 显示全部楼层 |阅读模式
本帖最后由 麦田稻草 于 2016-8-11 09:27 编辑

分享一下之前项目用到的同步网络时间的程序给大家,本例需要用到socket,要操作系统的支持,大家可以在 探索者F4 资料盘(A盘)\4,程序源码\3,扩展例程\2,LWIP扩展例程\网络实验10 NETCONN_WEBserver实验 的基础下测试。
rtc.h文件增加了如下结构体:
struct rtc_time
{
        u16 year;
        u8 month;
        u8 day;
        u8 week;
        u8 hour;
        u8 min;
        u8 sec;
        u8 ampm;
};

函数 void RTC_Get_Date(u8 *year,u8 *month,u8 *date,u8 *week);
改为 void RTC_Get_Date(u16 *year,u8 *month,u8 *date,u8 *week);
这些可以看着办,我主要是方便使用。

以下为代码,实现获取sntp服务器的时间,并同步更新本地时间(说明一下,我对时间精度要求不高,所以直接用服务器向客户发时间戳的时间来更新本地时间,经测试,误差小于一秒,这主要看网络状态和服务器的位置,我用的是深圳阿里云,我在广州):
[mw_shl_code=c,true]#include <string.h>  
#include <time.h>
#include "lwip/sockets.h"
#include "lwip/inet.h"
#include <netdb.h>  
#include "sntp.h"  
#include "rtc.h"

#define bzero(a, b)             memset(a, 0, b)

#define JAN_1970    2208988676UL+28701        /* 1970 - 1900 in seconds */  
#define NTP_SERVER_NAME "120.24.166.46"    //深圳阿里云
#define NTP_PORT    123  

typedef struct   
{  
    unsigned char LiVnMode;  
    unsigned char Stratum;  
    unsigned char Poll;  
    unsigned char Precision;  
    long int RootDelay;  
    long int RootDispersion;  
    char RefID[4];  
    long int RefTimeInt;  
    long int RefTimeFraction;  
    long int OriTimeInt;  
    long int OriTimeFraction;  
    long int RecvTimeInt;  
    long int RecvTimeFraction;  
    long int TranTimeInt;  
    long int TranTimeFraction;  
}STNP_Header;  
  

//获取sntp服务器的时间
int GetNTPTime(STNP_Header *H_SNTP)  
{  
    int sockfd=0;  
    struct sockaddr_in server;  
                fd_set set;   
    struct timeval timeout;  
     
               
    bzero((void*)H_SNTP, sizeof(STNP_Header));  //清零
    H_SNTP->LiVnMode = 0x1b;  //设置NTP报文格式,LI=0:无警告;  VN=3:NTP的版本号为3;  Mode=3:客户;
               
                //设置要连接的对方的IP地址和端口等属性
                bzero(&server,sizeof(server));//初始化结构体
                server.sin_family = AF_INET;    //设置地址家族
                server.sin_port = htons(NTP_PORT);  //设置端口
                if(inet_aton(NTP_SERVER_NAME,&server.sin_addr) <= 0)//设置地址
      printf("inet_pton error\r\n");
        
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);  //创建一个UDP socket
    if(sockfd<0)  
    {  
        printf("sockfd error!\r\n");  
        return -1;  
    }  
                //发送数据
    if(sendto(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0, (struct sockaddr*)&server, sizeof(server))<0)  
    {  
        printf("sendto error!\r\n");  
        return -1;  
    }  
      
    FD_ZERO(&set);  //清除描述词组set的全部位
    FD_SET(sockfd, &set);//设置描述词组set中相关fd的位
    timeout.tv_sec = 5;  //等待超时时间
    timeout.tv_usec = 0;  
      
    if( select(sockfd+1, &set, NULL, NULL, &timeout) <= 0 )  
                {
                                printf("select wait timeout!\r\n");  
        return -1;  
                }
    if(recv(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0)<0)  
                {
                                printf("recv error!\r\n");
        return -1;  
                }
      
    close(sockfd);  
    return 0;  
}  

//同步时间
//返回0:成功,-1:失败
int SYNC_Time(void)  
{  
    STNP_Header HeaderSNTP;  
    time_t t1;  
                struct timeval tv;  
                struct rtc_time st;
                struct tm *time;
        
                //获取sntp服务器数据
    printf("sync time from %s\n",NTP_SERVER_NAME);  
    if(GetNTPTime(&HeaderSNTP)<0)  
        return -1;         
                //从1900年1月1号0时0分0秒到服务器向客户发时间戳的时间,单位:秒
    t1 = ntohl(HeaderSNTP.TranTimeInt);  
                printf("t1:%u\r\n",t1);
                //减去1900年至1970年的时间
    tv.tv_sec=t1-JAN_1970;  
    printf("UTC:%s\r\n", ctime((const unsigned int *)&tv.tv_sec));  
               
                //把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为日历时间
                //获得的tm结构体的时间,是已经进行过时区转化为本地时间,年份加上1900,月份加上1
                time=localtime((const unsigned int *)&tv.tv_sec);
                printf("sntp:%u-%u-%u,%u:%u:%u,星期%d\r\n",(time->tm_year)+1900,(time->tm_mon)+1,time->tm_mday,time->tm_hour,time->tm_min,time->tm_sec,time->tm_wday);
               
                //获取rtc时间
                RTC_Get_Time(&st.hour,&st.min,&st.sec,&st.ampm);
                RTC_Get_Date(&st.year,&st.month,&st.day,&st.week);
                st.year=st.year+2000;
                printf("rtc1:%d-%d-%d,%d:%d:%d\r\n",st.year,st.month,st.day,st.hour,st.min,st.sec);
                //时间相差超过1分钟就更新时间
                if(st.year!=(time->tm_year)+1900 | st.month!=(time->tm_mon)+1 | st.day!=time->tm_mday)
                {
                        RTC_Set_Date((time->tm_year)+1900-2000,(time->tm_mon)+1,time->tm_mday,time->tm_wday);
                        printf("RTC set date!\r\n");
                }
                if(st.hour!=time->tm_hour | st.min!=time->tm_min)
                {
                        if(time->tm_hour<12) st.ampm=0;
                        else st.ampm=1;
                        RTC_Set_Time(time->tm_hour,time->tm_min,time->tm_sec,st.ampm);
                        printf("RTC set time!\r\n");
                }
                RTC_Get_Time(&st.hour,&st.min,&st.sec,&st.ampm);
                RTC_Get_Date(&st.year,&st.month,&st.day,&st.week);
                st.year=st.year+2000;
                printf("rtc2:%d-%d-%d,%d:%d:%d\r\n",st.year,st.month,st.day,st.hour,st.min,st.sec);
               
    return 0;  
}  
[/mw_shl_code]

网络实验10 NETCONN_WEBserver实验.rar

19.61 MB, 下载次数: 7768

专注一件事情并做到极致>>> https://shop275468129.taobao.com
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-8 23:11:05 | 显示全部楼层
楼主大大例程可以上传一下吗?
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-9 17:46:55 | 显示全部楼层
time=localtime((const unsigned int *)&tv.tv_sec); 这个tm结构体的时间怎么写的?
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-9 17:54:16 | 显示全部楼层
楼主指导一下吧  看了一个下午了 没有进展好烦 啊
回复 支持 反对

使用道具 举报

4

主题

133

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3860
金钱
3860
注册时间
2016-6-11
在线时间
709 小时
发表于 2016-8-9 18:52:01 | 显示全部楼层
a295055641 发表于 2016-8-9 17:46
time=localtime((const unsigned int *)&tv.tv_sec); 这个tm结构体的时间怎么写的?

指针的运用,&tv.tv_sec获取tv.tv_sec地址,(const unsigned int *)强制转化成unsigned int型,localtime函数获取时间参数到这片地址内。
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-10 08:53:37 | 显示全部楼层
为什么我的这句有错, struct tm *time;    我查了一下这个是在time.h里的我明明有 #include <time.h>  的啊 为什么啊  
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-10 09:35:10 | 显示全部楼层
为什么定义了 struct tm* time ;  time->tm_year还是有错
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-10 10:55:04 | 显示全部楼层
是不是必须要在有操作系统下才能用啊
回复 支持 反对

使用道具 举报

8

主题

102

帖子

1

精华

高级会员

Rank: 4

积分
991
金钱
991
注册时间
2014-11-19
在线时间
357 小时
 楼主| 发表于 2016-8-11 09:28:25 | 显示全部楼层
a295055641 发表于 2016-8-8 23:11
楼主大大例程可以上传一下吗?

上传了,你下载对比看一下
专注一件事情并做到极致>>> https://shop275468129.taobao.com
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-11 14:23:22 | 显示全部楼层
麦田稻草 发表于 2016-8-11 09:28
上传了,你下载对比看一下

是我的问题!现在做出来了 怎么时间比服务器的时间快了20秒的样子
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-11 14:33:58 | 显示全部楼层
a295055641 发表于 2016-8-11 14:23
是我的问题!现在做出来了 怎么时间比服务器的时间快了20秒的样子

把你的那个1900到1970 的时间改为了2208988800秒数对了可是时间差了8小时 应该 是时区的问题
回复 支持 反对

使用道具 举报

8

主题

102

帖子

1

精华

高级会员

Rank: 4

积分
991
金钱
991
注册时间
2014-11-19
在线时间
357 小时
 楼主| 发表于 2016-8-11 15:03:56 | 显示全部楼层
本帖最后由 麦田稻草 于 2016-8-11 15:06 编辑

差补多减,反正我就是这么调的,#define JAN_1970    2208988676UL+28701
我在后面加上28701,你也可以自己算出来试一下。。。
专注一件事情并做到极致>>> https://shop275468129.taobao.com
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-12 13:00:41 | 显示全部楼层
用的这个例程发现断电后上电的时候没接网线,然后在接网线的话就不能进行对时了,if( select(sockfd+1, &set, NULL, NULL, &timeout) <= 0 )  
        {
                printf("select wait timeout!\r\n");
                 close(sockfd);
        return -1;  这条语句中跳不过去
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-12 13:26:06 | 显示全部楼层
如果是你上传的那个程序 如果拔了网线再插上也没用了。必须要重启,你这个原因是没有close(sockfd);可是现在的问题是必须要在初始化的时候插上网线 然后的话 网线的插拔就没有影响了
回复 支持 反对

使用道具 举报

8

主题

102

帖子

1

精华

高级会员

Rank: 4

积分
991
金钱
991
注册时间
2014-11-19
在线时间
357 小时
 楼主| 发表于 2016-8-12 14:55:05 | 显示全部楼层
a295055641 发表于 2016-8-12 13:00
用的这个例程发现断电后上电的时候没接网线,然后在接网线的话就不能进行对时了,if( select(sockfd+1, &se ...

断电后上电的时候没接网线,你是怎么能跳到sntp的函数里去的?原子哥的例程main函数里有初始化网卡的函数:
while(lwip_comm_init())         //lwip初始化
{
        LCD_ShowString(30,110,200,20,16,"Lwip Init failed!");         //lwip初始化失败
        delay_ms(500);
        LCD_Fill(30,110,230,130,WHITE);
        delay_ms(500);
}
网线没接不可能通过这里,将一直死循环在这里,你是直接用f407跑那个例程吗?

等待时间超时而退出去导致没有执行close(sockfd);,这条语句在我上传的例程里已经修改过来了,贴出来的那些代码是测试的时候的代码。
原子哥的例程里有个问题就是:上电的时候没有接网线,过一段时间再接上去也会一直卡死在初始化网卡那里
while(lwip_comm_init())         //lwip初始化
{
        LCD_ShowString(30,110,200,20,16,"Lwip Init failed!");         //lwip初始化失败
        delay_ms(500);
        LCD_Fill(30,110,230,130,WHITE);
        delay_ms(500);
}
原因是u8 lwip_comm_init(void)函数里的以下3句:
if(ETH_Mem_Malloc())return 1;                //内存申请失败
if(lwip_comm_mem_malloc())return 1;        //内存申请失败
if(LAN8720_Init())return 2;                        //初始化LAN8720失败
初始化LAN8720失败时没有释放前面2个函数申请的内存导致不断申请内存而内存不足失败。
专注一件事情并做到极致>>> https://shop275468129.taobao.com
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-12 17:06:10 | 显示全部楼层
麦田稻草 发表于 2016-8-12 14:55
断电后上电的时候没接网线,你是怎么能跳到sntp的函数里去的?原子哥的例程main函数里有初始化网卡的函数 ...

我的想法是如果网口不通就让他走RTC的时间如果通了就让它对RTC的时间进行修改,看这样我可能需要写成两个TASK才行了
回复 支持 反对

使用道具 举报

22

主题

131

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-6-6
在线时间
49 小时
发表于 2016-8-12 17:29:36 | 显示全部楼层
a295055641 发表于 2016-8-12 17:06
我的想法是如果网口不通就让他走RTC的时间如果通了就让它对RTC的时间进行修改,看这样我可能需要写成两个 ...

现在的情况就是如果上电的时候插了网线中途插拔网线没有影响 ,如果上电的时候没有插网线 后面再插上去就没用了
回复 支持 反对

使用道具 举报

0

主题

0

帖子

0

精华

新手入门

积分
2
金钱
2
注册时间
2019-6-3
在线时间
1 小时
发表于 2017-3-10 11:38:47 | 显示全部楼层
请问楼主,源码在哪里,是这个    网络实验10 NETCONN_WEBserver实验.rar  吗?
回复 支持 反对

使用道具 举报

6

主题

44

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
348
金钱
348
注册时间
2014-8-31
在线时间
43 小时
发表于 2017-3-30 23:07:46 | 显示全部楼层
请教楼主,局域网内的设备怎么获取时间,设备没有连接外网。

是不是在局域网内的主机上建立一个sntp的服务即可?
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
27
金钱
27
注册时间
2015-1-30
在线时间
1 小时
发表于 2017-8-24 09:58:03 | 显示全部楼层
感谢楼主分享分享
回复 支持 反对

使用道具 举报

20

主题

138

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
339
金钱
339
注册时间
2012-10-11
在线时间
134 小时
发表于 2019-7-20 10:10:56 | 显示全部楼层
SNTP服务器时间同步,谢谢楼主分享!
回复 支持 反对

使用道具 举报

2

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2019-8-6
在线时间
28 小时
发表于 2020-1-14 22:38:21 | 显示全部楼层
发送时失败 if(sendto(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0, (struct sockaddr*)&server, sizeof(server))<0)   也不知道为什么 我实在103上运行的
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 13:36

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表