3

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!

rfreytag
  • 1,203
  • 11
  • 18

1 Answers1

3

Ì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.

Community
  • 1
  • 1
alk
  • 69,737
  • 10
  • 105
  • 255