使用 sntp (windows c++) 从服务器获取时间/日期
Posted
技术标签:
【中文标题】使用 sntp (windows c++) 从服务器获取时间/日期【英文标题】:Get time/date from server with sntp(windows c++) 【发布时间】:2012-10-22 12:31:23 【问题描述】:我正在寻找从服务器 (ntp.belnet.be) 获取时间和日期的 c/c++ 工作代码。它适用于 UDP 并使用端口 123。
有人可以帮忙吗?
//sending pakket
memset(&sntp_msg_header, 0, sizeof sntp_msg_header);
sntp_msg_header.flags = 27;
sntp_msg_header.originate_timestamp_secs = time(NULL);
// Get data in rxmsg
...
...
// print time
timeval = ntohl(rxmsg.transmit_timestamp_secs) - ((70ul * 365ul + 17ul) * 86400ul);
printf("%s", ctime(&timeval));
这是我目前所拥有的。但我无法从中获得正确的数据。 我希望这是更多信息。
如果发现:
import socket
import struct
import sys
import time
TIME1970 = 2208988800L # Thanks to F.Lundh
client = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
data = '\x1b' + 47 * '\0'
client.sendto( data, ( sys.argv[1], 123 ))
data, address = client.recvfrom( 1024 )
if data:
print 'Response received from:', address
t = struct.unpack( '!12I', data )[10]
t -= TIME1970
print '\tTime=%s' % time.ctime(t)
但它是在 python 中。有人可以将其更改为 c++ 吗?或者是否有转换器?
【问题讨论】:
doolittle.icarus.com/ntpclient 如果你不介意调整 linux c++,这是工作代码***.com/a/19835285/1166727 【参考方案1】:这是我使用的。它不是很优雅,但应该足以让这个想法得到理解。基本思想是具有与网络发送和接收的内容相匹配的结构。颠倒的字节顺序最初并不明显。这里值得一提的是,这对于错误情况几乎没有任何用处。你想解决这个问题。 :)
#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()
ReverseEndianInt(seconds);
ReverseEndianInt(fraction);
/**
* 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()
return (seconds - ((70 * 365 + 17) * 86400))&0x7fffffff;
;
/**
* 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()
ref.ReverseEndian();
orig.ReverseEndian();
rx.ReverseEndian();
tx.ReverseEndian();
/**
* Recieve an NTPMessage.
* Overwrites this object with values from the recieved packet.
*/
int recv(int sock)
int ret = ::recv(sock, (char*)this, sizeof(*this), 0);
ReverseEndian();
return ret;
/**
* Send an NTPMessage.
*/
int 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;
/**
* Zero all the values.
*/
void clear()
memset(this, 0, sizeof(*this));
;
你会像这样使用它,也许在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();
有很多方法可以获取 ip 和端口,但 getaddrinfo 在 Windows 上做得不错:
int dns_lookup(const char *host, sockaddr_in *out)
struct addrinfo *result;
int ret = getaddrinfo(host, "ntp", NULL, &result);
for (struct addrinfo *p = result; p; p = p->ai_next)
if (p->ai_family != AF_INET)
continue;
memcpy(out, p->ai_addr, sizeof(*out));
freeaddrinfo(result);
当然,该解决方案与搁浅的鲸鱼一样便携,但它适用于我™。
【讨论】:
可以使用ntohl
进行小端转换,便携。
是的,尽管这并不能解决关于 unsigned int 大小或位域或填充的精确性质的可移植性问题。不过,可能已经足够好了。归根结底,将对象转换为 char* 并在其上写入会有点“特别”。 :) (结构排序可能比我记得的更具体,但我认为它可以填充或重新排序位字段。)
@alficles 此代码有更新版本吗?我正在尝试解决rec()
无法解决我打开的另一个问题的问题:***.com/questions/20804145/…【参考方案2】:
C/++ 中的套接字不如大多数其他语言令人愉快,尤其是解释型和较新的编译型。
为什么不使用官方的实现
http://ntp.org/downloads.html当然,NTP 需要 MinGW,但是那里有一些库。
http://www.hillstone-software.com/hs_ntp_details.htm(不是免费的,但可以免费试用,不是必须的)自服务器 2000 版本以来,Windows 还内置了 NTP。我无法验证它,但它至少应该可以在 Vista 之后的系统上运行。
w32tm /help
(查找 /query
部分)
更多文档:http://technet.microsoft.com/en-us/library/cc773263%28v=ws.10%29.aspx
【讨论】:
我需要使用 ntp.belnet.be 服务器。所以我需要将它应用到我的代码中。我遇到的问题是读取我从服务器收到的 pakket。 @user1765216 除非你认为你可以比官方的 NTP 实现做得更好,否则我认为重新实现已经存在的东西是没有意义的。 NTP 通常没有任何预设服务器,因此使用您的 belnet 服务器应该不是问题。 我还是不明白。我如何使用它来创建一个 c++ 命令行程序,它只给我服务器的时间和日期?? @user1765216 我更新了答案。你为什么不从一开始就写这就是你想要的...... 我想做的是制作一个使用套接字的 c++ 命令行程序。我需要通过 sntp 连接到 ntp.belnet.be 服务器,这样我才能从服务器接收到一个包,该包给我时间和日期。我认为问题很清楚?以上是关于使用 sntp (windows c++) 从服务器获取时间/日期的主要内容,如果未能解决你的问题,请参考以下文章
我可以从使用 c++ 和 windows 连接到服务器的套接字获得啥信息?
QueryPerformanceCounter 或 GetSystemTimePreciseAsFileTime 使用 SNTP 时?