【问题标题】:c/c++ - safest way to send time_t over socketc/c++ - 通过套接字发送 time_t 的最安全方法
【发布时间】:2012-12-19 16:54:25
【问题描述】:

我已经建立了一个 C++ 服务器/客户端环境,并试图将 time_t 值从服务器发送到客户端(在任何服务器中都是有用的东西)。但我遇到了一个头疼的问题:time_t 似乎没有任何尺寸规格。我想知道通过网络发送 time_t 的最安全(更便携)的方式是什么。

这是一个小例子:

time_t T = time(NULL);
unsigned char * P = (unsigned char *)&T;
// ... Convert it to network byte order, etc.

// Here, 's' would be the socket, and 'S'
// the size of the data that is going to
// be sent
send(s, P, S, 0);

我有两个问题:

  • time_t 大小不固定
  • time_t 可以是有符号或无符号整数数据类型的 typedef

如何处理这两个问题,以便可以在(几乎)任何架构之间安全地发送它?

提前致谢。

编辑:看到相同的三个答案后,我发现在这里回复更好。很抱歉我之前没有澄清这一点。我更喜欢以“纯”字节发送它,尽管将它作为字符串发送不是问题。据我了解,我需要(再次)记住主机系统中 time_t 数据类型的签名和大小,不是吗? (谢谢和抱歉)

【问题讨论】:

  • 以统一格式发送数据的大小(uint32_t,有人吗?),同时发送一个关于其签名的布尔值。
  • @H2CO3 如果time_t在服务端是8字节,在客户端是4字节呢?
  • 将值作为字符串发送会导致问题吗?
  • 提示:您可以查看 PTPv2(Precision Timing Protocol)实现 herehere 它是用于网络节点(例如服务器和客户端,...)之间精确时间同步的标准我想你可能会在那里找到最实用的答案!

标签: c++ c time network-programming


【解决方案1】:

首先,我认为您需要决定是否要处理 C 标准使 time_t 的含义过于模糊的问题(它不一定以秒表示,甚至没有任何有意义的数字属性,例如顺序/比较)。这与 每个 现有和历史实现的行为相反,time_t 以秒为单位。 C 和 POSIX 都允许 time_t 为浮点类型;据我所知,没有实现使用它,并且在 POSIX 上这将是相当有害的,因为 time_tvalue 必须是一个整数,就像它在 struct timespec 中使用的方式一样.

如果你决定你很高兴假设time_t 总是自纪元以来的整数秒,即对于系统之间的交换是有意义的,那么它只是格式化它们的问题.最安全的做法是简单地转换为一个整数类型,该类型足够大以存储任何有意义的值,并且在所有系统上都是相同的大小:那就是int64_t。然后使用不受字节序差异影响的任何正常方式对int64_t 进行序列化。

另一方面,如果您想“绝对”便携,您应该为“时代”计算自己的time_t 值(标准值或您自己选择的时代),然后使用difftime 来转换为表示“自纪元以来的秒数”的双精度数,并将double 格式化为snprintf(buf, sizeof buf, "%.0f", diff)。请注意,在便携式 C 中计算纪元的time_t 值实际上非常困难,因为大多数标准函数都在本地时间工作,而您需要通用时间。您可以使用 gmtimemktimelocaltime 函数来解决这个问题,但这并不简单......

【讨论】:

  • 这是我想写的答案的一个更好的版本。感谢您的光临。 +1
  • 谢谢。我还添加了一段关于“完全可移植”的方法。
  • @R.. 是的,因为 2008 Posix 需要整数类型,我相信。但是您说“没有实现”,而不是“没有 Posix 实现”,而 370 在我使用时肯定会使用 [d] 加倍。不知道还有没有人用;我当然不会。
  • 使用 32 位整数会非常愚蠢;与 1970 年代和 80 年代使用 2 位字符串的愚蠢程度大致相同。这是 2012 年。你有 64 位整数,它们对于宇宙的生命周期来说足够大。使用它们。
【解决方案2】:

您可以发送由strftimegmtime 组合生成的文本表示。该表示会比二进制表示稍大,但不会很大。例如,格式字符串 "%Y%j%H%M%S" 会生成一个 13 字节的时间表示(不包括 NUL 字符)。

编辑:忘记我之前使用ctime 的建议;它使用localtime,因此仅在客户端和服务器位于同一时区时才有效。显然,asctime 是不安全的,所以使用strftime

【讨论】:

  • asctime 已弃用,其指定方式会导致在遥远的过去或未来数年发生危险的缓冲区溢出。
  • @R..:很高兴知道。将我的建议更改为专门使用 strftime。这也提供了对字符串大小的控制,所以无论如何它会更好。
【解决方案3】:

您可以使用固定位数的文本形式发送它。这样您就不必担心符号、大小不兼容,甚至字节顺序。

【讨论】:

    【解决方案4】:

    更新:似乎有一个库可以提供您正在寻找的内容,请查看Apache Portable Runtime,特别是this page on time routines。除此之外,如果所有系统都符合 POSIX.1-2001,我想说我的回答仍然提供了一种手动实现的方法。

    我现在遇到了类似的问题,我认为最好有一些关于如何解决这个问题的说明。请注意,这里的解决方案应该与 POSIX.1-2001 兼容(在 Ubuntu 14.04 上,man tzsetman localtime 提供了此类信息,我还没有使用其他来源,真的)。

    使用localtime 将从您对time 的调用中获得的数据转换为struct tm(参见time.h):

    struct tm {
               int tm_sec;         /* seconds */
               int tm_min;         /* minutes */
               int tm_hour;        /* hours */
               int tm_mday;        /* day of the month */
               int tm_mon;         /* month */
               int tm_year;        /* year */
               int tm_wday;        /* day of the week */
               int tm_yday;        /* day in the year */
               int tm_isdst;       /* daylight saving time */
    };
    

    注意localtime 会将声明的变量设置为(再次参见time.h

    extern long timezone; //seconds West of UTC 
    

    由于现在所有类型都是已知的,因此您可以编写自己的转换工具来使该数据在网络上具有可移植性。需要考虑的事项:

    • (最好在您的软件初始化中),您必须确定您的intlong 有多少字节(当然使用sizeof
    • 基于该信息,您可以对struct tm 的每个整数字段进行主机到网络转换(使用htonlhtons - 对于64 位类型,您必须自己编写)和将该信息发送给客户端(以您喜欢的任何方式)。
      如果您有时区差异,您还需要转换/发送时区信息,即存储在变量timezone(见上文)。
    • 客户将不得不

      1. 将接收到的数据转换成客户端字节序
      2. 写入struct tm(如果涉及时区,将接收到的时区数据写入long
      3. 如果涉及时区,将接收到的时区数据写入long 并计算时区差异(计算接收到的timezone 数据与本地调用tzset 得到的差异) ...),然后调整在步骤 2 中创建的 struct tm 变量。
      4. 已获得调整为客户端本地时区的struct tm,使用mktime 将数据转换回time_t
      5. 考虑时区差异,如果有的话。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-18
      • 1970-01-01
      • 2016-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-17
      • 2018-05-23
      相关资源
      最近更新 更多