加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
ntp.c 8.73 KB
一键复制 编辑 原始数据 按行查看 历史
jin 提交于 2024-07-15 16:06 . add demos
#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 <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 //NTP模式
#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 2208988800 //自1900-1-1到1970-1-1经过的秒数
//用于64位时间戳,即小数部分为32位
//NTP_CONV_FRAC32(x)将x转换为NTP时间戳中的小数部分值
//NTP_REVE_FRAC32(x)将NTP时间戳中的小数部分值x解析成具体值
#define NTP_CONV_FRAC32(x) (uint64_t) ((x) * ((uint64_t)1<<32))
#define NTP_REVE_FRAC32(x) ((double) ((double) (x) / ((uint64_t)1<<32)))
//用于32位时间戳,即小数部分为16位
//NTP_CONV_FRAC16(x)将x转换为NTP时间戳中的小数部分值
//NTP_REVE_FRAC16(x)将NTP时间戳中的小数部分值x解析成具体值
#define NTP_CONV_FRAC16(x) (uint32_t) ((x) * ((uint32_t)1<<16))
#define NTP_REVE_FRAC16(x) ((double)((double) (x) / ((uint32_t)1<<16)))
//timeval结构中tv_usec字段和NTP时间戳小数部分互转
#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 ))
typedef struct _uint64_time
{
uint32_t int_part;
uint32_t float_part;
} uint64_time;
typedef struct _NTP_time
{
unsigned char leap_ver_mode;
unsigned char stratum;
char poll;
char precision;
int32_t root_delay;
int32_t root_dispersion;
int32_t reference_identifine;
uint64_time reference_time;
uint64_time original_time;
uint64_time receive_time;
uint64_time transmit_time;
} NTP_time, *NTP_time_Ptr;
struct s_fixedpt //32位NTP时间戳
{
uint16_t intpart; //整数部分
uint16_t fracpart; //小数部分
};
struct l_fixedpt //64位NTP时间戳
{
uint32_t intpart;
uint32_t fracpart;
};
//此结构体按位定义字段,所以需要解决字节序问题
struct ntphdr
{
#if __BYTE_ORDER == __BID_ENDIAN //大端
unsigned int ntp_li : 2;
unsigned int ntp_vn : VERSION_3;
unsigned int ntp_mode : MODE_CLIENT;
#elif __BYTE_ORDER == __LITTLE_ENDIAN //小端
unsigned int ntp_mode : MODE_CLIENT;
unsigned int ntp_vn : VERSION_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 结构体返回
printf("sec: %ld\n", tv.tv_sec);
printf("usec: %ld\n", tv.tv_usec);
ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970); //将UTC时间戳改为NTP时间戳
ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
printf("intpart: %u\n", ntp->ntp_orits.intpart);
printf("fracpart: %u\n", ntp->ntp_orits.fracpart);
*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);
printf("t1 = %f\n", t1);
t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
printf("t2 = %f\n", t2);
t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
printf("t3 = %f\n", t3);
t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
printf("t4 = %f\n", t4);
return ((t2 - t1) + (t3 - t4)) / 2;
}
void test_len()
{
printf("sizeof(int8_t) = %ld\n", sizeof(int8_t));
printf("sizeof(uint8_t) = %ld\n", sizeof(uint8_t));
printf("sizeof(unsigned int) = %ld\n", sizeof(unsigned int));
printf("sizeof(uint32_t) = %ld\n", sizeof(uint32_t));
printf("sizeof(struct ntphdr) = %ld\n", sizeof(struct ntphdr));
printf("sizeof(struct NTP_time) = %ld\n", sizeof(NTP_time));
}
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 = 0;
int maxfd1;
struct sockaddr_in servaddr;
fd_set readfds;
struct timeval timeout; //超时时间
struct timeval recvtv;
struct timeval tv; //1970.1.1至今偏移秒数和微秒数
double offset;
test_len();
// 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);
}
//构建并发送NTP报文
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集合
FD_SET(sockfd, &readfds); //将给定的文件描述符加入到集合中
maxfd1 = sockfd + 1;
//设置select超时时间
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);
}
struct ntphdr *ndr = (struct ntphdr *)buf;
printf("ntp mode: %u\n", ndr->ntp_mode);
printf("ntp vn: %u\n", ndr->ntp_vn);
printf("ntp li: %u\n", ndr->ntp_li);
printf("ntp startum: %u\n", ndr->ntp_stratum);
printf("ntp poll: %u\n", ndr->ntp_poll);
printf("ntp precision: %u\n", ndr->ntp_precision);
printf("ntp orits int: %u\n", ndr->ntp_orits.intpart);
printf("ntp orits frac: %u\n", ndr->ntp_orits.fracpart);
printf("ntp recvts int: %u\n", ndr->ntp_recvts.intpart);
printf("ntp recvts frac: %u\n", ndr->ntp_recvts.fracpart);
printf("ntp transts int: %u\n", ndr->ntp_transts.intpart);
printf("ntp transts frac: %u\n", ndr->ntp_transts.fracpart);
//接收到服务器数据后记录一次时间,用以计算C/S之间时间偏移量
gettimeofday(&recvtv, NULL);
offset = get_offset((struct ntphdr *) buf, &recvtv);
printf("offset = %f\n", offset);
//再次系统时间,用以进行校时
gettimeofday(&tv, NULL); //1970.1.1至今偏移时间的秒数和微秒数
//针对tv.tv_usec向上溢出的处理,并进行校时
tv.tv_sec += (int) offset + 28800;
tv.tv_usec += offset - (int) offset;
local = localtime((time_t*)&tv.tv_sec); //1970.1.1到目前的偏移秒数和微秒数变为本地时间
strftime(dateBuf, 64, "%Y-%m-%d %H:%M:%S", local); //格式化本地时间
sprintf(cmd, "system busybox date -s \"%s\"", dateBuf);
printf("cmd: %s\n", cmd);
printf("Now: %s \n", ctime((time_t *) &tv.tv_sec));
}
}
close(sockfd);
return 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化