NTP 客户端无限循环(不工作)
NTP client in infinite loop (Not working )
我根据给定的参考 here 设计了一个客户端。
我修改了代码(见下文)以满足这些要求
以毫秒为单位打印时间戳
在无限循环中获取 NTP 时间戳、RTT 等
定义循环时间(每x
秒获取时间)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#define NTP_TIMESTAMP_DELTA 2208988800ull
#define ENDIAN_SWAP32(data) \
((data >> 24) | /* right shift 3 bytes */ \
((data & 0x00ff0000) >> 8) | /* right shift 1 byte */ \
((data & 0x0000ff00) << 8) | /* left shift 1 byte */ \
((data & 0x000000ff) << 24)) /* left shift 3 bytes */
void error(char *msg) {
perror(msg); // Print the error message to stderr.
exit(0); // Quit the process.
}
int main(int argc, char *argv[]) {
int sockfd, n; // Socket file descriptor and the n return result from
// writing/reading from the socket.
int portno = 123; // NTP UDP port number
int i, z = 0;
struct timeval tv1, tv2;
typedef struct {
unsigned li : 2; // Only two bits. Leap indicator.
unsigned vn : 3; // Only three bits. Version number of the protocol.
unsigned
mode : 3; // Only three bits. Mode. Client will pick mode 3 for client.
uint8_t stratum; // Eight bits. Stratum level of the local clock.
uint8_t poll; // Eight bits. Maximum interval between successive messages.
uint8_t precision; // Eight bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t
rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
uint32_t txTm_s; // 32 bits and the most important field the client cares
// about. Transmit time-stamp seconds.
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
} ntp_packet; // Total: 384 bits or 48 bytes.
// Create and zero out the packet. All 48 bytes worth.
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memset(&packet, 0, sizeof(ntp_packet));
*((char *)&packet + 0) =
0x1b; // Represents 27 in base 10 or 00011011 in base 2.
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
struct sockaddr_in serv_addr; // Server address data structure.
struct hostent *server; // Server data structure.
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create a UDP socket.
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]); // Convert URL to IP.
if (server == NULL)
error("ERROR, no such host");
// Zero out the server address structure.
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
// Copy the server's IP address to the server address structure.
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
server->h_length);
// Convert the port number integer to network big-endian style and save it to
// the server address structure.
serv_addr.sin_port = htons(portno);
// Call up the server using its IP address and port number.
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("\nNTP client started \n\n");
while (1) {
z = z + 1;
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memset(&packet, 0, sizeof(ntp_packet));
*((char *)&packet + 0) =
0x1b; // Represents 27 in base 10 or 00011011 in base 2.
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
gettimeofday(&tv1, NULL);
unsigned long long millisecondsSinceEpochStart =
(unsigned long long)(tv1.tv_sec) * 1000 +
(unsigned long long)(tv1.tv_usec) / 1000;
n = write(sockfd, (char *)&packet, sizeof(ntp_packet));
if (n < 0)
error("ERROR writing to socket");
n = read(sockfd, (char *)&packet, sizeof(ntp_packet));
if (n < 0)
error("ERROR reading from socket");
gettimeofday(&tv2, NULL);
unsigned long long millisecondsSinceEpochEnd =
(unsigned long long)(tv2.tv_sec) * 1000 +
(unsigned long long)(tv2.tv_usec) / 1000;
packet.precision = ntohl(packet.precision); // Precision
packet.rootDelay = ntohl(packet.rootDelay);
packet.rxTm_s = ntohl(packet.rxTm_s); // Time-stamp seconds.
packet.rxTm_f = ntohl(packet.rxTm_f); // Time-stamp fraction of a second.
packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds.
packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second.
// packet.rootDelay = ntohl(packet.rootDelay); // RTT
// time_t rootDelay = ( time_t ) ( packet.rootDelay - NTP_TIMESTAMP_DELTA );
printf("\n................................................................."
"...\n");
printf("\nIteration Number : %d\n", z);
printf("..................................................................."
"..\n");
printf("NTP Telegram Contains\n");
for (i = 0; i < sizeof(ntp_packet); i++) {
if (i != 0 && i % 8 == 0)
printf("\n");
printf("0x%2x ", ptr[i]);
}
printf("\n\n\n");
printf("Client Send Timestamp T1(ms): %llu\n", millisecondsSinceEpochStart);
printf("server Recieve Timestamp T2: %llu.", packet.rxTm_s);
printf("%llu\n", packet.rxTm_f);
printf("server Transmit Timestamp T3: %llu.", packet.txTm_s);
printf("%llu\n", packet.txTm_f);
printf("Client Recieve Timestamp T4(ms): %llu\n",
millisecondsSinceEpochEnd);
printf("RTT (calculated by NTP): %llu\n", packet.rootDelay);
printf("Precision (calculated by NTP): %llu\n", packet.precision);
sleep(atoi(argv[2]));
}
return 0;
close(sockfd);
}
生成文件:
.PHONY: all clean tags
CFLAGS := -Wall -O2
all: client
client: client.c
$(CC) $(CFLAGS) $^ -o $@
strip -s $@
tags:
ctags -R .
clean:
-rm *.o client
使用
制作和 运行 代码
./client 0.pool.ntp.org 1
我面临的问题需要您的建议可能的代码更改和改进
代码不会无限运行,它会在中间停止并且在一些迭代后不会打印任何输出(没有显示错误消息)
尽管 64
位
,但时间戳的大小不同
RTT 和精度打印相同的值并且不会在每次迭代中更新(有时两者都显示 0
)
如何从 packet.rxTm_s
和 packet.rxTm_f
构建 T2
作为 64
位的一个时间戳。
当我用一些警告标志编译你的代码时,我收到了一些警告:
25
warnings generated.
有的明明没用,有的却很可怕:
warning: implicit conversion changes signedness: 'int' to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
server->h_length);
~~~~~~~~^~~~~~~~
warning: declaration shadows a local variable [-Wshadow]
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
^
note: previous declaration is here
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
^
warning: declaration shadows a local variable [-Wshadow]
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
^
note: previous declaration is here
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
^
warning: implicit declaration of function 'gettimeofday' is invalid in C99 [-Wimplicit-function-declaration]
gettimeofday(&tv1, NULL);
^
warning: implicit declaration of function 'write' is invalid in C99 [-Wimplicit-function-declaration]
n = write(sockfd, (char *)&packet, sizeof(ntp_packet));
^
warning: implicit declaration of function 'read' is invalid in C99 [-Wimplicit-function-declaration]
n = read(sockfd, (char *)&packet, sizeof(ntp_packet));
^
warning: implicit conversion loses integer precision: 'unsigned int' to 'uint8_t' (aka 'unsigned char') [-Wconversion]
packet.precision = ntohl(packet.precision); // Precision
~ ^~~~~~~~~~~~~~~~~~~~~~~
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("server Recieve Timestamp T2: %llu.", packet.rxTm_s);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("%llu\n", packet.rxTm_f);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("server Transmit Timestamp T3: %llu.", packet.txTm_s);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("%llu\n", packet.txTm_f);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("RTT (calculated by NTP): %llu\n", packet.rootDelay);
~~~~ ^~~~~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint8_t' (aka 'unsigned char') [-Wformat]
printf("Precision (calculated by NTP): %llu\n", packet.precision);
~~~~ ^~~~~~~~~~~~~~~~
%hhu
warning: implicit declaration of function 'sleep' is invalid in C99 [-Wimplicit-function-declaration]
sleep(atoi(argv[2]));
^
warning: implicit declaration of function 'close' is invalid in C99 [-Wimplicit-function-declaration]
close(sockfd);
^
warning: unused parameter 'argc' [-Wunused-parameter]
int main(int argc, char *argv[]) {
^
warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
for (i = 0; i < sizeof(ntp_packet); i++) {
~ ^ ~~~~~~~~~~~~~~~~~~
warning: 'return' will never be executed [-Wunreachable-code-return]
return 0;
^
warning: code will never be executed [-Wunreachable-code]
close(sockfd);
我没有全部保留,但还有19!首先,您应该修复所有这些问题,因为在我看来它们很重要,而忽略它们可能会导致未定义的行为。清除所有这些警告后,您可以再次尝试调试代码。 (尝试使用 clang -Weverything 使警告达到零;并非所有警告都有用,但在忽略它们之前尝试理解它们)。
接下来,我测试了您的代码,它似乎可以在我的机器上运行。然而,您的代码中存在许多潜在的未定义行为。位字段非常不方便,因为它们的布局是实现定义的。
尽管如此,我得到了这个输出:
....................................................................
Iteration Number : 42
.....................................................................
NTP Telegram Contains
0x1c 0x 2 0x 3 0x 0 0xd7 0x 7 0x 0 0x 0
0x 0 0x 0 0x 8 0xad 0xf7 0x5c 0x9c 0x6b
0xdd 0xd1 0xe8 0x64 0xc1 0x31 0xc9 0x4a
0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0
0xca 0xe8 0xd1 0xdd 0x66 0x71 0xa4 0x25
0xca 0xe8 0xd1 0xdd 0xb8 0xb0 0xa6 0x25
Client Send Timestamp T1(ms): 1512536048971
server Recieve Timestamp T2: 3721521354.631533926
server Transmit Timestamp T3: 3721521354.631681208
Client Recieve Timestamp T4(ms): 1512536048973
RTT (calculated by NTP): 2007
Precision (calculated by NTP): 0
// after some iteration
....................................................................
Iteration Number : 69
.....................................................................
NTP Telegram Contains
0x1c 0x 2 0x 3 0x 0 0xd7 0x 7 0x 0 0x 0
0x 0 0x 0 0x 8 0xc7 0xf7 0x5c 0x9c 0x6b
0xdd 0xd1 0xe8 0x64 0xc1 0x31 0xc9 0x4a
0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0
0xe5 0xe8 0xd1 0xdd 0xcf 0x58 0x20 0x37
0xe5 0xe8 0xd1 0xdd 0x86 0xaf 0x22 0x37
Client Send Timestamp T1(ms): 1512536076040
server Recieve Timestamp T2: 3721521381.924866767
server Transmit Timestamp T3: 3721521381.925020038
Client Recieve Timestamp T4(ms): 1512536076042
RTT (calculated by NTP): 2007
Precision (calculated by NTP): 0
我觉得不错。
所以回答你的问题:
- 代码没有在我的机器中停止并且按预期工作。当然,您的代码停止的原因很明显:您调用了
write()
和 read()
,它们可能会阻塞,直到 I/O 完成。请记住,您使用的是 UDP 协议,有些数据包永远不会到达服务器,或者来自服务器的某些数据包永远不会到达您。如果您想编写真实服务器代码,请使用 select()
或 poll()
或 epoll()
. 之类的东西
- NTP 的版本为
4
,您的代码要求 3
(0x1b; // Represents 27 in base 10 or 00011011 in base 2.
=> 版本字段为 3。顺便说一下,您创建了一个位字段...使用IT;不要使用幻数)。所以我想你最终会以某种方式混合版本 3
和 4
;我不明白的是 NTP 的文档声称该协议是向后兼容的。要回答这个问题,您应该阅读 NTP 协议的 RFC V3(已过时)。
- 我不知道它是什么以及为什么它没有改变。
- 我不知道。
所以,你会问,如果我不给你 你的问题的答案,我为什么要回答你?这是因为你显然误解了关键的事情。
首先本教程是********(Linus 最喜欢的词),当[的最后一个RFC时,你不能指望用C 30 行代码解析一个协议。 =23=] 协议有 6163
行!如果您想实现自己的解析器,您必须阅读此协议。
其次,这个教程的代码写的很烂,我的"wtf-o-matic"让我看到了很高的"wtf (worse than failure) by line"值。为什么对位字段使用 unsigned
而不是 uint8_t
(无论如何,位字段都是 80% 实现定义的)?为什么 ntp_packet packet = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
而不是 ntp_packet packet = {0};
?为什么以及为什么以及为什么在 ??? 之后使用 memset(&packet, 0, sizeof(ntp_packet));
那是什么 *((char *)&packet + 0) = 0x1b;
,这是 ******** !好吧,还有更多要说的,但我只建议您使用 getaddrinfo()
代替所有乱七八糟的 gethostbyname()
、htons()
等等...
作为结论:阅读我链接的 RFC (NTP V4);并停止使用此 "tutorial"。如果你想实现你自己的 NTP 库(好消息来了),你可以使用 RFC 提供的 nice skeleton,我很高兴在 RFC 中找到一个代码示例我给你做了一个gist of the example。好消息是 RFC 的文档行数减少了一半!所以 GL,HF 在你的演讲中。
我已阅读 C 代码并同意此代码的 "quality" 要做的事情。
对于分析,我不同意这一点:
- 我不认为问题是实时问题,这可以通过在 NTP 协议上使用 wireshark 和过滤器轻松检查。尽管程序失败了,但 wireshark 仍应捕获以太网接口上的数据。
- 对我来说,问题似乎是由 Kiss-o'-Death 数据包引起的(请参阅 RFC 5905 的第 7.4 节)。
- 一次 wireshark 捕获将有助于了解真正发生的事情并进行更深入的调查。
我根据给定的参考 here 设计了一个客户端。
我修改了代码(见下文)以满足这些要求
以毫秒为单位打印时间戳
在无限循环中获取 NTP 时间戳、RTT 等
定义循环时间(每
x
秒获取时间)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#define NTP_TIMESTAMP_DELTA 2208988800ull
#define ENDIAN_SWAP32(data) \
((data >> 24) | /* right shift 3 bytes */ \
((data & 0x00ff0000) >> 8) | /* right shift 1 byte */ \
((data & 0x0000ff00) << 8) | /* left shift 1 byte */ \
((data & 0x000000ff) << 24)) /* left shift 3 bytes */
void error(char *msg) {
perror(msg); // Print the error message to stderr.
exit(0); // Quit the process.
}
int main(int argc, char *argv[]) {
int sockfd, n; // Socket file descriptor and the n return result from
// writing/reading from the socket.
int portno = 123; // NTP UDP port number
int i, z = 0;
struct timeval tv1, tv2;
typedef struct {
unsigned li : 2; // Only two bits. Leap indicator.
unsigned vn : 3; // Only three bits. Version number of the protocol.
unsigned
mode : 3; // Only three bits. Mode. Client will pick mode 3 for client.
uint8_t stratum; // Eight bits. Stratum level of the local clock.
uint8_t poll; // Eight bits. Maximum interval between successive messages.
uint8_t precision; // Eight bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t
rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
uint32_t txTm_s; // 32 bits and the most important field the client cares
// about. Transmit time-stamp seconds.
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
} ntp_packet; // Total: 384 bits or 48 bytes.
// Create and zero out the packet. All 48 bytes worth.
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memset(&packet, 0, sizeof(ntp_packet));
*((char *)&packet + 0) =
0x1b; // Represents 27 in base 10 or 00011011 in base 2.
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
struct sockaddr_in serv_addr; // Server address data structure.
struct hostent *server; // Server data structure.
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create a UDP socket.
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]); // Convert URL to IP.
if (server == NULL)
error("ERROR, no such host");
// Zero out the server address structure.
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
// Copy the server's IP address to the server address structure.
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
server->h_length);
// Convert the port number integer to network big-endian style and save it to
// the server address structure.
serv_addr.sin_port = htons(portno);
// Call up the server using its IP address and port number.
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("\nNTP client started \n\n");
while (1) {
z = z + 1;
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memset(&packet, 0, sizeof(ntp_packet));
*((char *)&packet + 0) =
0x1b; // Represents 27 in base 10 or 00011011 in base 2.
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
gettimeofday(&tv1, NULL);
unsigned long long millisecondsSinceEpochStart =
(unsigned long long)(tv1.tv_sec) * 1000 +
(unsigned long long)(tv1.tv_usec) / 1000;
n = write(sockfd, (char *)&packet, sizeof(ntp_packet));
if (n < 0)
error("ERROR writing to socket");
n = read(sockfd, (char *)&packet, sizeof(ntp_packet));
if (n < 0)
error("ERROR reading from socket");
gettimeofday(&tv2, NULL);
unsigned long long millisecondsSinceEpochEnd =
(unsigned long long)(tv2.tv_sec) * 1000 +
(unsigned long long)(tv2.tv_usec) / 1000;
packet.precision = ntohl(packet.precision); // Precision
packet.rootDelay = ntohl(packet.rootDelay);
packet.rxTm_s = ntohl(packet.rxTm_s); // Time-stamp seconds.
packet.rxTm_f = ntohl(packet.rxTm_f); // Time-stamp fraction of a second.
packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds.
packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second.
// packet.rootDelay = ntohl(packet.rootDelay); // RTT
// time_t rootDelay = ( time_t ) ( packet.rootDelay - NTP_TIMESTAMP_DELTA );
printf("\n................................................................."
"...\n");
printf("\nIteration Number : %d\n", z);
printf("..................................................................."
"..\n");
printf("NTP Telegram Contains\n");
for (i = 0; i < sizeof(ntp_packet); i++) {
if (i != 0 && i % 8 == 0)
printf("\n");
printf("0x%2x ", ptr[i]);
}
printf("\n\n\n");
printf("Client Send Timestamp T1(ms): %llu\n", millisecondsSinceEpochStart);
printf("server Recieve Timestamp T2: %llu.", packet.rxTm_s);
printf("%llu\n", packet.rxTm_f);
printf("server Transmit Timestamp T3: %llu.", packet.txTm_s);
printf("%llu\n", packet.txTm_f);
printf("Client Recieve Timestamp T4(ms): %llu\n",
millisecondsSinceEpochEnd);
printf("RTT (calculated by NTP): %llu\n", packet.rootDelay);
printf("Precision (calculated by NTP): %llu\n", packet.precision);
sleep(atoi(argv[2]));
}
return 0;
close(sockfd);
}
生成文件:
.PHONY: all clean tags
CFLAGS := -Wall -O2
all: client
client: client.c
$(CC) $(CFLAGS) $^ -o $@
strip -s $@
tags:
ctags -R .
clean:
-rm *.o client
使用
制作和 运行 代码./client 0.pool.ntp.org 1
我面临的问题需要您的建议可能的代码更改和改进
代码不会无限运行,它会在中间停止并且在一些迭代后不会打印任何输出(没有显示错误消息)
尽管
64
位 ,但时间戳的大小不同
RTT 和精度打印相同的值并且不会在每次迭代中更新(有时两者都显示
0
)如何从
packet.rxTm_s
和packet.rxTm_f
构建T2
作为64
位的一个时间戳。
当我用一些警告标志编译你的代码时,我收到了一些警告:
25
warnings generated.
有的明明没用,有的却很可怕:
warning: implicit conversion changes signedness: 'int' to 'size_t' (aka 'unsigned long') [-Wsign-conversion]
server->h_length);
~~~~~~~~^~~~~~~~
warning: declaration shadows a local variable [-Wshadow]
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
^
note: previous declaration is here
ntp_packet packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
^
warning: declaration shadows a local variable [-Wshadow]
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
^
note: previous declaration is here
uint8_t *ptr = (uint8_t *)(&packet); /* to read raw bytes */
^
warning: implicit declaration of function 'gettimeofday' is invalid in C99 [-Wimplicit-function-declaration]
gettimeofday(&tv1, NULL);
^
warning: implicit declaration of function 'write' is invalid in C99 [-Wimplicit-function-declaration]
n = write(sockfd, (char *)&packet, sizeof(ntp_packet));
^
warning: implicit declaration of function 'read' is invalid in C99 [-Wimplicit-function-declaration]
n = read(sockfd, (char *)&packet, sizeof(ntp_packet));
^
warning: implicit conversion loses integer precision: 'unsigned int' to 'uint8_t' (aka 'unsigned char') [-Wconversion]
packet.precision = ntohl(packet.precision); // Precision
~ ^~~~~~~~~~~~~~~~~~~~~~~
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("server Recieve Timestamp T2: %llu.", packet.rxTm_s);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("%llu\n", packet.rxTm_f);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("server Transmit Timestamp T3: %llu.", packet.txTm_s);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("%llu\n", packet.txTm_f);
~~~~ ^~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Wformat]
printf("RTT (calculated by NTP): %llu\n", packet.rootDelay);
~~~~ ^~~~~~~~~~~~~~~~
%u
warning: format specifies type 'unsigned long long' but the argument has type 'uint8_t' (aka 'unsigned char') [-Wformat]
printf("Precision (calculated by NTP): %llu\n", packet.precision);
~~~~ ^~~~~~~~~~~~~~~~
%hhu
warning: implicit declaration of function 'sleep' is invalid in C99 [-Wimplicit-function-declaration]
sleep(atoi(argv[2]));
^
warning: implicit declaration of function 'close' is invalid in C99 [-Wimplicit-function-declaration]
close(sockfd);
^
warning: unused parameter 'argc' [-Wunused-parameter]
int main(int argc, char *argv[]) {
^
warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
for (i = 0; i < sizeof(ntp_packet); i++) {
~ ^ ~~~~~~~~~~~~~~~~~~
warning: 'return' will never be executed [-Wunreachable-code-return]
return 0;
^
warning: code will never be executed [-Wunreachable-code]
close(sockfd);
我没有全部保留,但还有19!首先,您应该修复所有这些问题,因为在我看来它们很重要,而忽略它们可能会导致未定义的行为。清除所有这些警告后,您可以再次尝试调试代码。 (尝试使用 clang -Weverything 使警告达到零;并非所有警告都有用,但在忽略它们之前尝试理解它们)。
接下来,我测试了您的代码,它似乎可以在我的机器上运行。然而,您的代码中存在许多潜在的未定义行为。位字段非常不方便,因为它们的布局是实现定义的。
尽管如此,我得到了这个输出:
....................................................................
Iteration Number : 42
.....................................................................
NTP Telegram Contains
0x1c 0x 2 0x 3 0x 0 0xd7 0x 7 0x 0 0x 0
0x 0 0x 0 0x 8 0xad 0xf7 0x5c 0x9c 0x6b
0xdd 0xd1 0xe8 0x64 0xc1 0x31 0xc9 0x4a
0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0
0xca 0xe8 0xd1 0xdd 0x66 0x71 0xa4 0x25
0xca 0xe8 0xd1 0xdd 0xb8 0xb0 0xa6 0x25
Client Send Timestamp T1(ms): 1512536048971
server Recieve Timestamp T2: 3721521354.631533926
server Transmit Timestamp T3: 3721521354.631681208
Client Recieve Timestamp T4(ms): 1512536048973
RTT (calculated by NTP): 2007
Precision (calculated by NTP): 0
// after some iteration
....................................................................
Iteration Number : 69
.....................................................................
NTP Telegram Contains
0x1c 0x 2 0x 3 0x 0 0xd7 0x 7 0x 0 0x 0
0x 0 0x 0 0x 8 0xc7 0xf7 0x5c 0x9c 0x6b
0xdd 0xd1 0xe8 0x64 0xc1 0x31 0xc9 0x4a
0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0
0xe5 0xe8 0xd1 0xdd 0xcf 0x58 0x20 0x37
0xe5 0xe8 0xd1 0xdd 0x86 0xaf 0x22 0x37
Client Send Timestamp T1(ms): 1512536076040
server Recieve Timestamp T2: 3721521381.924866767
server Transmit Timestamp T3: 3721521381.925020038
Client Recieve Timestamp T4(ms): 1512536076042
RTT (calculated by NTP): 2007
Precision (calculated by NTP): 0
我觉得不错。
所以回答你的问题:
- 代码没有在我的机器中停止并且按预期工作。当然,您的代码停止的原因很明显:您调用了
write()
和read()
,它们可能会阻塞,直到 I/O 完成。请记住,您使用的是 UDP 协议,有些数据包永远不会到达服务器,或者来自服务器的某些数据包永远不会到达您。如果您想编写真实服务器代码,请使用select()
或poll()
或epoll()
. 之类的东西
- NTP 的版本为
4
,您的代码要求3
(0x1b; // Represents 27 in base 10 or 00011011 in base 2.
=> 版本字段为 3。顺便说一下,您创建了一个位字段...使用IT;不要使用幻数)。所以我想你最终会以某种方式混合版本3
和4
;我不明白的是 NTP 的文档声称该协议是向后兼容的。要回答这个问题,您应该阅读 NTP 协议的 RFC V3(已过时)。 - 我不知道它是什么以及为什么它没有改变。
- 我不知道。
所以,你会问,如果我不给你 你的问题的答案,我为什么要回答你?这是因为你显然误解了关键的事情。
首先本教程是********(Linus 最喜欢的词),当[的最后一个RFC时,你不能指望用C 30 行代码解析一个协议。 =23=] 协议有 6163
行!如果您想实现自己的解析器,您必须阅读此协议。
其次,这个教程的代码写的很烂,我的"wtf-o-matic"让我看到了很高的"wtf (worse than failure) by line"值。为什么对位字段使用 unsigned
而不是 uint8_t
(无论如何,位字段都是 80% 实现定义的)?为什么 ntp_packet packet = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
而不是 ntp_packet packet = {0};
?为什么以及为什么以及为什么在 ??? 之后使用 memset(&packet, 0, sizeof(ntp_packet));
那是什么 *((char *)&packet + 0) = 0x1b;
,这是 ******** !好吧,还有更多要说的,但我只建议您使用 getaddrinfo()
代替所有乱七八糟的 gethostbyname()
、htons()
等等...
作为结论:阅读我链接的 RFC (NTP V4);并停止使用此 "tutorial"。如果你想实现你自己的 NTP 库(好消息来了),你可以使用 RFC 提供的 nice skeleton,我很高兴在 RFC 中找到一个代码示例我给你做了一个gist of the example。好消息是 RFC 的文档行数减少了一半!所以 GL,HF 在你的演讲中。
我已阅读 C 代码并同意此代码的 "quality" 要做的事情。
对于分析,我不同意这一点: - 我不认为问题是实时问题,这可以通过在 NTP 协议上使用 wireshark 和过滤器轻松检查。尽管程序失败了,但 wireshark 仍应捕获以太网接口上的数据。 - 对我来说,问题似乎是由 Kiss-o'-Death 数据包引起的(请参阅 RFC 5905 的第 7.4 节)。 - 一次 wireshark 捕获将有助于了解真正发生的事情并进行更深入的调查。