c / c ++ - 通过套接字发送time_t的最安全的方法

时间:2012-12-19 16:54:26

标签: c++ c time network-programming

我已经建立了一个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 个答案:

答案 0 :(得分:15)

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

如果您认为自己很高兴假设struct timespec始终是自纪元以来的整数秒,即对系统之间的交换有意义,那么它只是一个问题格式化它们。最安全的做法是简单地转换为足够大的整数类型来存储任何有意义的值,并且在所有系统上都是相同的大小:那将是time_t。然后使用您用于序列化int64_t的任何常规方法,使其免受端序差异的影响。

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:3)

您可以使用固定位数的文本形式发送。然后,您不必担心标志,大小不兼容,甚至字节顺序。

答案 3 :(得分:0)

更新:似乎有一个库可以提供您想要的内容,请查看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. 考虑时区差异,如果有的话。