搬磚萌新記錄工作點滴:
一、時間類型及常用函數
(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; }
更多與時間相關的內容可參考這個博客:c++ 時間類型詳解(time_t和tm)
(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權限。
②不使用命令的情況,完整代碼如下:
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
15 #define VERSION_3 3
16 #define VERSION_4 4
17
18 #define MODE_CLIENT 3
19 #define MODE_SERVER 4
20
21 #define NTP_LI 0
22 #define NTP_VN VERSION_3
23 #define NTP_MODE MODE_CLIENT
24 #define NTP_STRATUM 0
25 #define NTP_POLL 4
26 #define NTP_PRECISION -6
27
28 #define NTP_HLEN 48
29
30 #define NTP_PORT 123
31 #define NTP_SERVER "182.92.12.11"
32
33 #define TIMEOUT 10
34
35 #define BUFSIZE 1500
36
37 #define JAN_1970 0x83aa7e80
38
39 #define NTP_CONV_FRAC32(x) (uint64_t) ((x) * ((uint64_t)1<<32))
40 #define NTP_REVE_FRAC32(x) ((double) ((double) (x) / ((uint64_t)1<<32)))
41
42 #define NTP_CONV_FRAC16(x) (uint32_t) ((x) * ((uint32_t)1<<16))
43 #define NTP_REVE_FRAC16(x) ((double)((double) (x) / ((uint32_t)1<<16)))
44
45
46 #define USEC2FRAC(x) ((uint32_t) NTP_CONV_FRAC32( (x) / 1000000.0 ))
47 #define FRAC2USEC(x) ((uint32_t) NTP_REVE_FRAC32( (x) * 1000000.0 ))
48
49
50 #define NTP_LFIXED2DOUBLE(x) ((double) ( ntohl(((struct l_fixedpt *) (x))->intpart) - JAN_1970 + FRAC2USEC(ntohl(((struct l_fixedpt *) (x))->fracpart)) / 1000000.0 ))
51
52 using namespace std; 53 struct s_fixedpt { 54 uint16_t intpart; 55 uint16_t fracpart; 56 }; 57
58 struct l_fixedpt { 59 uint32_t intpart; 60 uint32_t fracpart; 61 }; 62
63
64 struct ntphdr { 65 #if __BYTE_ORDER == __BID_ENDIAN
66 unsigned int ntp_li:2; 67 unsigned int ntp_vn:3; 68 unsigned int ntp_mode:3; 69 #endif
70 #if __BYTE_ORDER == __LITTLE_ENDIAN
71 unsigned int ntp_mode:3; 72 unsigned int ntp_vn:3; 73 unsigned int ntp_li:2; 74 #endif
75 uint8_t ntp_stratum; 76 uint8_t ntp_poll; 77 int8_t ntp_precision; 78 struct s_fixedpt ntp_rtdelay; 79 struct s_fixedpt ntp_rtdispersion; 80 uint32_t ntp_refid; 81 struct l_fixedpt ntp_refts; 82 struct l_fixedpt ntp_orits; 83 struct l_fixedpt ntp_recvts; 84 struct l_fixedpt ntp_transts; 85 }; 86
87
88 in_addr_t inet_host(const char *host) 89 { 90 in_addr_t saddr; 91 struct hostent *hostent; 92
93 if ((saddr = inet_addr(host)) == INADDR_NONE) { 94 if ((hostent = gethostbyname(host)) == NULL) 95 return INADDR_NONE; 96
97 memmove(&saddr, hostent->h_addr, hostent->h_length); 98 } 99
100 return saddr; 101 } 102
103
104 int get_ntp_packet(void *buf, size_t *size) //構建並發送NTP請求報文
105 { 106 struct ntphdr *ntp; 107 struct timeval tv; 108
109
110 if (!size || *size<NTP_HLEN) 111 return -1; 112
113 memset(buf, 0, *size); 114
115 ntp = (struct ntphdr *) buf; 116 ntp->ntp_li = NTP_LI; 117 ntp->ntp_vn = NTP_VN; 118 ntp->ntp_mode = NTP_MODE; 119 ntp->ntp_stratum = NTP_STRATUM; 120 ntp->ntp_poll = NTP_POLL; 121 ntp->ntp_precision = NTP_PRECISION; 122
123 gettimeofday(&tv, NULL); //把目前的時間用tv 結構體返回
124 ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970); 125 ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec)); 126
127 *size = NTP_HLEN; 128
129 return 0; 130 } 131
132
133
134 double get_rrt(const struct ntphdr *ntp, const struct timeval *recvtv) //往返時延
135 { 136 double t1, t2, t3, t4; 137
138 t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits); 139 t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts); 140 t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts); 141 t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0; 142
143 return (t4 - t1) - (t3 - t2); 144 } 145
146
147 double get_offset(const struct ntphdr *ntp, const struct timeval *recvtv) //偏移量
148 { 149 double t1, t2, t3, t4; 150
151 t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits); 152 t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts); 153 t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts); 154 t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0; 155
156 return ((t2 - t1) + (t3 - t4)) / 2; 157 } 158
159
160
161
162 int main(int argc, char *argv[]) 163 { 164 char dateBuf[64] = {0}; 165 char cmd[128] = {0}; 166 tm* local; 167 char buf[BUFSIZE]; 168 size_t nbytes; 169 int sockfd, maxfd1; 170 struct sockaddr_in servaddr; 171 fd_set readfds; 172 struct timeval timeout, recvtv, tv; 173 double offset; 174
175 // if (argc != 2) { 176 // usage(); 177 // exit(-1); 178
179
180 servaddr.sin_family = AF_INET; 181 servaddr.sin_port = htons(NTP_PORT); 182 servaddr.sin_addr.s_addr = inet_host("119.28.183.184"); 183
184 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 185 perror("socket error"); 186 exit(-1); 187 } 188
189 if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) != 0) { 190 perror("connect error"); 191 exit(-1); 192 } 193 nbytes = BUFSIZE; 194 if (get_ntp_packet(buf, &nbytes) != 0) { 195 fprintf(stderr, "construct ntp request error \n"); 196 exit(-1); 197 } 198 send(sockfd, buf, nbytes, 0); 199
200
201 FD_ZERO(&readfds); 202 FD_SET(sockfd, &readfds); 203 maxfd1 = sockfd + 1; 204
205
206 timeout.tv_sec = TIMEOUT; 207 timeout.tv_usec = 0; 208
209 if (select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) { 210 if (FD_ISSET(sockfd, &readfds)) 211 { 212 if ((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0) 213 { 214 perror("recv error"); 215 exit(-1); 216 } 217
218 //計算C/S時間偏移量
219 gettimeofday(&recvtv, NULL); 220 offset = get_offset((struct ntphdr *) buf, &recvtv); 221 ////更新系統時間
222
223 gettimeofday(&tv, NULL); 224
225 tv.tv_sec += (int) offset +28800; 226 tv.tv_usec += offset - (int) offset; 227
228 local = localtime((time_t *) &tv.tv_sec); 229 strftime(dateBuf, 64, "%Y-%m-%d %H:%M:%S", local); 230 sprintf(cmd, "system busybox date -s \"%s\"", dateBuf); 231
232 printf("%s \n", ctime((time_t *) &tv.tv_sec)); 233
234
235 } 236 } 237
238 close(sockfd); 239
240 return 0; 241 }