NTP对时服务,C语言实现

本文详细描述了一个C语言程序,它使用NTP协议从服务器获取时间,计算与本地时间的偏移,并调整系统时间。主要涉及构建NTP请求、接收响应和时间处理函数。
摘要由CSDN通过智能技术生成
   #include <stdio.h>
   #include <stdlib.h>
   #include <string.h>
   #include <time.h>
   #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;
 }

 char ntpTimeStr[32];

 void getSystemTime() {
    time_t tt = time(0);
    //产生“YYYY-MM-DD hh:mm:ss”格式的字符串。
    strftime(ntpTimeStr, sizeof(ntpTimeStr), "%Y-%m-%d %H:%M:%S", localtime(&tt));
    ntpTimeStr[31] = '\n';
     printf("%s\n", ntpTimeStr);
}
 
 
 
 
 int main(int argc, char *argv[])
 {    
     char dateBuf[64] = {0};
     char cmd[128] = {0};
     struct 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);

             if (settimeofday(&tv, NULL) != 0) {
                perror("set time of day error");
             }
             printf("%s \n", ctime((time_t *) &tv.tv_sec));
             printf("dateBuf:%s\n", dateBuf);

             //getSystemTime();
             

         }
     }
 
     close(sockfd);
 
     return 0;
 }

这段代码是一个C语言程序,用于获取NTP(网络时间协议)服务器的时间,并计算与本地时间的偏移量。以下是代码的主要功能:

1. 定义了一些常量和宏,如NTP版本、模式等。
2. 实现了`inet_host`函数,用于将主机名转换为IP地址。
3. 实现了`get_ntp_packet`函数,用于构建并发送NTP请求报文。
4. 实现了`get_rrt`函数,用于计算往返时延。
5. 实现了`get_offset`函数,用于计算偏移量。
6. 在`main`函数中,首先连接到NTP服务器,然后发送NTP请求报文。接着使用`select`函数等待接收服务器的响应。如果收到响应,就计算C/S时间偏移量,并更新系统时间。最后关闭套接字并返回0。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
NTP(Network Time Protocol)是一种用于同步计算机时间的协议。在 Linux 中,可以使用 NTP 客户端来同步本地计算机的时钟。以下是一个简单的 C 语言程序,可以使用 NTP 客户端来获取网络时间并将其设置为系统时间: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/udp.h> #include <time.h> #define NTP_PORT 123 #define NTP_PACKET_SIZE 48 #define NTP_TIMESTAMP_DELTA 2208988800ull typedef struct ntp_packet { uint8_t li_vn_mode; uint8_t stratum; uint8_t poll; uint8_t precision; uint32_t root_delay; uint32_t root_dispersion; uint32_t ref_id; uint64_t ref_timestamp; uint64_t orig_timestamp; uint64_t recv_timestamp; uint64_t transmit_timestamp; } ntp_packet; void error(char *msg) { perror(msg); exit(1); } uint64_t get_ntp_time() { int sockfd, n; struct sockaddr_in serv_addr; ntp_packet packet; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("pool.ntp.org"); serv_addr.sin_port = htons(NTP_PORT); sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sockfd < 0) error("ERROR opening socket"); memset(&packet, 0, sizeof(packet)); packet.li_vn_mode = (0x3 << 6) | (0x3 << 3) | 0x3; if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) error("ERROR in sendto"); memset(&packet, 0, sizeof(packet)); if ((n = recv(sockfd, &packet, sizeof(packet), 0)) < 0) error("ERROR in recv"); close(sockfd); uint64_t timestamp = ntohl(packet.transmit_timestamp >> 32); timestamp = (timestamp << 32) | ntohl(packet.transmit_timestamp & 0xFFFFFFFF); timestamp -= NTP_TIMESTAMP_DELTA; return timestamp; } int main() { time_t now; struct tm *tm; uint64_t ntp_time; time(&now); tm = localtime(&now); printf("Local time: %s", asctime(tm)); ntp_time = get_ntp_time(); tm = gmtime((time_t *)&ntp_time); printf("NTP time: %s", asctime(tm)); if (settimeofday((struct timeval *)&ntp_time, NULL) < 0) error("ERROR in settimeofday"); time(&now); tm = localtime(&now); printf("Local time after setting NTP time: %s", asctime(tm)); return 0; } ``` 在上述程序中,我们使用 `get_ntp_time()` 函数获取当前网络时间。该函数发送一个 NTP 数据包到 NTP 服务器,并等待服务器响应。在收到响应后,函数从响应中提取时间戳,并将其转换为本地时间戳。 然后,我们使用 `settimeofday()` 函数将获取的网络时间设置为系统时间。最后,我们打印出本地时间和设置后的时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会C语言的男孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值