当前位置:   article > 正文

native层c语言获取网络NTP时间_c实现ntp获取时间

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起到现在经过的秒数,例如运行下面代码:

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

得到:1539332642

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

 

(2)常用时间函数举例

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

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

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

  1. #include <time.h>
  2. #include <stdio.h>
  3. int main()
  4. {
  5. time_t timep;
  6. time(&timep);
  7. printf("%ld\n", timep);
  8. printf("北京时间:%s", ctime(&timep));
  9. printf("UTC时间:%s", asctime(gmtime(&timep)));
  10. return 0;
  11. }

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

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

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

  1. #include <time.h>
  2. #include <stdio.h>
  3. /*struct tm {
  4.   int tm_sec; /* 秒 – 取值区间为[0,59] */
  5.   int tm_min; /* 分 - 取值区间为[0,59] */
  6.   int tm_hour; /* 时 - 取值区间为[0,23] */
  7.   int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
  8.   int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
  9.   int tm_year; /* 年份,其值等于实际年份减去1900 */
  10.   int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一 */
  11.   int tm_yday; /* 从每年1月1日开始的天数– 取值区间[0,365],其中0代表1月1日 */
  12.   int tm_isdst; /* 夏令时标识符,夏令时tm_isdst为正;不实行夏令时tm_isdst为0 */
  13. };*/
  14. typedef struct RTSPUTCTime
  15. {
  16. int year;
  17. int mon;
  18. int day;
  19. int hour;
  20. int min;
  21. int second;
  22. } RTSPUTCTime;
  23. time_t utc2seconds(RTSPUTCTime *utc)
  24. {
  25. struct tm tm_time;
  26. memset(&tm_time, 0, sizeof(tm_time));
  27. tm_time.tm_year = utc->year - 1900;
  28. tm_time.tm_mon = utc->mon;
  29. tm_time.tm_mday = utc->day;
  30. tm_time.tm_hour = utc->hour;
  31. tm_time.tm_min = utc->min;
  32. tm_time.tm_sec = utc->second;
  33. return mktime(&tm_time);
  34. }
  35. 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);      // 将秒转化为当前时间
  36.   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 文件名。

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

  1. uid_t uid = getuid();
  2. if (setuid(0)) {
  3. return -1;
  4. }
  5. //...
  6. if (setuid(uid)) { //恢复uid
  7. }

