linux 时间校准

本文介绍了在Linux设备上进行时间同步的两种方法,包括使用ntpdate和sntp命令,以及通过C语言编写程序实现NTP协议同步。详细阐述了每个方法的优缺点,并提供了C语言代码示例,展示了如何构建和发送NTP请求报文,计算时间偏移量并更新系统时间。
摘要由CSDN通过智能技术生成

linux 设备有对时需求时,可用的操作

一、采用指令,适合脚本方式

1、ntpdate

ntpdate ntp.aliyun.com

此指令如果设备没网或者服务器没响应,超时等待相对较久;
在代码用 system 调用时,没有结果返回,无法知道成功还是失败;

2、sntp

sntp -S ntp.aliyun.com

此指令超时响应会比 ntpdate 短;
在代码用 system 调用会有结果返回,0为成功,其他值为失败;

二、c语言代码实现,适合应用方式

#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[])
{
	int ret = -1;
    char dateBuf[64] = {0};
    char cmd[128] = {0};
    tm* local;
    char buf[BUFSIZE];
    size_t len;
    int sockfd, maxfd1;
    struct sockaddr_in servaddr;
    fd_set readfds;
    struct timeval timeout, recvtv, tv;
    double offset;

	//获取域名的ip
	int i;
	char ntp_buf[3][50] = {"ntp.aliyun.com", "ntp1.aliyun.com", "ntp2.aliyun.com"};
	struct hostent *ntp_he[3];
	for (i=0; i<3; i++) {
		if ((ntp_he[i] = gethostbyname(ntp_buf[i])) != NULL) {
			printf("%d:%s, %s:%s\n", i, ntp_buf[i], ntp_he[i]->h_name, inet_ntoa(*(struct in_addr*)ntp_he[i]->h_addr_list[0]));
		}
	}

	i = 0;
	while(1) {
		i++;
		if (i >= 3)
			break;
		
		//无效域名跳过
		if (ntp_he[i] != NULL)
			continue;
		
		//连接时间服务器
		servaddr.sin_family = AF_INET;
		servaddr.sin_port = htons(NTP_PORT);
		servaddr.sin_addr = *(struct in_addr*)ntp_he[i]->h_addr_list[0];
		
		if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			printf("socket error\n");
			continue;
		}

		if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) {
			printf("connect %s error\n", ntp_buf[i]);
			close(sockfd);
			continue;
		}
		
		//发送NTP请求报文
		len = sizeof(buf);
		if (get_ntp_packet(buf, &len) != 0) {
			printf("get_ntp_packet error\n"); 
			close(sockfd);
			continue;
		}
		send(sockfd, buf, len, 0);
		
		timeout.tv_sec = TIMEOUT;
		timeout.tv_usec = 0;
		if (0 > setsockopt(sockfd,SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout))) 
		{ 
			printf("setsockopt recv timeout error:%d\n", errno); 
			close(sockfd);
			continue;
		} 
		
		if ((len = recv(sockfd, buf, BUFSIZE, 0)) < 0)
		{
			printf("recv error\n");
			close(sockfd);
			continue;
		}
		close(sockfd);

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

		gettimeofday(&tv, NULL);

		tv.tv_sec += (int) offset;
		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, "date -s \"%s\"", dateBuf);
		// printf("%s\n", cmd);
		system(cmd);

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

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值