#define _GNU_SOURCE #include #include #include #include #include #include "ntpclient.h" #include "thread.h" #include "sys/time.h" #define NTP_PORT 123 #define NTP_PACKET_SIZE 48 // 发送NTP请求 static int sendNTPRequest(NTPClient* client) { client->ntp_packet[0] = 0x1B; // LI = 0, VN = 3, Mode = 3 (client) ssize_t sentBytes = sendto(client->sockfd, client->ntp_packet, NTP_PACKET_SIZE, 0, &client->server_addr, sizeof(client->server_addr)); if (sentBytes < 0) { perror("sendto failed"); return -1; } return 0; } // 接收NTP响应 static int receiveNTPResponse(NTPClient* client) { socklen_t len = sizeof(client->server_addr); ssize_t receivedBytes = recvfrom(client->sockfd, client->ntp_packet, NTP_PACKET_SIZE, 0, &client->server_addr, &len); if (receivedBytes < 0) { perror("recvfrom failed"); return -1; } return 0; } // 解析NTP响应中的时间 static time_t parseNTPResponse(const char* ntp_packet) { unsigned int sec_since_1900 = ntohl(((unsigned int *)ntp_packet)[10]); unsigned int frac_sec = ntohl(((unsigned int *)ntp_packet)[11]); double frac = frac_sec * 2.0e-32; return (time_t)(sec_since_1900 - 2208988800U) + (time_t)frac; } typedef struct { char ip[64]; uint16_t port; }ipaddr_t; static int get_addr(ipaddr_t *addr, struct addrinfo *info) { addr->ip[0] = 0; addr->port = 0; if(info->ai_family==AF_INET) { struct sockaddr_in* p=(struct sockaddr_in*)(info->ai_addr); addr->port = htons(p->sin_port); inet_ntop(p->sin_family, &p->sin_addr, addr->ip, sizeof(addr->ip)); } else if(info->ai_family==AF_INET6) { struct sockaddr_in6* p=(struct sockaddr_in6*)(info->ai_addr); addr->port = htons(p->sin6_port); inet_ntop(p->sin6_family, &p->sin6_addr, addr->ip, sizeof(addr->ip)); } else { return -1; } return 0; } static void *tmp_thread(void *arg) { ipaddr_t ipa; int r,fd=-1,err; struct addrinfo hints; struct addrinfo *rp; NTPClient *pc=(NTPClient*)arg; char tmp[32]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0;//IPPROTO_UDP; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_testcancel(); sprintf(tmp, "%d", pc->port); printf("___getaddrinfo in\n"); err = getaddrinfo(pc->server, tmp, &hints, &pc->res); if(err){ printf("___getaddrinfo from %s failed, %s\n", pc->server, gai_strerror(err)); pthread_exit(NULL); } printf("___getaddrinfo out\n"); for(rp=pc->res; rp; rp=rp->ai_next){ get_addr(&ipa, rp); printf("socket to %s:%d ", ipa.ip, ipa.port); fd = socket(rp->ai_family, rp->ai_socktype, 0); if (fd>=0) { pc->sockfd = fd; pc->server_addr = *rp->ai_addr; printf("ok!\n"); break; } printf("failed!\n"); } if(fd>=0) { pc->flag = 1; } else { pc->flag = -1; } pthread_exit(NULL); } NTPClient* createNTPClient(const char* ntp_server, int port, int timeout_sec) { NTPClient* client = (NTPClient*)malloc(sizeof(NTPClient)); if (!client) { perror("createNTPClient, malloc failed\n"); return NULL; } pthread_t tid; client->server = (char*)ntp_server; client->port = port; client->res = NULL; client->flag = 0; pthread_create(&tid, NULL, tmp_thread, client); pthread_detach(tid); for(int i=0; iflag) { break; } sleep(1); } if(client->flag!=1) { if(client->flag==0) { printf("___getaddrinfo timeout, cancel thread %lu\n", (unsigned long)tid); // pthread_cancel(tid);//SIGTERM // pthread_join(tid, NULL); printf("___cancel ok!\n"); } if(client->res) freeaddrinfo(client->res); free(client); return NULL; } if(client->res) freeaddrinfo(client->res); struct timeval tv; tv.tv_sec = timeout_sec; tv.tv_usec = 0; setsockopt(client->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv)); printf("___createNTPClient ok!\n"); return client; } // 销毁NTP客户端实例 void destroyNTPClient(NTPClient* client) { if (client) { close(client->sockfd); free(client); } } // 从NTP服务器获取当前时间 int getCurrentTimeFromNTP(NTPClient* client, time_t* current_time) { if (sendNTPRequest(client) < 0) { return -1; } if (receiveNTPResponse(client) < 0) { return -1; } *current_time = parseNTPResponse(client->ntp_packet); return 0; } // 根据时间获取星期信息 int getCurrentWeekday(time_t current_time,char *weekday) { struct tm * timeinfo; timeinfo = localtime(¤t_time); if (timeinfo == NULL) { return -1; } // 格式化星期几 strftime(weekday, 20, "%A", timeinfo); // %A 代表完整的星期名,例如:Sunday return 0; }