无限循环的NTP客户端(不工作)

问题描述 投票:0回答:2

我根据给定的here设计了一个客户端。

我修改了代码(见下文)以满足这些要求

  1. 打印时间戳,以毫秒为单位
  2. 在无限循环内获取NTP时间戳,RTT等
  3. 定义一个循环时间(获得每个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

我面临的问题,需要您的建议可能的代码更改和改进

  1. 代码不能无限运行,它在两者之间停止,并且在一些迭代后不会打印任何输出(没有显示错误消息)
  2. 尽管64位,时间戳的大小不同
  3. RTT和精度打印相同的值,并且在每次迭代中都不会更新(有时两者都显示0
  4. 如何从T2packet.rxTm_s建立packet.rxTm_f作为64bit的一个时间戳。
c sockets time server ntp
2个回答
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

这对我来说很好看。


所以回答你的问题:

  1. 代码没有在我的机器上停止并按预期工作。当然,你的代码停止的原因很明显:你已经调用了write()read(),它们可以阻塞直到I / O完成。请记住,您正在使用UDP协议,某些数据包永远不会到达服务器,或者某些来自服务器的数据包永远不会到达您。如果要编写真实服务器代码,请使用select()poll()epoll()之类的内容。
  2. NTP的版本是4,你的代码要求30x1b; // Represents 27 in base 10 or 00011011 in base 2. => 3版本字段。顺便说一下,你创建一个位字段......使用它;不​​要使用幻数)。所以我想你最终会得到34版本的混合;我不明白的是,NTP的文档声称该协议是向后兼容的。要回答这个问题,您应该阅读NTP协议的RFC V3(已废弃)。
  3. 我不知道它是什么以及它为什么不改变。
  4. 我不知道。

那么,如果我没有给你答案,你会问我为什么要回答你?这是因为你明显误解了批判性事物。

首先这个教程是********(Linus最喜欢的单词),当Qazxswpoi协议的最后一个RFCNTP行时,你不能指望在C中用30行代码解析一个协议!如果要实现自己的解析器,必须阅读此协议。

其次,本教程的代码写得非常糟糕,我的“wtf-o-matic”给我看了一个非常高的“wtf(比故障更糟糕)”的值。为什么使用6163而不是unsigned用于位字段(位字段仍然是80%的实现定义)?为什么uint8_t而不是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()等...


作为结论:阅读我链接的RFC(NTP V4);并停止使用这个“教程”。如果你想实现你自己的NTP库(好消息即将到来),你可以使用RFC提供的漂亮的骨架,我很高兴在RFC中找到一个代码示例,我为你做了一个htons()。好消息是RFC在文档行方面减少了一半来阅读!所以GL,HF在你的讲座中。


0
投票

我已阅读C代码,并就要做的事情就此代码的“质量”达成一致。

对于分析,我不同意这一点: - 我认为这个问题不是实时问题,可以通过在NTP协议上使用wireshark和过滤器轻松检查。尽管程序失败了,但wireshark仍然应该在以太网接口上捕获数据。 - 对我来说,问题似乎是由Kiss-o'-Death数据包引起的(参见RFC 5905的7.4节)。 - wirehark捕获将有助于了解真正发生的事情并进行更深入的调查。

© www.soinside.com 2019 - 2024. All rights reserved.