通过上面步骤则该用户不管在普通用户还是在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. #define VERSION_3 3
  15. #define VERSION_4 4
  16. #define MODE_CLIENT 3
  17. #define MODE_SERVER 4
  18. #define NTP_LI 0
  19. #define NTP_VN VERSION_3
  20. #define NTP_MODE MODE_CLIENT
  21. #define NTP_STRATUM 0
  22. #define NTP_POLL 4
  23. #define NTP_PRECISION -6
  24. #define NTP_HLEN 48
  25. #define NTP_PORT 123
  26. #define NTP_SERVER "182.92.12.11"
  27. #define TIMEOUT 10
  28. #define BUFSIZE 1500
  29. #define JAN_1970 0x83aa7e80
  30. #define NTP_CONV_FRAC32(x) (uint64_t) ((x) * ((uint64_t)1<<32))
  31. #define NTP_REVE_FRAC32(x) ((double) ((double) (x) / ((uint64_t)1<<32)))
  32. #define NTP_CONV_FRAC16(x) (uint32_t) ((x) * ((uint32_t)1<<16))
  33. #define NTP_REVE_FRAC16(x) ((double)((double) (x) / ((uint32_t)1<<16)))
  34. #define USEC2FRAC(x) ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 ))
  35. #define FRAC2USEC(x) ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 ))
  36. #define NTP_LFIXED2DOUBLE(x) ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))
  37. using namespace std;
  38. struct s_fixedpt {
  39. uint16_t intpart;
  40. uint16_t fracpart;
  41. };
  42. struct l_fixedpt {
  43. uint32_t intpart;
  44. uint32_t fracpart;
  45. };
  46. struct ntphdr {
  47. #if __BYTE_ORDER == __BID_ENDIAN
  48. unsigned int ntp_li:2;
  49. unsigned int ntp_vn:3;
  50. unsigned int ntp_mode:3;
  51. #endif
  52. #if __BYTE_ORDER == __LITTLE_ENDIAN
  53. unsigned int ntp_mode:3;
  54. unsigned int ntp_vn:3;
  55. unsigned int ntp_li:2;
  56. #endif
  57. uint8_t ntp_stratum;
  58. uint8_t ntp_poll;
  59. int8_t ntp_precision;
  60. struct s_fixedpt ntp_rtdelay;
  61. struct s_fixedpt ntp_rtdispersion;
  62. uint32_t ntp_refid;
  63. struct l_fixedpt ntp_refts;
  64. struct l_fixedpt ntp_orits;
  65. struct l_fixedpt ntp_recvts;
  66. struct l_fixedpt ntp_transts;
  67. };
  68. in_addr_t inet_host(const char *host)
  69. {
  70. in_addr_t saddr;
  71. struct hostent *hostent;
  72. if ((saddr = inet_addr(host)) == INADDR_NONE) {
  73. if ((hostent = gethostbyname(host)) == NULL)
  74. return INADDR_NONE;
  75. memmove(&saddr, hostent->h_addr, hostent->h_length);
  76. }
  77. return saddr;
  78. }
  79. int get_ntp_packet(void *buf, size_t *size) //构建并发送NTP请求报文
  80. {
  81. struct ntphdr *ntp;
  82. struct timeval tv;
  83. if (!size || *size<NTP_HLEN)
  84. return -1;
  85. memset(buf, 0, *size);
  86. ntp = (struct ntphdr *) buf;
  87. ntp->ntp_li = NTP_LI;
  88. ntp->ntp_vn = NTP_VN;
  89. ntp->ntp_mode = NTP_MODE;
  90. ntp->ntp_stratum = NTP_STRATUM;
  91. ntp->ntp_poll = NTP_POLL;
  92. ntp->ntp_precision = NTP_PRECISION;
  93. gettimeofday(&tv, NULL); //把目前的时间用tv 结构体返回
  94. ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
  95. ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
  96. *size = NTP_HLEN;
  97. return 0;
  98. }
  99. double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv) //往返时延
  100. {
  101. double t1, t2, t3, t4;
  102. t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
  103. t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
  104. t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
  105. t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
  106. return (t4 - t1) - (t3 - t2);
  107. }
  108. double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv) //偏移量
  109. {
  110. double t1, t2, t3, t4;
  111. t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
  112. t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
  113. t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
  114. t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
  115. return ((t2 - t1) + (t3 - t4)) / 2;
  116. }
  117. int main(int argc, char *argv[])
  118. {
  119. char dateBuf[64] = {0};
  120. char cmd[128] = {0};
  121. tm* local;
  122. char buf[BUFSIZE];
  123. size_t nbytes;
  124. int sockfd, maxfd1;
  125. struct sockaddr_in servaddr;
  126. fd_set readfds;
  127. struct timeval timeout, recvtv, tv;
  128. double offset;
  129. // if (argc != 2) {
  130. // usage();
  131. // exit(-1);
  132. servaddr.sin_family = AF_INET;
  133. servaddr.sin_port = htons(NTP_PORT);
  134. servaddr.sin_addr.s_addr = inet_host("119.28.183.184");
  135. if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  136. perror("socket error");
  137. exit(-1);
  138. }
  139. if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) {
  140. perror("connect error");
  141. exit(-1);
  142. }
  143. nbytes = BUFSIZE;
  144. if (get_ntp_packet(buf, &nbytes) != 0) {
  145. fprintf(stderr, "construct ntp request error \n");
  146. exit(-1);
  147. }
  148. send(sockfd, buf, nbytes, 0);
  149. FD_ZERO(&readfds);
  150. FD_SET(sockfd, &readfds);
  151. maxfd1 = sockfd + 1;
  152. timeout.tv_sec = TIMEOUT;
  153. timeout.tv_usec = 0;
  154. if (select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) {
  155. if (FD_ISSET(sockfd, &readfds))
  156. {
  157. if ((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0)
  158. {
  159. perror("recv error");
  160. exit(-1);
  161. }
  162. //计算C/S时间偏移量
  163. gettimeofday(&recvtv, NULL);
  164. offset = get_offset((struct ntphdr *) buf, &recvtv);
  165. 更新系统时间
  166. gettimeofday(&tv, NULL);
  167. tv.tv_sec += (int) offset +28800;
  168. tv.tv_usec += offset - (int) offset;
  169. local = localtime((time_t *) &tv.tv_sec);
  170. strftime(dateBuf, 64, "%Y-%m-%d %H:%M:%S", local);
  171. sprintf(cmd, "system busybox date -s \"%s\"", dateBuf);
  172. printf("%s \n", ctime((time_t *) &tv.tv_sec));
  173. }
  174. }
  175. close(sockfd);
  176. return 0;
  177. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/202566
推荐阅读
相关标签
  

闽ICP备14008679号