I am quite a fan of the D programming language and I think it deserves more attention, even if since a few months it’s becoming more and more popular, as it gained top20 in the TIOBE Index for February 2020.
As an experiment in network programming, I took this simple NTP client written in C and translated to D ; in my opinion while it’s keeping the low-level nature, it’s shorter, clearer and more effective. It’s only a dozen lines of code, but full program is available on my github; stars and contributions are welcome!
Starting from the top, we find the needed imports and then the packet structure, as is specified in the reference implementation (it’s a matter of copy-paste). Notice that in D we can initialize the structure field with tne value we need.
import std.stdio;
import std.socket;
import std.datetime;
import std.bitmanip;
// from reference
struct Packet {
align(1): // we want the structure packed, with no gaps
byte flags=0x23; // Flags 00|100|011 for li=0, vn=4, mode=3
byte stratum;
byte poll;
byte precision;
uint root_delay;
uint root_dispersion;
uint referenceID;
uint ref_ts_secs;
uint ref_ts_frac;
uint origin_ts_secs;
uint origin_ts_frac;
ubyte[4] recv_ts_secs; // This is what we need mostly to get current time.
ubyte[4] recv_ts_fracs; // for this example nanoseconds can be dropped
uint transmit_ts_secs;
uint transmit_ts_frac;
}
when the main function begins, we create a new UDP socket, with protocol IPV4, and allocate on the stack. Then we tell the socket to connect to a NTP server of the ’europe’ pool. Of course you can change it to any address you prefer :)
const ntpEpochOffset = 2208988800L; // Difference between Jan 1, 1900 and Jan 1, 1970
void main()
{
auto sock=new UdpSocket(AddressFamily.INET);
Packet packet; // stack allocation
sock.connect(new InternetAddress("europe.pool.ntp.org",123));
Here it comes the trickiest part: the send method of the UdpSocket class requires a slice, and returns the number of bytes effectively transferred; but we have to send a struct, so we need to cast our packet as a 1-element slice:
const sent=sock.send((&packet)[0..1]);
const received=sock.receive((&packet)[0..1]);
if (sent!=Packet.sizeof || received!=Packet.sizeof) {
writeln("Hmmm .. Something went wrong");
}
The last piece is straightforward, thanks also to Phobos, the powerful D standard library: we convert the received bytes to our native bit-order representation (notice the template syntax) and translate from unix Epoch to human-readable date and time. Every variable type is inferred automagically for us :)
sock.close();
// network byte order is Big-Endian
auto unixTime=bigEndianToNative!uint(packet.recv_ts_secs);
// NTP returns seconds from Jan 1, 1900
auto stdTime = SysTime.fromUnixTime(unixTime-ntpEpochOffset);
writeln("Hello, the time is: ",stdTime);