1、系统时间的修改可能会对整个系统产生广泛的影响,因此 Linux 系统将修改系统时间的操作限制为只有具有足够权限的用户才能执行,所以我们先进入超级用户。
su root
2、由于系统中可能默认开启了 NTP 服务,它会周期性地将系统时间与网络上的时间服务器进行同步。即便你手动修改了系统时间,NTP 服务也可能会迅速将时间重新调整回其从时间服务器获取的标准时间。所以我们测试代码的时候先临时停止 NTP 服务。
systemctl stop systemd-timesyncd
3、查看系统当前时区设置
timedatectl status | grep "Time zone"
4、如果时区不是Asia/Shanghai,使用指令修改时区
timedatectl set-timezone Asia/Shanghai //在普通用户下运行该指令需在最前方加上 sudo
5、前面准备完毕后接下来就是我们的代码编写
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main()
{
// 设置时区为北京时间
setenv("TZ", "Asia/Shanghai", 1);
tzset();
char command[100];
char time_addr[100];
//将ping 淘宝IP的终端指令封装到命令中
snprintf(command, sizeof(command), "ping -c 3 acs.m.taobao.com | grep \"PING\" | awk -F '[()]' '{print $2}'");
//使用popen执行终端指令并保存IP
FILE *fp = popen(command, "r");
fgets(time_addr, sizeof(time_addr), fp);
time_addr[strcspn(time_addr, "\n")] = 0;
if (fp == NULL) {
perror("popen");
} else {
pclose(fp);
}
printf("淘宝IP地址%s\n",time_addr);
//创建连接套接字
int fd=socket(AF_INET,SOCK_STREAM,0);
if (fd==-1)
{
perror("socket");
return 0;
}
//配置ip和端口号
struct sockaddr_in addr;
addr.sin_family=AF_INET;
if (inet_aton(time_addr, &addr.sin_addr) == 0) {
perror("inet_aton");
close(fd);
return -1;
}
addr.sin_port=htons(80);
//进行连接
int net=connect(fd,(struct sockaddr*)&addr,sizeof(addr));
if (net==-1)
{
perror("connect");
return 0;
}
//构建请求报文
const char *request = "GET /gw/mtop.common.getTimestamp/ HTTP/1.1\r\n"
"Host: acs.m.taobao.com\r\n"
"User-Agent: C-Socket-Request\r\n"
"Connection: close\r\n\r\n";
write(fd, request, strlen(request));
char buff[2048];
//这里使用recv而不是使用read是为了确保接收数据的完整性,尽管如此在测试过程中还是偶现会丢包情况,请大家多试几次即可;
recv(fd,buff,sizeof(buff),0);
printf("接收到的数据%s",buff);
//使用字符串查找
const char *start = strstr(buff,"\"t\":\"");
if (start == NULL) {
printf("内容匹配失败!\n");
printf("%s\n",buff);
return -1;
}
//偏移到时间戳所在位置
start += strlen("\"t\":\"");
char timestamp_str[20];
size_t i = 0;
//将时间戳取出来
while (*start >= '0' && *start <= '9' && i < sizeof(timestamp_str) - 1) {
timestamp_str[i++] = *start++;
}
timestamp_str[i] = '\0';
//使用longlong类型增加代码的可移植性和通用性
long long stampe=strtoll(timestamp_str, NULL, 10);
//将以毫秒为单位的时间戳转换为以秒为单位的时间戳。
time_t stamp_sec = stampe / 1000;
//将以秒为单位的时间戳转换为本地时间的结构体表示形式。
struct tm *tm_info = localtime(&stamp_sec);
char time_str[26];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
printf("时间%s",time_str);
//将时间戳封装为命令
memset(command,0,sizeof(command));
snprintf(command, sizeof(command), "date -s \"%s\"", time_str);
//执行终端时间更新命令
fp = popen(command, "r");
if (fp == NULL) {
perror("popen");
} else {
pclose(fp);
}
close(fd);
}
6、对代码进行编译
gcc time.c -o time
7、运行代码如果出现:�内容匹配失败!HTTP/1.1 200 OK;说明只读取到了响应头部分,而没有获取到响应体中的 JSON 数据,请大家多试几次即可!