0


Linux时间校准(ntpdate及NTP客户端代码校准示例)

背景

机器每次机启后时间就会出现异常,因为机器无法访问外网,只能访问局域网的ntp服务,所以需要保证局域网内部有ntp服务,如何安装ntp服务,参考Ubuntu20.04 Ntp服务安装及验证。

网络时间协议Network Time Protocol(NTP) 是一种确保时钟保持准确的方法。如果可以访问互联网,只需安装ntp的客户端软件到互联网上的公共ntp服务器自动修正时间即可

一、系统时间和硬件时间

Linux在默认情况下,系统时间和硬件时间并不会自动同步。而是以异步的方式运行,互不干扰。其中硬件时间的运行,是靠Bios电池来维持,而系统时间,是用CPU 时钟来维持的。

在系统开机的时候,会自动从Bios中取得硬件时间,设置为系统时间。

1.1 date命令

用来查看和设置系统时间

date#查看系统当前时间sudodate -s "2023-03-18 11:16:10"#修改系统时间为 "xxxx-xx-xx xx:xx:xx"===============================================================================
nvidia@nvidia-desktop:~$ date
Вт мар 1811:16:27 +08 2023
nvidia@nvidia-desktop:~$
nvidia@nvidia-desktop:~$
nvidia@nvidia-desktop:~$ sudodate -s "2023-03-18 11:16:10"[sudo] password for nvidia:
Вт мар 1811:16:10 +08 2023
nvidia@nvidia-desktop:~$

硬件时间的设置,可以用hwclock

1.2 hwclock 命令

查看当前硬件时间

注意:hwclock 所有命令需要使用root 权限

nvidia@nvidia-desktop:~$ hwclock
hwclock: Cannot access the Hardware Clock via any known method.
hwclock: Use the --debug option to see the details of our search for an access method.
nvidia@nvidia-desktop:~$
nvidia@nvidia-desktop:~$
nvidia@nvidia-desktop:~$ sudo hwclock
2023-03-21 11:18:49.607690+0800
nvidia@nvidia-desktop:~$

将系统时间同步到硬件时间

hwclock -w

将硬件时间同步到系统时间

hwclock -s

二、不同机器间时间同步

为了避免主机时间因为长期运作下所导致的时间偏差,进行时间同步(synchronize)的工作是非常必要的。Linux系统下,一般使用ntp服务器来同步不同机器的时间。一台机器,可以同时是ntp服务器和ntp客户机。

2.1 ntpdate命令实现

ntpdate 安装:

yum install ntpdate -y   # Centos系统======================================sudoaptinstall ntpdate  # Ubuntu系统

时间同步

sudo ntpdate -u cn.pool.ntp.org
18 Mar 18:25:22 ntpdate[18673]: adjust time server 84.16.73.33 offset 0.015941 sec

使用ntpdate 只是强制将系统时间设置为ntp服务器时间,如果cpu tick有问题,时间还是会不准。所以,一般配合cron命令,来进行定期同步设置。比如,在crontab中添加:

sudocrontab -e

012 * * * * /usr/sbin/ntpdate 192.168.10.110

上述命令的意思是:每天的12点整,从192.168.10.110 ntp服务器同步一次时间(前提是 192.168.10.110有ntp服务)。

2.2 Ntp客户端代码实现

本质上还是创建socket连接去获取ntp服务的时间与本地时间比较,不一致修改本机时间即可。

NtpClient.h

//// Created by lwang on 2023-03-18.//#ifndefNTP_CLIENT_H#defineNTP_CLIENT_H#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>#include<map>#include<string>#include<mutex>

using namespace std;#defineNTP_LI0#defineNTP_VERSION_NUM3#defineNTP_MODE_CLIENT3#defineNTP_MODE_SERVER4#defineNTP_STRATUM0#defineNTP_POLL4#defineNTP_PRECISION-6#defineNTP_MIN_LEN48#defineNTP_SERVER_PORT123#defineNTP_SERVER_ADDR"119.28.183.184"#defineTIMEOUT2#defineBUFSIZE1500#defineJAN_19700x83aa7e80#defineNTP_CONV_FRAC32(x)(uint64_t)((x)*((uint64_t)1<<32))#defineNTP_REVE_FRAC32(x)((double)((double)(x)/((uint64_t)1<<32)))#defineNTP_CONV_FRAC16(x)(uint32_t)((x)*((uint32_t)1<<16))#defineNTP_REVE_FRAC16(x)((double)((double)(x)/((uint32_t)1<<16)))#defineUSEC2FRAC(x)((uint32_t)NTP_CONV_FRAC32((x)/1000000.0))#defineFRAC2USEC(x)((uint32_t)NTP_REVE_FRAC32((x)*1000000.0))#defineNTP_LFIXED2DOUBLE(x)((double)(ntohl(((structl_fixedpt*)(x))->intpart)- JAN_1970 +FRAC2USEC(ntohl(((structl_fixedpt*)(x))->fracpart))/1000000.0))structs_fixedpt{uint16_t intpart;uint16_t fracpart;};structl_fixedpt{uint32_t intpart;uint32_t fracpart;};structntphdr{#if__BYTE_ORDER == __BID_ENDIANunsignedint ntp_li :2;unsignedint ntp_vn :3;unsignedint ntp_mode :3;#endif#if__BYTE_ORDER == __LITTLE_ENDIANunsignedint ntp_mode :3;unsignedint ntp_vn :3;unsignedint ntp_li :2;#endifuint8_t ntp_stratum;uint8_t ntp_poll;int8_t ntp_precision;structs_fixedpt ntp_rtdelay;structs_fixedpt ntp_rtdispersion;uint32_t ntp_refid;structl_fixedpt ntp_refts;structl_fixedpt ntp_orits;structl_fixedpt ntp_recvts;structl_fixedpt ntp_transts;};

