plusangel / NTP-client

NTP client in C++ (compatible with chrony)
MIT License
31 stars 10 forks source link

Wont work on windows/ Rewrite done for openFrameworks #2

Open adminfriso opened 1 year ago

adminfriso commented 1 year ago

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?

adminfriso commented 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

adminfriso commented 1 year ago

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;
    }