cited words
I installed two systems on the machine, one Linux Ubuntu, one Windows8.1. The depressing thing is that every time you restart from Ubuntu into Windows, the system time is always less than 8 hours, each time with the Windows program to synchronize, which is the following:
This thing is actually an NTP Client, select an NTP Server from the Internet, get UTC time, and then set the local time.
So I want to implement a program like this, Baidu first, there are a lot of information on the NTP and implementation code, mostly a single platform, not cross-platform
, here are a few references:
http://blog.csdn.net/loongee/article/details/24271129
http://blog.csdn.net/chexlong/article/details/6963541
Http://www.cnblogs.com/TianFang/archive/2011/12/20/2294603.html
This article uses the boost ASIO to implement NTP Client across platforms.
Get ready
1. The latest boost library, this article uses the 1.56.0 version
To use the ASIO network library inside.
2. IDE is visual Studio with Update3
The author is the version of the emperor
3. Wireshark is also the latest 1.12.1 version
NTP Client to analyze Windows comes with
NTP Packet Analysis
Here we analyze is that program, click Update Now, will send the request packet NTP, the following is the Wireshark capture results:
You can get some of the following information:
NTP time synchronization is divided into two processes, a request, a response
The IP address of the NTP server here is 129.6.15.28
The program does not have DNS resolution, it is possible to save the IP address directly
The port number of the NTP service is 123,client also uses 123 Port , later found that the client is not bound to use the 123 port
The NTP protocol is an application protocol built on the UDP transport protocol .
The V3 version of the NTP protocol is used here, and there are V4
Well, with some basic information about the NTP protocol, let's take a look at the application tier details:
Response Package:
A lot of fields, about the meaning of each field, please refer to the above-mentioned link, this article mainly about implementation. Here Reference timestamp is the timestamp sent by the request package, and Origin,receive,transmit is returned from the server, the last three times are very small, so it's convenient to take a final transmit timestamp as a result.
? Coding ?
The compilation of the relevant libraries in boost can refer to the official documentation, which has a very simple example.
1. Required header files and namespaces
#include <iostream> #include "boost/asio.hpp" #include "boost/date_time/posix_time/posix_time.hpp" using Namespace boost::p osix_time;using namespace Boost::asio::ip;
2. Structure of the Ntppacket
Class ntppacket {public: ntppacket () { _rep._flags = 0xdb; // 11.. .... Leap Indicator: unknown &NBSP;&NBSP;&NBSP;&NBSP;//&NBSP;&NBSP;&NBSP;&NBSP, .... 01 1... ntp version 3 // .... .011 Mode: client _rep._pcs = 0x00;//unspecified _ rep._ppt = 0x01; _rep._pcp = 0x01; _rep._rdy = 0x01000000;//big-endian _rep._rdn = 0x01000000; &nBsp; _rep._rid = 0x00000000; _rep._ ret = 0x0; _rep._ort = 0x0; _rep._rct = 0x0; _rep._trt = 0x0; } friend std::ostream& operator<< (Std::ostream& os, const ntppacket& ntpacket) { return os.write (reinterpret_cast<const char *> (& NTPACKET._REP), sizeof (Ntpacket._rep)); &NBSP;&NBSP;&NBSP;&NBSP;}&NBSP;&NBSP;&NBSP;&NBSP;FRIEND&NBSP;STD:: Istream& operator>> (Std::istream& is, ntppacket& ntpacket) { return is.read (reinterpret_cast<char*> (&ntpacket._rep ), sizeof (NTPACket._rep)); }public: #pragma pack (1) struct ntpheader { uint8_t _flags;//Flags uint8_t _pcs;//Peer Clock Stratum uint8_t _ppt;//Peer Polling Interval uint8_t _pcp;//peer clock precision uint32 _t _rdy;//root delay uint32_t _rdn;//root dispersion uint32_t _rid;//reference id uint64_t _ret;//Reference Timestamp uint64_t _ort;//Origin Timestamp uint64_t _rct;//receiVe timestamp uint64_t _trt;//transmit timestamp }; #pragma pack () NtpHeader _rep;};
Here, in order to facilitate access, the struct is not placed in private, it is important to note that the structure of the various fields of the order and the need for memory 1-byte alignment, even with:
#pragma pack (1)
Memory alignment is important in network programming and directly affects the content of packet, which can be referenced in memory alignment:
Http://www.cppblog.com/cc/archive/2006/08/01/10765.html
The most important thing in the NTP request package is the flags, which contain version information that directly affects the content of the protocol work, and therefore cannot be mistaken.
Two operator overloads are used to facilitate the reading and writing of packet data.
Looking at the implementation of the client class, the main task of the client class is to send and receive the NTP packet and return the last 64bit timestamp.
Class ntpclient {public: ntpclient (Const std::string& serverip) :_socket (IO), _serverip (ServerIP) { } time_t gettime () { if (_socket.is_open ()) { _socket.shutdown (UDP::SOCKET::SHUTDOWN_BOTH,&NBSP;_EC); if (_EC) { std::cout << _ec.message () << std::endl; _socket.close (); return 0; } _socket.close (); } udp::endpoint ep (Boost::asio::ip::address_v4::from_string (_serverip), NTP_PORT); NtpPacket request; std::stringstream ss; std::string buf; ss << request; ss >> buf; _socket.open (Udp::v4 ()); _socket.send_to (Boost::asio::buffer (BUF), &NBSP;EP); std::array<uint8_t, 128> recv; siZe_t len = _socket.receive_from (Boost::asio::buffer (recv), &NBSP;EP); uint8_t* pbytes = recv.data (); /****dump hex data for (size_t i = 0; i < len; i++) { if (i % 16 == 0) { std::cout << std::endl; } &NBSP;&NBSP;ELSE&NBSP;{&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;STD::COUT&NBSP;<<&NBSP;STD::SETW ( 2) << std::setfill (' 0 ') << std::hex << (uint32_t) pBytes[i]; std::cout << ' '; } } ****/ time_t tt; uint64_t last; uint32_t seconds; /****get the last 8 bytes (Transmit timestamp) from received packet. std::memcpy (&last, pbytes + len - 8, sizeof (last)); ****create a NtpPacket*/ ntppacket resonpse; std::stringstream rss ; rss.write (Reinterpret_cast<const char*> (pBytes), len); rss >> resonpse; last = resonpse._rep._trt; // reversebyteorder (last); seconds = (last & 0x7fffffff00000000) >> 32; tt = seconds + 8 * 3600 * 2 - 61533950; return tt; }private: const uint16_t ntp_port = 123; udp::socket _socket ; std::string _serverip; boost::system::error_code _ec ;};
Note several places:
1. Udp::socket is a socket using the UDP protocol in boost, and his construction requires a io_service,io_service that can be declared directly in the global zone:
Boost::asio::io_service io;
2. Create an address that endpoint uses to represent the NTP server:
Udp::endpoint EP (Boost::asio::ip::address_v4::from_string (_serverip), ntp_port);
Send_to to this EP and Receive_from packets from this EP.
3. time_t is defined as follows:
typedef __time64_t time_t; /* Time value */typedef __int64 __time64_t; /* 64-bit Time value */
That is to say, this time_t is actually a 64bit int, we can use uint64_t this type to swap with, he can use to represent a timestamp.
4. There are two ways to get the last 8 bytes of content, one is to copy pbytes memory directly, one is to construct the Ntppacket, and then take the member, here the latter is easy to understand.
5. Problem with byte order
Network byte order is the big-endian mode, need to be converted, because only need the last uint64_t so I wrote a byte-order conversion function for 64bit:
static void reversebyteorder (uint64_t &in) { uint64_t rs = 0; int len = sizeof (uint64_t); for (int i = 0; i < len; i++) { std::memset (reinterpret_cast< Uint8_t*> (&RS) + len - 1 - i , static_cast<uint8_t > ((in & 0xffll << (i * 8)) >> i * 8 ) , 1); } in = rs;}
The high 32 bits of the last 64bit content are stored in UTC seconds, so it needs to be removed and converted to the number of seconds in the local time zone.
Seconds = (last & 0x7fffffff00000000) >> 32;
Note that the highest bit is not available, although it is unsigned, as to why 61533950 this is the author on their own computer to try to find a lot of information is not
Know what is the problem, but also ask everyone to know the reader told me ha.
Then take a look at the main function:
int main (int argc, char* agrv[]) {NtpClient ntp ("129.6.15.28"); int n = 5; while (n--) {time_t TT = Ntp.gettime (); Boost::p osix_time::p time UTC = from_time_t (TT); Std::cout << "Local Timestamp:" << time (0) << ' \ t ' << "NTP Server:" << tt << "(" <& Lt To_simple_string (UTC) << ")" << Std::endl; Sleep (10); } return 0;}
This makes 5 NTP requests and uses boost to_simple_string to convert UTC time to print results.
This is probably the effect:
Closure
Synchronization time is generally expected to find an HTTP API interface, this article is mainly using the NTP protocol. To cross-platform, the above code avoids using the platform-related macros and functions as much as possible, as long as modifications can be performed on a variety of platforms, as well as the convenience of the robust quasi-standard library of boost for developers.
C + + uses boost to implement network time Protocol (NTP) clients