Open adminfriso opened 1 year ago
So i have rewritten it to work with open frameworks on both mac and windows and i would like to share my code
the main thing i overhauled was changing the udp requests to an openframeworks specific connector
NtpClient.cpp
#include "NtpClient.h"
NTPClient::NTPClient(string hostname, uint16_t port) : hostname_(hostname), port_(port)
{
}
void NTPClient::build_connection()
{
cout << "Creating socket with: " << hostname_ << endl;
ofxUDPSettings settings;
settings.sendTo(hostname_, port_);
settings.blocking = true;
udpConnection.Setup(settings);
udpConnection.SetTimeoutReceive(1);
}
NTPClient::~NTPClient() {
udpConnection.Close();
}
uint64_t NTPClient::request_time()
{
int response; // return result from writing/reading from the socket
build_connection();
std::cout << "Connecting\n";
udpConnection.Connect(hostname_.c_str(), port_);
NTPPacket packet = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
packet.li_vn_mode = 0x1b;
std::cout << "Sending request\n";
response = udpConnection.Send((char *)&packet, sizeof(NTPPacket));
if (response < 0)
{
std::cerr << "ERROR writing to socket\n";
return 0;
}
std::cout << "Reading request\n";
response = udpConnection.Receive((char *)&packet, sizeof(NTPPacket));
if (response < 0)
{
std::cerr << "ERROR reading from socket\n";
close_socket();
return 0;
}
// These two fields contain the time-stamp seconds as the packet left the NTP
// server. The number of seconds correspond to the seconds passed since 1900.
// ntohl() converts the bit/byte order from the network's to host's
// "endianness".
packet.transmited_timestamp_sec = ntohl(packet.transmited_timestamp_sec); // Time-stamp seconds.
packet.transmited_timestamp_sec_frac = ntohl(packet.transmited_timestamp_sec_frac); // Time-stamp fraction of a second.
// Extract the 32 bits that represent the time-stamp seconds (since NTP epoch)
// from when the packet left the server. Subtract 70 years worth of seconds
// from the seconds since 1900. This leaves the seconds since the UNIX epoch
// of 1970.
// (1900)---------(1970)**********(Time Packet Left the Server)
// seconds since UNIX epoch
uint32_t txTm = packet.transmited_timestamp_sec - NTP_TIMESTAMP_DELTA;
// convert seconds to milliseconds
double milliseconds = (double)txTm * 1000l;
return milliseconds;
}
void NTPClient::close_socket()
{
udpConnection.Close();
}
NtpClient.h
#pragma once
#include "ofMain.h"
#include "ofxNetwork.h"
struct NTPPacket
{
uint8_t li_vn_mode; // Eight bits. li, vn, and mode.
// li. Two bits. Leap indicator.
// vn. Three bits. Version number of the protocol.
// mode. Three bits. Client will pick mode 3 for client.
// Eight bits. Stratum level of the local clock.
uint8_t stratum;
// Eight bits. Maximum interval between successive messages.
uint8_t poll;
// Eight bits. Precision of the local clock.
uint8_t precision;
// 32 bits. Total round trip delay time.
uint32_t rootDelay;
// 32 bits. Max error aloud from primary clock source.
uint32_t root_dispersion;
// 32 bits. Reference clock identifier.
uint32_t ref_id;
// 32 bits. Reference time-stamp seconds.
uint32_t ref_timestamp_sec;
// 32 bits. Reference time-stamp fraction of a second.
uint32_t ref_timestamp_sec_frac;
// 32 bits. Originate time-stamp seconds.
uint32_t orig_timestamp_sec;
// 32 bits. Originate time-stamp fraction of a second.
uint32_t orig_timestamp_sec_frac;
// 32 bits. Received time-stamp seconds.
uint32_t received_timestamp_sec;
// 32 bits. Received time-stamp fraction of a second.
uint32_t received_timestamp_sec_frac;
// 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
uint32_t transmited_timestamp_sec;
// 32 bits. Transmit time-stamp fraction of a second.
uint32_t transmited_timestamp_sec_frac;
};
struct NTPClient
{
NTPClient(string host, uint16_t port);
~NTPClient();
/**
* @brief Transmits an NTP request to the defined server and returns the
* timestamp
*
* @return (uint64_t) the number of milliseconds since 1970. Return 0 if fail.
*/
uint64_t request_time();
private:
ofxUDPManager udpConnection;
/**
* @brief Converts from hostname to ip address
*
* @param hostname name of the host.
* @return ip address. Return empty string if coun't find the ip.
*/
string hostname_to_ip(const std::string &hostname);
/// @brief Build the connection. Set all the params for the socket_client.
void build_connection();
/// @brief Close the connection. Set -1 to socket_fd.
void close_socket();
/// @brief NTP server IP address
string hostname_;
/// @brief NTP server port
size_t port_;
/// @brief Socket file descriptor
int socket_fd;
/// @brief Server address data structure
struct sockaddr_in socket_client;
/// @brief Delta between epoch time and ntp time
static constexpr unsigned long long NTP_TIMESTAMP_DELTA{2208988800ull};
};
caller method
time_t getNtpTime()
{
int port = 123;
string host = "YourHost.com";
cout << "--- Sync NTP ----" << endl;
cout << host << endl;
NTPClient client{host, static_cast<uint16_t>(port)};
auto epoch_server_ms = client.request_time();
if (0 == epoch_server_ms)
{
cout << "Error requesting server time" << endl;
}
// The function ctime receives the timestamps in seconds.
time_t epoch_server = (uint32_t)(epoch_server_ms / 1000);
cout << "Server time: " << ctime(&epoch_server);
cout << "Timestamp server: " << (uint32_t)epoch_server << "\n\n";
time_t local_time;
local_time = time(0);
cout << "System time is " << (epoch_server - local_time) << " seconds off\n";
cout << "--- Sync NTP Done ----" << endl;
return epoch_server_ms;
}
I have successfully implemented your NTP client in an openframeworks application and it works perfectly on mac.
However on windows it would not compile because it cannot find the libaries
include <sys/socket.h>
include <arpa/inet.h>
include
include
include <netinet/in.h>
have you experienced this already and maybe an idea for a solution?