Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Time-server time type issue

so I'm writing a little time server-client application in C on linux that's supposed to send the current unix time stamp to the client.

It works all fine and all, but I've been told that time_t might not always be the same size and byte order. How do I make sure I send a time that the client will always understand?

As of now I just do

time_t now = htonl(time(0));

and send that.

I searched on google and stackoverflow but it seems everyone else just sends the time string generated by ctime() or strftime().

Thanks in advance!

like image 822
rfreytag Avatar asked Aug 24 '26 00:08

rfreytag


1 Answers

Ìn general sending binary data is error-prone, due the possiblity of the different ways of its interpretation on the sender and the receiver side.

For time_t in particular it isn't even clear how many bits would be involved, it may be 32 or 64 or even something much more complicated, as time_t might even be implemented as a struct.

In your special case using htonl(), 32 bits are assumed, as htonl() takes a 32 bit value.

So the fail-safe solution indeed is to send out a text representation of the system time.

Programmatically this might look like this:

char st[64] = "";

{
  struct * tm = gmtime(time(NULL));
  if (NULL == tm)
  {
    fprintf(stderr, "gmtime() failed\n");
  }
  {
    if(0 == strftime(st, sizeof(st), "%s", tm)) /* Prints the text representaiotn of the seconds since Epoch into st. */
    {
      fprintf(stderr, "strftime() failed\n");
    }
  }
}

To reverse this operation you could use strptime():

char st[64] = "123456789123";
time_t t;
memset(&t, 0, sizeof(t));
{
  struct tm = {0};
  char p = strptime(t, "%s", &tm);
  if (NULL == p || p != (t + strlen(t)))
  {
    fprintf(stderr, "strptime() failed\n");
  }
  else
  {
    t = mktime(&tm);
  }
}

The nice thing using strptime() and strftime() is that you a can easily changed the format of the date/time in transit, by just modifying the format as specified when calling those two functions.

Changing "%s" to "%Y-%m-%d %H:%M:%S" would transfer the time like "2014-05-20 13:14:15".


However if you really want to send seconds since Epoch in binary format and stay fail-safe and portable you need to take care of three things:

  1. Get the number of seconds since Epoch in a portable way.
  2. Select an integer type definitly large enough.
  3. Convert this "large" value to network byte order.

A possible approach for this would be:

#include <time.h>
#include <inttypes.h> /* For uint64_t, as 64bit should do to represent the seconds since Epoch for the next few years. */

...

time_t t_epochbegin;
memset(&t_epochbegin, 0, sizeof(t_epochbegin);
uint64_t t_host = (uint64_t) difftime(time(NULL), t_epochbegin); /* Get the seconds since Epoch without relying on time_t being an integer. */
uint64_t t_network = htonll(t_host); /* Convert to network byte order. */

On how to implement the non-standard htonll() see the various answers to this question: Big Endian and Little Endian support for byte ordering


All code in the examples above assumes the system the code runs on provides a timer and though calls to time() wouldn't fail.

like image 192
alk Avatar answered Aug 27 '26 00:08

alk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!