10

我已经建立了一个 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 数据类型的签名和大小,不是吗?(谢谢和抱歉)

4

4 回答 4

16

First, I think you need to decide if you want to deal with the issue that the C standard leaves the meaning of time_t overly vague (it's not necessarily represented in seconds and doesn't even have any meaningful numerical properties like order/comparison). This is contrary to the behavior of every existing and historical implementation, where time_t is in seconds. Both C and POSIX also allow time_t to be a floating point type; as far as I know, no implementations make use of this, and on POSIX it would be rather harmful since the value of time_t has to be an integer the way it's used in struct timespec, etc.

If you decide you're happy assuming time_t is always an integral number of seconds since the epoch, i.e. the values are meaningful for interchange between systems, then it's just a matter of formatting them. The safest thing to do would be to simply cast to a integer type that's large enough to store any meaningful value and that's the same size on all systems: that would be int64_t. Then use whatever normal means you use for serializing int64_t in a way that's immune to endian differences.

If on the other hand you want to be "absolutely" portable, you should compute your own time_t value for "the epoch" (either the standard one or your own choice of epoch), then use difftime to convert to a double representing "seconds since the epoch", and format the double with snprintf(buf, sizeof buf, "%.0f", diff). Note that computing a time_t value for the epoch in portable C is actually quite difficult because most of the standard functions work in local time, whereas you need universal time. There are tricks you can do with the gmtime, mktime, and localtime functions to figure it out, but it's nontrivial...

于 2012-12-19T17:00:25.033 回答
4

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

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

于 2012-12-19T16:57:41.887 回答
3

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

于 2012-12-19T16:58:28.650 回答
0

更新:似乎有一个库可以提供您正在寻找的内容,请查看Apache Portable Runtime,特别是有关时间例程的此页面。除此之外,我会说我的答案仍然提供了一种手动实现此功能的方法,前提是所有系统都符合 POSIX.1-2001。

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

用于localtime将从您的调用中获得的数据转换timestruct 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 

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

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

    1. 将接收到的数据转换成客户端字节序
    2. 写入a struct tm(如果涉及时区,将接收到的时区数据写入a long
    3. 如果涉及时区,将接收到的时区数据写入along并计算时区差异(计算接收到的数据与本地timezone调用得到的差异......),然后调整在步骤2中创建的变量。tzsetstruct tm
    4. 获得struct tm调整到客户端本地时区的一个,用于mktime将数据转换回一个time_t
    5. 考虑时区差异,如果有的话。
于 2018-03-13T21:00:46.717 回答