Linux环境C++编写NTP客户端运行后无返回数据该如何解决?
问题原因
- 核心原因:未发送NTP请求报文:NTP采用请求-响应交互模式,UDP本身无连接状态,你调用的UDP
connect仅在内核层面绑定了对端地址端口,不会触发任何网络交互。你没有主动向服务端发送标准NTP请求,服务端不会主动向你推送时间数据,导致recv系统调用一直阻塞等待数据。 - 未设置接收超时:默认情况下套接字接收是永久阻塞模式,没有数据到达时会一直等待,不会主动超时退出。
- 额外问题:返回值解析错误:NTP返回的是二进制格式的时间报文,不是可打印字符串,直接用
std::cout输出pack只会得到乱码,无法得到可读时间。
修复步骤
- 构造标准NTP请求包:NTP请求报文固定为48字节,首字节填
0x1B(表示版本号4、客户端模式),剩余47字节初始化为0即可。 - 发送请求包后再调用
recv接收响应。 - 给套接字设置接收超时,避免永久阻塞。
- 解析返回的二进制NTP时间戳,转换为可读的本地时间。
修复后完整代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netdb.h> #include <netinet/in.h> #include <iostream> #include <string.h> #include <arpa/inet.h> #include <ctime> int main(int argc, char *argv[]) { int sock; int recvsize; // NTP报文固定为48字节 char pack[48] = {0}; struct sockaddr_in sAddr; struct hostent *sHost; sHost = gethostbyname("time.google.com"); if(sHost == NULL){ perror("gethostbyname error"); return 0; } sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sock < 0) { perror("socket create error"); return -1; } // 设置5秒接收超时,避免永久阻塞 struct timeval timeout = {5, 0}; if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { perror("setsockopt error"); close(sock); return -1; } memset(&sAddr, 0, sizeof(sAddr)); sAddr.sin_family = AF_INET; memcpy(&sAddr.sin_addr, sHost->h_addr_list[0], sizeof(sAddr.sin_addr)); sAddr.sin_port = htons(123); if(connect(sock, (struct sockaddr*)&sAddr, sizeof(sAddr)) == -1){ perror("connect error"); close(sock); return -1; } std::cout << "Connect success, sending NTP request..." << std::endl; // 构造NTP请求包,首字节为0x1B,其余为0 pack[0] = 0x1B; if(send(sock, pack, sizeof(pack), 0) < 0) { perror("send request error"); close(sock); return -1; } recvsize = recv(sock, pack, sizeof(pack), 0); if(recvsize < 0) { perror("recv error, maybe timeout"); close(sock); return -1; } std::cout << "Get response size: " << recvsize << std::endl; // 解析NTP时间戳,偏移40字节的位置是传输时间戳的秒数部分 uint32_t ntp_time = ntohl(*(uint32_t*)&pack[40]); // NTP时间起始是1900-01-01,Unix时间起始是1970-01-01,相差2208988800秒 time_t unix_time = ntp_time - 2208988800ULL; std::cout << "Current time: " << ctime(&unix_time) << std::endl; close(sock); return 0; }
额外注意事项
- 运行前请确认VirtualBox的Ubuntu虚拟机网络正常,能访问公网,且没有防火墙拦截123端口的UDP流量。
- 若time.google.com访问不稳定,可以替换为国内NTP服务地址,比如
ntp.aliyun.com。
内容的提问来源于stack exchange,提问作者tkm-knj
相关产品推荐
相关产品推荐

