zoukankan      html  css  js  c++  java
  • linux C/C++实现同步NTP时间

    搬砖萌新记录工作点滴:


    一、时间类型及常用函数

    (1)时间类型

    本地时间(locale time)
    格林威治时间(Greenwich Mean Time GMT)
    世界标准时间 (Universal Time Coordinated UTC)

     

    GMT、UTC时间,都是以秒数为单位计数,而不是真实世界中的年月日,时分秒时间。

    这个时间是从1970年01月01日 0:00:00起到现在经过的秒数例如运行下面代码:

    #include <time.h>
    #include <stdio.h>
    
    int main()  
    {  
        time_t timep;  //用来存储从1970年到现在经过了多少秒
        time(&timep);  //获取time_t类型的当前时间
        printf("%ld\n", timep);  
        printf("%s", ctime(&timep)); 
    return 0; }

    得到:1539332642

    通过函数ctime()将其转换为真实世界时间:Fri Oct 12 16:30:01 2018

     

    (2)常用时间函数举例

    因为时区不同的关系,不同函数取得的时间会相差8个小时(北京处于东八区)。简单举例:

    获得UTC时间:time()、asctime()、gmtime()... ...

    获得经时区转换后的时间:ctime()、localtime()... ...

    #include <time.h>
    #include <stdio.h>
    
    int main()
    { 
        time_t timep;  
        time(&timep);  
        printf("%ld\n", timep);   
        printf("北京时间:%s", ctime(&timep));  
        printf("UTC时间:%s", asctime(gmtime(&timep)));  
        return 0;  
    }  

    更多与时间相关的内容可参考这个博客:c++ 时间类型详解(time_t和tm) 

     Linux C++中的时间函数(转)

    (3)UTC时间转换成秒,再转换成当前时间

    #include <time.h>
    #include <stdio.h>
    /*
    struct tm
    {   int tm_sec; /* 秒 – 取值区间为[0,59] */   int tm_min; /* 分 - 取值区间为[0,59] */   int tm_hour; /* 时 - 取值区间为[0,23] */   int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */   int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */   int tm_year; /* 年份,其值等于实际年份减去1900 */   int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一 */   int tm_yday; /* 从每年1月1日开始的天数– 取值区间[0,365],其中0代表1月1日 */   int tm_isdst; /* 夏令时标识符,夏令时tm_isdst为正;不实行夏令时tm_isdst为0 */ };
    */
    typedef struct RTSPUTCTime
    {
        int year;
        int mon;
        int day;
        int hour;
        int min;
        int second;
    } RTSPUTCTime;
    
    time_t utc2seconds(RTSPUTCTime *utc)
    {
        struct tm tm_time;
    
        memset(&tm_time, 0, sizeof(tm_time));
        tm_time.tm_year = utc->year - 1900;
        tm_time.tm_mon  = utc->mon;
        tm_time.tm_mday = utc->day;
        tm_time.tm_hour = utc->hour;
        tm_time.tm_min  = utc->min;
        tm_time.tm_sec  = utc->second;
    return mktime(&tm_time);
    }
    

    int main()
    {
      struct tm *tm_now;
      RTSPUTCTime utc = {2019,3,15,9,30,15};   // 给定一个UTC时间
      time_t seektime = utc2seconds(&utc);    // 将UTC时间转化为秒
      tm_now = localtime(&seektime);        // 将秒转化为当前时间

      printf("tm_now =%d-%d-%d %d:%d:%d \n",tm_now->tm_year+1900, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);
    }

     

     二、实现NTP同步功能

    了解了时间概念后,要做的就比较明确了

    (1)发送NTP请求报文,从一个NTP服务器获取到时间

    (2)更新系统时间 

    这里可以参考博客:简单的NTP客户端-C语言实现,讲解详细,提供的代码稍作修改编译就通过了,很好用。

     

    关于我修改的地方:

    (1)从一个NTP服务器获取到时间

    我选择的NTP服务器IP地址:119.28.183.184(百度可以查到国家授时中心IP等)

     

    (2)更新系统时间

    代码里的settimeofday(&tv, NULL)函数,是需要root权限的。怎么在普通用户下实现NTP同步呢,

    ①命令加程序:

    先登录root用户设置程序的UID,#chmod u+s 文件名。

    然后在更新系统时间部分添加如下代码

    uid_t uid = getuid();
    if (setuid(0)) {
        return -1;
    }
    //...
    if (setuid(uid)) {   //恢复uid
    }

    通过上面步骤则该用户不管在普通用户还是在root用户下都能获取root权限。

     

    ②不使用命令的情况,完整代码如下

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <time.h>
      5 #include<iostream>
      6 #include <unistd.h>
      7 #include <sys/select.h>
      8  #include<sys/time.h> 
      9 #include <sys/socket.h>
     10 #include <arpa/inet.h>
     11 #include <netdb.h>
     12 #include <errno.h>
     13 #include <endian.h>
     14 
     15 #define VERSION_3           3
     16 #define VERSION_4           4
     17 
     18 #define MODE_CLIENT         3
     19 #define MODE_SERVER         4
     20  
     21 #define NTP_LI              0
     22 #define NTP_VN              VERSION_3   
     23 #define NTP_MODE            MODE_CLIENT
     24 #define NTP_STRATUM         0
     25 #define NTP_POLL            4
     26 #define NTP_PRECISION       -6
     27 
     28 #define NTP_HLEN            48
     29 
     30 #define NTP_PORT            123
     31 #define NTP_SERVER          "182.92.12.11"
     32 
     33 #define TIMEOUT             10
     34 
     35 #define BUFSIZE             1500
     36 
     37 #define JAN_1970            0x83aa7e80
     38 
     39 #define NTP_CONV_FRAC32(x)  (uint64_t) ((x) * ((uint64_t)1<<32))    
     40 #define NTP_REVE_FRAC32(x)  ((double) ((double) (x) / ((uint64_t)1<<32)))   
     41 
     42 #define NTP_CONV_FRAC16(x)  (uint32_t) ((x) * ((uint32_t)1<<16))    
     43 #define NTP_REVE_FRAC16(x)  ((double)((double) (x) / ((uint32_t)1<<16)))    
     44 
     45 
     46 #define USEC2FRAC(x)        ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 )) 
     47 #define FRAC2USEC(x)        ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 )) 
     48 
     49 
     50 #define NTP_LFIXED2DOUBLE(x)    ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))   
     51 
     52 using namespace std;
     53 struct s_fixedpt {
     54     uint16_t    intpart;
     55     uint16_t    fracpart;
     56 };
     57 
     58 struct l_fixedpt {
     59     uint32_t    intpart;
     60     uint32_t    fracpart;
     61 };
     62 
     63 
     64 struct ntphdr {
     65 #if __BYTE_ORDER == __BID_ENDIAN
     66     unsigned int    ntp_li:2;
     67     unsigned int    ntp_vn:3;
     68     unsigned int    ntp_mode:3;
     69 #endif
     70 #if __BYTE_ORDER == __LITTLE_ENDIAN
     71     unsigned int    ntp_mode:3;
     72     unsigned int    ntp_vn:3;
     73     unsigned int    ntp_li:2;
     74 #endif
     75     uint8_t         ntp_stratum;
     76     uint8_t         ntp_poll;
     77     int8_t          ntp_precision;
     78     struct s_fixedpt    ntp_rtdelay;
     79     struct s_fixedpt    ntp_rtdispersion;
     80     uint32_t            ntp_refid;
     81     struct l_fixedpt    ntp_refts;
     82     struct l_fixedpt    ntp_orits;
     83     struct l_fixedpt    ntp_recvts;
     84     struct l_fixedpt    ntp_transts;
     85 };
     86 
     87 
     88 in_addr_t inet_host(const char *host)
     89 {
     90     in_addr_t saddr;
     91     struct hostent *hostent;
     92 
     93     if ((saddr = inet_addr(host)) == INADDR_NONE) {
     94         if ((hostent = gethostbyname(host)) == NULL)
     95             return INADDR_NONE;
     96 
     97         memmove(&saddr, hostent->h_addr, hostent->h_length);
     98     }
     99 
    100     return saddr;
    101 }
    102 
    103 
    104 int get_ntp_packet(void *buf, size_t *size)  //构建并发送NTP请求报文
    105 {
    106     struct ntphdr *ntp;
    107     struct timeval tv;
    108 
    109 
    110     if (!size || *size<NTP_HLEN)
    111         return -1;
    112 
    113     memset(buf, 0, *size);
    114 
    115     ntp = (struct ntphdr *) buf;
    116     ntp->ntp_li = NTP_LI;
    117     ntp->ntp_vn = NTP_VN;
    118     ntp->ntp_mode = NTP_MODE;
    119     ntp->ntp_stratum = NTP_STRATUM;
    120     ntp->ntp_poll = NTP_POLL;
    121     ntp->ntp_precision = NTP_PRECISION;
    122 
    123     gettimeofday(&tv, NULL);  //把目前的时间用tv 结构体返回
    124     ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
    125     ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
    126 
    127     *size = NTP_HLEN;
    128 
    129     return 0;
    130 }
    131 
    132 
    133 
    134 double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv)  //往返时延
    135 {
    136     double t1, t2, t3, t4;
    137 
    138     t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
    139     t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
    140     t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
    141     t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
    142 
    143     return (t4 - t1) - (t3 - t2);
    144 }
    145 
    146 
    147 double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv)  //偏移量
    148 {
    149     double t1, t2, t3, t4;
    150 
    151     t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
    152     t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
    153     t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
    154     t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
    155 
    156     return ((t2 - t1) + (t3 - t4)) / 2;
    157 }
    158 
    159 
    160 
    161 
    162 int main(int argc, char *argv[])
    163 {    
    164     char dateBuf[64] = {0};
    165     char cmd[128] = {0};
    166     tm* local; 
    167     char buf[BUFSIZE];
    168     size_t nbytes;
    169     int sockfd, maxfd1;
    170     struct sockaddr_in servaddr;
    171     fd_set readfds;
    172     struct timeval timeout, recvtv, tv;
    173     double offset;
    174 
    175     // if (argc != 2) {
    176         // usage();
    177         // exit(-1);
    178    
    179 
    180     servaddr.sin_family = AF_INET;
    181     servaddr.sin_port = htons(NTP_PORT);
    182     servaddr.sin_addr.s_addr = inet_host("119.28.183.184");
    183     
    184     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    185         perror("socket error");
    186         exit(-1);
    187     }
    188 
    189     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) {  
    190         perror("connect error");
    191         exit(-1);
    192     }
    193     nbytes = BUFSIZE;
    194     if (get_ntp_packet(buf, &nbytes) != 0) {
    195         fprintf(stderr, "construct ntp request error \n");
    196         exit(-1);
    197     }
    198     send(sockfd, buf, nbytes, 0);
    199 
    200 
    201     FD_ZERO(&readfds);
    202     FD_SET(sockfd, &readfds);
    203     maxfd1 = sockfd + 1;
    204 
    205 
    206     timeout.tv_sec = TIMEOUT;
    207     timeout.tv_usec = 0;
    208     
    209     if (select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) {
    210         if (FD_ISSET(sockfd, &readfds)) 
    211         {
    212             if ((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0)  
    213             {
    214                 perror("recv error");
    215                 exit(-1);
    216             }
    217             
    218             //计算C/S时间偏移量
    219             gettimeofday(&recvtv, NULL);
    220             offset = get_offset((struct ntphdr *) buf, &recvtv);    
    221             ////更新系统时间
    222 
    223             gettimeofday(&tv, NULL);
    224             
    225             tv.tv_sec += (int) offset +28800;
    226             tv.tv_usec += offset - (int) offset;
    227             
    228             local = localtime((time_t *) &tv.tv_sec); 
    229             strftime(dateBuf, 64, "%Y-%m-%d %H:%M:%S", local);
    230             sprintf(cmd, "system busybox date -s \"%s\"", dateBuf);
    231             
    232             printf("%s \n", ctime((time_t *) &tv.tv_sec));
    233             
    234 
    235         }
    236     }
    237 
    238     close(sockfd);
    239 
    240     return 0;
    241 }

     

  • 相关阅读:
    STL
    Python
    Swift学习笔记
    Swift学习笔记
    Cocos2d-x -- 如何让背景从上到下滚动
    Cocos2d-x -- 图片菜单按钮
    How to change in the Cocos2d-x project from landscape to portrait both in iOS and Android
    系统集成项目管理工程师和信息系统管理工程师的区别是什么?
    公积金取出来后悔了 公积金取出来好还是不取好?
    青岛公积金贷款额度最高多少?怎么算?
  • 原文地址:https://www.cnblogs.com/zy791976083/p/9779421.html
Copyright © 2011-2022 走看看