我根据给定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);
}
Makefile:
.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
)
如何从T2
和packet.rxTm_s
构建packet.rxTm_f
作为64
位的一个时间戳。
答案 0 :(得分:1)
当我用一些警告标志编译你的代码时,我收到了一些警告:
25
警告已生成。
有些显然没用,但有些确实很可怕:
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()
之类的内容。4
,您的代码要求3
(0x1b; // Represents 27 in base 10 or 00011011 in base 2.
=&gt; 3版本字段。顺便提一下,您创建一个位字段...使用它;不要使用魔法数字)。所以我想你最终会混合使用版本3
和4
;我不明白的是,NTP的文档声称该协议是向后兼容的。要回答这个问题,您应该阅读NTP协议的RFC V3(已废弃)。所以,如果我没有给你 你问题的答案,你会问我为什么要回答你?这是因为你明显误解了批判性的事情。
首先这个教程是********(Linus&#39;最喜欢的单词),你不能期望在最后RFC时用C语言中的30行代码解析协议NTP
协议的行6163
行!如果要实现自己的解析器,必须才能阅读此协议。
其次,本教程的代码编写得很糟糕,我的&#34; wtf-o-matic&#34;通过线&#34;向我展示了非常高的&#34; wtf (比失败更糟糕)。为什么使用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);并停止使用这个&#34;教程&#34;。如果你想实现你自己的NTP库(好消息即将到来),你可以使用RFC提供的漂亮的 skeleton ,我很高兴在RFC中找到一个代码示例,我做了一个gist of the example对你而言。好消息是RFC在文档行方面减少了一半来阅读!所以GL,HF在你的演讲中。
答案 1 :(得分:0)
我已阅读C代码并同意&#34;质量&#34;根据要做的事情,这段代码。
对于分析,我不同意这一点: - 我不认为问题是实时问题,可以通过在NTP协议上使用wireshark和过滤器轻松检查。尽管程序失败了,但wireshark仍然应该在以太网接口上捕获数据。 - 对我来说,问题似乎是由Kiss-o&-Dath-Packet引起的(参见RFC 5905的7.4节)。 - wirehark捕获将有助于了解真正发生的事情并进行更深入的调查。