class NtpClient {
public:NtpClient();
    virtual ~NtpClient();voidGetNtpTime(std::string &ntpTime);in_addr_tHostTransfer(constchar*host);intPaddingNtpPackage(void*buf,size_t*size);doubleGetOffset(conststructntphdr*ntp,conststructtimeval*recvtv);
private:int m_sockfd;};#endif/* NTP_CLIENT_H */

NtpClient.cpp

//// Created by lwang on 2023-03-18.//#include"NtpClient.h"NtpClient::NtpClient(){}NtpClient::~NtpClient(){}

in_addr_t NtpClient::HostTransfer(constchar*host){
    in_addr_t saddr;structhostent*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;}intNtpClient::PaddingNtpPackage(void*buf, size_t *size)// 构建并发送NTP请求报文{if(!size)return-1;structntphdr*ntp;structtimeval tv;memset(buf,0, BUFSIZE);

    ntp =(structntphdr*)buf;
    ntp->ntp_li = NTP_LI;
    ntp->ntp_vn = NTP_VERSION_NUM;
    ntp->ntp_mode = NTP_MODE_CLIENT;
    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_MIN_LEN;return0;}doubleNtpClient::GetOffset(conststructntphdr*ntp,conststructtimeval*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;}voidNtpClient::GetNtpTime(std::string &ntpTime){char buffer[64]={0};char cmd[128]={0};
    tm *local;char buf[BUFSIZE];
    size_t nbytes;int maxfd1;structsockaddr_in servaddr;
    fd_set readfds;structtimeval timeout, recvtv, tv;double offset;

    servaddr.sin_family = AF_INET;
    servaddr.sin_port =htons(NTP_SERVER_PORT);
    servaddr.sin_addr.s_addr =HostTransfer(NTP_SERVER_ADDR);if((m_sockfd =socket(AF_INET, SOCK_DGRAM,0))<0){perror("socket error");return;}if(connect(m_sockfd,(structsockaddr*)&servaddr,sizeof(structsockaddr))!=0){perror("connect error");return;}

    nbytes = BUFSIZE;if(PaddingNtpPackage(buf,&nbytes)!=0){fprintf(stderr,"construct ntp request error \n");exit(-1);}send(m_sockfd, buf, nbytes,0);FD_ZERO(&readfds);FD_SET(m_sockfd,&readfds);
    maxfd1 = m_sockfd +1;

    timeout.tv_sec = TIMEOUT;
    timeout.tv_usec =0;if(select(maxfd1,&readfds,NULL,NULL,&timeout)>0){if(FD_ISSET(m_sockfd,&readfds)){if((nbytes =recv(m_sockfd, buf, BUFSIZE,0))<0){perror("recv error");exit(-1);}// 计算C/S时间偏移量gettimeofday(&recvtv,NULL);
            offset =GetOffset((structntphdr*)buf,&recvtv);gettimeofday(&tv,NULL);
            tv.tv_sec +=(int)offset;
            tv.tv_usec += offset -(int)offset;
            local =localtime((time_t *)&tv.tv_sec);strftime(buffer,64,"%Y-%m-%d %H:%M:%S", local);
            ntpTime = std::string(buffer);}}return;}

main.cpp

#include"NtpClient.h"intmain(){
    std::string ntpTime ="";char curBuf[64]={0};structtimeval cur;
    tm *local;

    NtpClient client;

    client.GetNtpTime(ntpTime);

    cout <<"ntpTime: "<< ntpTime << endl;gettimeofday(&cur,NULL);

    local =localtime((time_t *)&cur.tv_sec);strftime(curBuf,64,"%Y-%m-%d %H:%M:%S", local);
    std::string curTime = std::string(curBuf);
    cout <<"curTime: "<< curTime << endl;if(curTime != ntpTime){
        cout <<"start time calibrate!"<< endl;
        std::string cmd ="sudo date -s \""+ ntpTime +"\"";system(cmd.c_str());
        cout <<"cmd: "<< cmd << endl;}else{
        cout <<"time seem"<< endl;}return0;}

推荐一个零声学院免费教程,个人觉得老师讲得不错, 分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis, fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker, TCP/IP,协程,DPDK等技术内容,点击立即学习:

标签: linux 运维 服务器

本文转载自: https://blog.csdn.net/weixin_46935110/article/details/129683157
版权归原作者 基层搬砖的Panda 所有, 如有侵权,请联系我们删除。

“Linux时间校准(ntpdate及NTP客户端代码校准示例)”的评论:

还没有评论