{"id":19891632,"url":"https://github.com/parezj/ntp-client","last_synced_at":"2025-05-02T18:31:23.665Z","repository":{"id":144165112,"uuid":"263266595","full_name":"parezj/NTP-Client","owner":"parezj","description":"NTP Client (Windows DLL/LIB + Console App + CVI GUI App) - small and accurate NTP client, easily integrable into any app","archived":false,"fork":false,"pushed_at":"2020-06-18T14:13:41.000Z","size":31055,"stargazers_count":43,"open_issues_count":4,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-07T04:41:14.836Z","etag":null,"topics":["client","cpp-library","cvi","library","ntp","windows","windows-dll"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/parezj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-12T07:37:21.000Z","updated_at":"2025-02-18T11:05:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"cf4ea1fd-c7ce-4120-a12e-da18f07e5e47","html_url":"https://github.com/parezj/NTP-Client","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parezj%2FNTP-Client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parezj%2FNTP-Client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parezj%2FNTP-Client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/parezj%2FNTP-Client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/parezj","download_url":"https://codeload.github.com/parezj/NTP-Client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252088422,"owners_count":21692794,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","cpp-library","cvi","library","ntp","windows","windows-dll"],"created_at":"2024-11-12T18:18:54.900Z","updated_at":"2025-05-02T18:31:23.652Z","avatar_url":"https://github.com/parezj.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NTP Client (Windows DLL/LIB + Console + CVI GUI)\r\n \r\n1. [Time Synchronization via NTP](#1-Time-Synchronization-via-NTP)  \r\n2. [NTP Client Library (C++ DLL)](#2-NTP-Client---Library-C++-DLL)  \r\n   2.1 [Description](#Description)  \r\n   2.2 [C++ interface](#C++-interface)  \r\n   2.3 [C interface](#C-interface)  \r\n   2.4 [Example of Use](#Example-of-Use)  \r\n3. [NTP client - Graphical Interface (CVI)](#3-NTP-client---Graphical-Interface-CVI)  \r\n\r\n## 1. Time Synchronization via NTP\r\n\r\nNTP has been used to synchronize time in variable response networks since\r\n1985 and that makes it one of the oldest Internet protocols. Uses UDP\r\nOSI layer 4 protocol and port 123. By default, it achieves an accuracy of 10 ms to 200\r\nµs, depending on the quality of the connection.\r\n\r\nNTP uses a hierarchical system called \"*stratum*\". Server of type *stratum* 0\r\nobtains the most accurate time, for example, from a cesium clock, but is not intended for\r\ntime distribution to the network. This is done by the server of type *stratum* 1, which it receives\r\ntime from *loss* 0. Then there are servers *stratum* 2 to 15, which always\r\nthey get the time from the parent server and their number basically shows\r\ndistance from the reference clock.\r\n\r\nThe NTP algorithm begins by sending a defined packet (RFC 5905), respectively\r\ndatagram, from client to server. The most important information transmitted by this packet\r\nare client mode (NTPv4), *stratum* local clock, accuracy of local clock,\r\nand especially the time **T1**, which indicates the time of the local clock at the time the packet leaves to\r\nnetworks. After the NTP server receives the packet, the server writes the time **T2** to it, which\r\nindicates the current time on the server clock and just before sending the time **T3**, which\r\nindicates the time the packet leaves back to the network. After receiving the packet by the client, it is finally\r\nwrites the last time **T4**, which indicates the arrival back to the client. if they are\r\nthese times are measured accurately, it is enough to calculate the two resulting ones thanks to the formulas below\r\nvalues. **Offset**, which symbolizes the shift of the client clock from the clock on the server and\r\n**Delay**, which represents the delay of the packet passing through the network, which can be due\r\nswitches and network technologies are highly variable. The sum of these values then\r\nrepresents the final shift of the local clock, which should ideally be\r\nequal to zero.\r\n\r\n\u003cdiv align=\"center\" margin=\"0\" padding=\"0\"\u003e\r\n\u003cimg src=\"https://raw.githubusercontent.com/parezj/NTP-Client/master/img/ntp_eq.png\"\u003e\r\n\u003c/div\u003e\r\n\r\n## 2. NTP Client - Library (C++ DLL)\r\n\r\n### 2.1 Description\r\n\r\nI developed a simple and single-purpose library in C ++ in the environment\r\nMicrosoft Visual Studio 2019. I only relied on the official RFC specification\r\n5905. The library is currently designed for Windows NT because it uses Win32\r\nAPI for reading and writing system time and *Winsock* for UDP communication. However\r\nin the future it is not a problem to extend it with directives \\ #ifdef eg with POSIX\r\n*sockets*.\r\n\r\nBecause the library contains only one **Client** class, the class diagram is\r\nunnecessary.\r\n\r\n```cpp\r\nclass Client: public IClient\r\n```\r\n\r\nThe library has only two public methods, **query** and\r\n**query_and_sync**.\r\n\r\n```cpp\r\nvirtual Status query (const char* hostname, ResultEx** result_out);\r\nvirtual Status query_and_sync (const char* hostname, ResultEx** result_out);\r\n```\r\n\r\nQuery is the core of the whole library. At the beginning of this method, a UDP is created first\r\npacket, it is filled with the current values ??I mentioned in the first chapter and\r\nsends it to the NTP server. Upon arrival, he completes the time T4 and performs the calculation, according to\r\nformulas from the first chapter. Times are represented by the time_point class from the library\r\nstd :: chrono with resolution to nanoseconds (time **t1**) or class\r\nhigh_resolution_clock (time **t1_b**).\r\n\r\n```cpp\r\ntypedef std::chrono::time_point\u003cstd::chrono::system_clock, std::chrono::nanoseconds\u003e time_point_t;\r\ntime_point_t t1 = std::chrono::time_point_cast\u003cstd::chrono::nanoseconds\u003e(std::chrono::system_clock::now());\r\nauto t1_b = std::chrono::high_resolution_clock::now();\r\n```\r\n\r\nThis combination is because the times in the first formula (offset) must be\r\nabsolute. These are the times **T2** and **T3** that came from the server. therefore not\r\nuse high_resolution_clock, in the second formula (delay) it is then possible to read **T1**\r\nfrom **T4** use relative times, which can be obtained using high_resolution_clock.\r\nThe following formulas show the calculation using this approach, all units\r\nvariables are nanoseconds.\r\n\r\n```cpp\r\ndouble offset = [(T2 - T1) + (T3 - T4)] / 2\r\ndouble delay = (T4b - T1b) - (T3 - T2)\r\n```\r\n\r\nBy summing the values *offset* and *delay* we get *delta*, ie the value by which\r\nadjust the local system clock. However, this only applies if the latter is used\r\npublic methods **query_and_sync**, the first mentioned will only communicate with\r\nserver and calculation.\r\n\r\nResulting calculated and obtained values, including *jitter* (stability indicators\r\nnetwork connection) is returned to the user either in the Result structure that is used for\r\nclassic C interface, or in the class ResultEx, which, unlike the first contains\r\ntime represented by class time_point_t, as opposed to time represented by classical\r\nTimePt structure with *integers*.\r\n\r\n```cpp\r\nstruct Result\r\n{\r\n    struct TimePt time;\r\n    struct Metrics mtr;\r\n};\r\n```\r\n```cpp\r\nclass ResultEx\r\n{\r\npublic:\r\n    time_point_t time;\r\n    Metrics mtr;\r\n};\r\n```\r\n\r\nThis achieves the compatibility between C and C ++, which is required for a dynamic library.\r\nIf the user uses the library directly from C ++, it is more convenient to work with time\r\nrepresented by the time_point_t class, otherwise there is no choice but to use it\r\nstructure.\r\n\r\n```cpp\r\nstruct TimePt\r\n{\r\n    int tm_nsec;\r\n    int tm_usec;\r\n    int tm_msec;\r\n    int tm_sec;\r\n    int tm_min;\r\n    int tm_hour\r\n    int tm_mday;\r\n    int tm_mon;\r\n    int tm_year;\r\n};\r\n```\r\n```cpp\r\nstruct Metrics\r\n{\r\n    double delay_ns;\r\n    double offset_ns;\r\n    double jitter_ns;\r\n    double delta_ns;\r\n};\r\n```\r\n\r\nError states are returned as *enumerator* Status, where 0 means success\r\n(similar to POSIX) and anything else is a bug.\r\n\r\n```cpp\r\nenum Status : int16_t\r\n{\r\n    OK = 0,\r\n    UNKNOWN_ERR = 1,\r\n    INIT_WINSOCK_ERR = 2,\r\n    CREATE_SOCKET_ERR = 3,\r\n    SEND_MSG_ERR = 4,\r\n    RECEIVE_MSG_ERR = 5,\r\n    RECEIVE_MSG_TIMEOUT = 6,\r\n    SET_WIN_TIME_ERR = 7,\r\n    ADMIN_RIGHTS_NEEDED = 8\r\n};\r\n```\r\n\r\nIn addition, the library contains several static stateless methods to facilitate the work\r\nprogrammer, used primarily to format results and convert types.\r\n\r\n### 2.2 C++ interface\r\n\r\nThe standard library interface for use with object-oriented languages is in\r\nform *interface*, which exposes the two main public methods described above\r\n**query** a **query_and_sync**. The interface is only a macro for the struct type,\r\nof course you could use a proprietary MS **__interface**, but most of the time it gets better\r\nstick to proven and compatible things.\r\n\r\n```cpp\r\nInterface IClient\r\n{\r\n    virtual Status query(const char* hostname, ResultEx** result_out) = 0;\r\n    virtual Status query_and_sync(const char* hostname, ResultEx**result_out) = 0;\r\n    virtual ~IClient() {};\r\n};\r\n```\r\n\r\n### 2.3 C interface\r\n\r\nThe interface usable for DLL calls must be compatible with classic ANSI C,\r\ninstead of classes, it is necessary to use the classic C OOP style, namely functions, structures and\r\n*opaque pointers*. These functions must then be exported using the EXPORT macro,\r\nwhich is a macro for **__declspec (dllexport)**. It is also necessary to set adequate\r\ncalling convention, in our case it is **__cdecl**, where the one calling as well\r\ncleans the tray.\r\n\r\nThe **Client__create** function creates a library instance, which is represented\r\npointer, or macro, HNTP, which in the context of Windows is called\r\n*handle*.\r\n\r\n```cpp\r\ntypedef void* HNTP;\r\n```\r\n\r\nOther functions, such as **Client__query** or **Client__query_and_sync**\r\nthey take this indicator as the first argument. The rest is very similar to C++\r\ninterface, however one difference it has. Instead of delete, it must be called at the end\r\n**Client__free_result** and **Client__close**.\r\n\r\n```cpp\r\nextern \"C\"\r\n{\r\n    /* object lifecycle */\r\n    EXPORT HNTP __cdecl Client__create(void);\r\n    EXPORT void __cdecl Client__close(HNTP self);\r\n    \r\n    /* main NTP server query functions */\r\n    EXPORT enum Status __cdecl Client__query(HNTP self, const char* hostname, struct Result** result_out);\r\n    EXPORT enum Status __cdecl Client__query_and_sync(HNTP self, const char* hostname, struct Result** result_out);\r\n    \r\n    /* helper functions */\r\n    EXPORT void __cdecl Client__format_info_str(struct Result* result, char* str_out);\r\n    EXPORT void __cdecl Client__get_status_str(enum Status status, char* str_out);\r\n    EXPORT void __cdecl Client__free_result(struct Result* result);\r\n}\r\n```\r\n\r\n### 2.4 Example of Use\r\n\r\nYou must have *runtime* **vc_redist** (2015-19) installed to run. Code\r\nit is at least partially annotated and perhaps even clear. I tried to make it\r\nuse trivial. A client instance is created, the *query* function is called, and it terminates\r\nthe client. This can be done in an infinite loop with a defined interval,\r\nto ensure constant time synchronization. The following lines are excluded\r\nfrom a console application that serves as an example of use.\r\n\r\n```cpp\r\nenum Status s;\r\nstruct Result* result = nullptr;\r\nHNTP client = Client__create()\r\ns = Client__query_and_sync(client, \"195.113.144.201\", \u0026result);\r\nClient__free_result(result);\r\nClient__close(client);\r\n```\r\n\r\n![Console](https://raw.githubusercontent.com/parezj/NTP-Client/master/img/ntp_client_console2.png)\r\n\r\n## 3. NTP client - Graphical Interface (CVI)\r\n\r\nI used the dynamic library in the LabWindows / CVI environment to create\r\ngraphical interface of the NTP client, which is periodically called from its own thread. On\r\ngraph we then see the green delta value (the current difference of the local clock from\r\nserver), its diameter in yellow and *jitter* in network communication in red. To run\r\n**CVI Runtime 2019** is required.\r\n\r\n![CVI GUI](https://raw.githubusercontent.com/parezj/NTP-Client/master/img/ntp_client_gui.png)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparezj%2Fntp-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fparezj%2Fntp-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparezj%2Fntp-client/lists"}