native层c语言获取网络NTP时间

https://www.cnblogs.com/zy791976083/p/9779421.html

 

一、时间类型及常用函数

(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;  
}

更多与时间相关的内容可参考这个博客:https://blog.csdn.net/ybhjx/article/details/69374354 

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权限。

 

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include<iostream>
#include <unistd.h>
#include <sys/select.h>
 #include<sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <endian.h>

#define VERSION_3           3
#define VERSION_4           4

#define MODE_CLIENT         3
#define MODE_SERVER         4

#define NTP_LI              0
#define NTP_VN              VERSION_3
#define NTP_MODE            MODE_CLIENT
#define NTP_STRATUM         0
#define NTP_POLL            4
#define NTP_PRECISION       -6

#define NTP_HLEN            48

#define NTP_PORT            123
#define NTP_SERVER          "182.92.12.11"

#define TIMEOUT             10

#define BUFSIZE             1500

#define JAN_1970            0x83aa7e80

#define NTP_CONV_FRAC32(x)  (uint64_t) ((x) * ((uint64_t)1<<32))
#define NTP_REVE_FRAC32(x)  ((double) ((double) (x) / ((uint64_t)1<<32)))

#define NTP_CONV_FRAC16(x)  (uint32_t) ((x) * ((uint32_t)1<<16))
#define NTP_REVE_FRAC16(x)  ((double)((double) (x) / ((uint32_t)1<<16)))


#define USEC2FRAC(x)        ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 ))
#define FRAC2USEC(x)        ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 ))


#define NTP_LFIXED2DOUBLE(x)    ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))

using namespace std;
struct s_fixedpt {
    uint16_t    intpart;
    uint16_t    fracpart;
};

struct l_fixedpt {
    uint32_t    intpart;
    uint32_t    fracpart;
};


struct ntphdr {
#if __BYTE_ORDER == __BID_ENDIAN
    unsigned int    ntp_li:2;
    unsigned int    ntp_vn:3;
    unsigned int    ntp_mode:3;
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int    ntp_mode:3;
    unsigned int    ntp_vn:3;
    unsigned int    ntp_li:2;
#endif
    uint8_t         ntp_stratum;
    uint8_t         ntp_poll;
    int8_t          ntp_precision;
    struct s_fixedpt    ntp_rtdelay;
    struct s_fixedpt    ntp_rtdispersion;
    uint32_t            ntp_refid;
    struct l_fixedpt    ntp_refts;
    struct l_fixedpt    ntp_orits;
    struct l_fixedpt    ntp_recvts;
    struct l_fixedpt    ntp_transts;
};


in_addr_t inet_host(const char *host)
{
    in_addr_t saddr;
    struct hostent *hostent;

    if ((saddr = inet_addr(host)) == INADDR_NONE) {
        if ((hostent = gethostbyname(host)) == NULL)
            return INADDR_NONE;

        memmove(&saddr, hostent->h_addr, hostent->h_length);
    }

    return saddr;
}


int get_ntp_packet(void *buf, size_t *size)  //构建并发送NTP请求报文
{
    struct ntphdr *ntp;
    struct timeval tv;


    if (!size || *size<NTP_HLEN)
        return -1;

    memset(buf, 0, *size);

    ntp = (struct ntphdr *) buf;
    ntp->ntp_li = NTP_LI;
    ntp->ntp_vn = NTP_VN;
    ntp->ntp_mode = NTP_MODE;
    ntp->ntp_stratum = NTP_STRATUM;
    ntp->ntp_poll = NTP_POLL;
    ntp->ntp_precision = NTP_PRECISION;

    gettimeofday(&tv, NULL);  //把目前的时间用tv 结构体返回
    ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
    ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));

    *size = NTP_HLEN;

    return 0;
}



double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv)  //往返时延
{
    double t1, t2, t3, t4;

    t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
    t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
    t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
    t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;

    return (t4 - t1) - (t3 - t2);
}


double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv)  //偏移量
{
    double t1, t2, t3, t4;

    t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
    t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
    t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
    t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;

    return ((t2 - t1) + (t3 - t4)) / 2;
}




int main(int argc, char *argv[])
{
    char dateBuf[64] = {0};
    char cmd[128] = {0};
    tm* local;
    char buf[BUFSIZE];
    size_t nbytes;
    int sockfd, maxfd1;
    struct sockaddr_in servaddr;
    fd_set readfds;
    struct timeval timeout, recvtv, tv;
    double offset;

    // if (argc != 2) {
        // usage();
        // exit(-1);


    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(NTP_PORT);
    servaddr.sin_addr.s_addr = inet_host("119.28.183.184");

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket error");
        exit(-1);
    }

    if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) {
        perror("connect error");
        exit(-1);
    }
    nbytes = BUFSIZE;
    if (get_ntp_packet(buf, &nbytes) != 0) {
        fprintf(stderr, "construct ntp request error \n");
        exit(-1);
    }
    send(sockfd, buf, nbytes, 0);


    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);
    maxfd1 = sockfd + 1;


    timeout.tv_sec = TIMEOUT;
    timeout.tv_usec = 0;

    if (select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) {
        if (FD_ISSET(sockfd, &readfds))
        {
            if ((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0)
            {
                perror("recv error");
                exit(-1);
            }

            //计算C/S时间偏移量
            gettimeofday(&recvtv, NULL);
            offset = get_offset((struct ntphdr *) buf, &recvtv);
            更新系统时间

            gettimeofday(&tv, NULL);

            tv.tv_sec += (int) offset +28800;
            tv.tv_usec += offset - (int) offset;

            local = localtime((time_t *) &tv.tv_sec);
            strftime(dateBuf, 64, "%Y-%m-%d %H:%M:%S", local);
            sprintf(cmd, "system busybox date -s \"%s\"", dateBuf);

            printf("%s \n", ctime((time_t *) &tv.tv_sec));


        }
    }

    close(sockfd);

    return 0;
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值