【发布时间】:2014-01-15 05:59:07
【问题描述】:
早安,
我正在尝试实现一个非常基本的 NTP 客户端,这样我就可以查询远程 NTP 服务器(即:pool.ntp.org)的互联网时间,以便在每次启动时自动设置我的 Windows 开发板上的时间。
我已经从另一个用户那里重构了这里的代码:
Get time/date from server with sntp(windows c++)
但程序在recv() 操作处挂起。 DNS 解析和send() 操作似乎可以正常执行。
有谁知道我在哪里可以找到 Windows 中的简单 NTP 客户端示例(首选 GPL,但此时任何事情都可以),或者他们可以评论为什么以下代码块会从示例中挂起(我不应该' t 说“挂起”,它似乎永远不会收到响应)。
NTP_CLIENT.CPP
/******************************************************************************
* Project Headers
*****************************************************************************/
#include "stdafx.h"
#include "ntp_client.h"
/******************************************************************************
* System Headers
*****************************************************************************/
#include <winsock2.h>
#include <winsock.h>
#include <ws2tcpip.h>
/******************************************************************************
* Preprocessor Directives and Macros
*****************************************************************************/
/******************************************************************************
* Class Member Function Definitions
*****************************************************************************/
void Timestamp::ReverseEndian(void) {
ReverseEndianInt(seconds);
ReverseEndianInt(fraction);
}
time_t Timestamp::to_time_t(void) {
return (seconds - ((70 * 365 + 17) * 86400))&0x7fffffff;
}
void NTPMessage::ReverseEndian(void) {
ref.ReverseEndian();
orig.ReverseEndian();
rx.ReverseEndian();
tx.ReverseEndian();
}
int NTPMessage::recv(int sock) {
int ret = ::recv(sock, (char*)this, sizeof(*this), 0);
ReverseEndian();
return ret;
}
int NTPMessage::sendto(int sock, struct sockaddr_in* srv_addr) {
ReverseEndian();
int ret = ::sendto(sock, (const char*)this, sizeof(*this), 0, (sockaddr*)srv_addr, sizeof(*srv_addr));
ReverseEndian();
return ret;
}
void NTPMessage::clear() {
memset(this, 0, sizeof(*this));
}
NTP_CLIENT.H
#ifndef __NTP_CLIENT_H__
#define __NTP_CLIENT_H__
#include <ctime>
#define ReverseEndianInt(x) ((x) = \
((x)&0xff000000) >> 24 | \
((x)&0x00ff0000) >> 8 | \
((x)&0x0000ff00) << 8 | \
((x)&0x000000ff) << 24)
/**
* NTP Fixed-Point Timestamp Format.
* From [RFC 5905](http://tools.ietf.org/html/rfc5905).
*/
struct Timestamp {
unsigned int seconds; /**< Seconds since Jan 1, 1900. */
unsigned int fraction; /**< Fractional part of seconds. Integer number of 2^-32 seconds. */
/**
* Reverses the Endianness of the timestamp.
* Network byte order is big endian, so it needs to be switched before
* sending or reading.
*/
void ReverseEndian(void);
/**
* Convert to time_t.
* Returns the integer part of the timestamp in unix time_t format,
* which is seconds since Jan 1, 1970.
*/
time_t to_time_t(void);
};
/**
* A Network Time Protocol Message.
* From [RFC 5905](http://tools.ietf.org/html/rfc5905).
*/
struct NTPMessage {
unsigned int mode :3; /**< Mode of the message sender. 3 = Client, 4 = Server */
unsigned int version :2; /**< Protocol version. Should be set to 3. */
unsigned int leap :2; /**< Leap seconds warning. See the [RFC](http://tools.ietf.org/html/rfc5905#section-7.3) */
unsigned char stratum; /**< Servers between client and physical timekeeper. 1 = Server is Connected to Physical Source. 0 = Unknown. */
unsigned char poll; /**< Max Poll Rate. In log2 seconds. */
unsigned char precision; /**< Precision of the clock. In log2 seconds. */
unsigned int sync_distance; /**< Round-trip to reference clock. NTP Short Format. */
unsigned int drift_rate; /**< Dispersion to reference clock. NTP Short Format. */
unsigned char ref_clock_id[4]; /**< Reference ID. For Stratum 1 devices, a 4-byte string. For other devices, 4-byte IP address. */
Timestamp ref; /**< Reference Timestamp. The time when the system clock was last updated. */
Timestamp orig; /**< Origin Timestamp. Send time of the request. Copied from the request. */
Timestamp rx; /**< Recieve Timestamp. Reciept time of the request. */
Timestamp tx; /**< Transmit Timestamp. Send time of the response. If only a single time is needed, use this one. */
/**
* Reverses the Endianness of all the timestamps.
* Network byte order is big endian, so they need to be switched before
* sending and after reading.
*
* Maintaining them in little endian makes them easier to work with
* locally, though.
*/
void ReverseEndian(void);
/**
* Recieve an NTPMessage.
* Overwrites this object with values from the received packet.
*/
int recv(int sock);
/**
* Send an NTPMessage.
*/
int sendto(int sock, struct sockaddr_in* srv_addr);
/**
* Zero all the values.
*/
void clear();
};
#endif /* __NTP_CLIENT_H__ */
被测设备上的防火墙和防病毒软件已被禁用。
谢谢。
编辑
根据要求,这是main的正文:
WSADATA wsaData;
DWORD ret = WSAStartup(MAKEWORD(2,0), &wsaData);
char *host = "pool.ntp.org"; /* Don't distribute stuff pointing here, it's not polite. */
//char *host = "time.nist.gov"; /* This one's probably ok, but can get grumpy about request rates during debugging. */
NTPMessage msg;
/* Important, if you don't set the version/mode, the server will ignore you. */
msg.clear();
msg.version = 3;
msg.mode = 3 /* client */;
NTPMessage response;
response.clear();
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in srv_addr;
memset(&srv_addr, 0, sizeof(srv_addr));
dns_lookup(host, &srv_addr); /* Helper function defined below. */
msg.sendto(sock, &srv_addr);
response.recv(sock);
time_t t = response.tx.to_time_t();
char *s = ctime(&t);
printf("The time is %s.", s);
WSACleanup();
参考文献
【问题讨论】:
-
这不是一个真正的答案,但您知道这是内置于 Windows 中的,对吧?控制面板 -> 日期和时间 -> 互联网时间。
-
是的,我知道这一点。我正在构建一个简单的启动应用程序,该应用程序执行一系列健全性检查并支持大量命令行选项,具体取决于它所部署的板。每次关机都会重置系统时钟,每次开机都需要与特定的 NTP 服务器自动同步。
-
您是否尝试过使用诸如 Wireshark 之类的程序来检查/验证您的程序生成的数据包的正确性,或者检查程序是否开始成功发送这些数据包?
-
@JAB 是的。传出的数据包似乎是正确的 AFAIK,但我似乎没有收到任何响应。我假设我的数据包格式不正确,或者到目前为止我完全丢失了一些东西。
-
消息是如何组装的?您必须发送适当的信息才能获得回复。请。提供设置
msg结构的代码。
标签: c++ windows visual-c++ time ntp