Проблема с типом времени сервера времени

поэтому я пишу небольшое клиент-серверное приложение времени на C в Linux, которое должно отправлять текущую отметку времени unix клиенту.

Он работает нормально и все такое, но мне сказали, что time_t может не всегда иметь одинаковый размер и порядок байтов. Как мне убедиться, что я отправляю время, которое клиент всегда поймет?

На данный момент я просто делаю

time_t now = htonl(time(0));

и отправить это.

Я искал в Google и stackoverflow, но кажется, что все остальные просто отправляют строку времени, сгенерированную ctime() или strftime().

Заранее спасибо!


person rfreytag    schedule 26.06.2014    source источник


Ответы (1)


В общем случае отправка двоичных данных подвержена ошибкам из-за возможности различных способов их интерпретации на стороне отправителя и получателя.

В частности, для time_t даже неясно, сколько битов будет задействовано, это может быть 32 или 64 или даже что-то гораздо более сложное, поскольку time_t может даже быть реализовано как struct.

В вашем особом случае с использованием htonl() предполагается 32 бита, как htonl() принимает 32-битное значение.

Таким образом, надежное решение действительно состоит в том, чтобы отправить текстовое представление системного времени.

Программно это может выглядеть так:

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

Чтобы отменить эту операцию, вы можете использовать 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);
  }
}

Преимущество использования strptime() и strftime() заключается в том, что вы можете легко изменить формат даты/времени в пути, просто изменив формат, указанный при вызове этих двух функций.

Изменение "%s" на "%Y-%m-%d %H:%M:%S" перенесет время как "2014-05-20 13:14:15".


Однако, если вы действительно хотите отправлять секунды с начала эпохи в двоичном формате и оставаться безотказным и переносимым, вам нужно позаботиться о трех вещах:

  1. Получите количество секунд с начала эпохи портативным способом.
  2. Выберите целочисленный тип определенно достаточно большим.
  3. Преобразуйте это «большое» значение в сетевой порядок байтов.

Возможный подход для этого:

#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. */

О том, как реализовать нестандартный htonll(), см. различные ответы на этот вопрос: поддержка Big Endian и Little Endian для порядка байтов


Весь код в приведенных выше примерах предполагает, что система, в которой выполняется код, предоставляет таймер, и хотя вызовы time() не завершатся ошибкой.

person alk    schedule 26.06.2014
comment
Большое спасибо! Это больше, чем я просил, если бы я мог, я бы проголосовал за вас сейчас! :) - person rfreytag; 26.06.2014