c / c ++ – 通过套接字发送time_t的最安全的方法
我已经建立了一个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数据类型的签名和大小,不是吗? (谢谢,对不起)
首先,我认为您需要决定是否要处理C标准留下time_t
含义过于模糊的问题(它不一定以秒表示,甚至没有任何有意义的数字属性,如顺序/比较)。 这与每个现有和历史实现的行为相反,其中time_t
以秒为单位。 C和POSIX都允许time_t
为浮点类型; 据我所知,没有实现可以使用它,并且在POSIX上它会相当有害,因为time_t
的值必须是它在struct timespec
使用的整数等等。
如果你认为你很高兴假设time_t
总是自纪元以来的整数秒,即这些值对于系统之间的交换是有意义的,那么它只是格式化它们的问题。 最安全的做法是简单地转换为一个足够大的整数类型来存储任何有意义的值,并且在所有系统上都是相同的大小:即int64_t
。 然后使用正常的方法,以一种不受endian差异影响的方式序列化int64_t
。
另一方面,如果你想要“绝对”可移植,你应该为“纪元”(标准的或你自己选择的纪元)计算你自己的time_t
值,然后使用difftime
转换为代表“秒”的双difftime
从纪元“开始,用snprintf(buf, sizeof buf, "%.0f", diff)
格式化double
snprintf(buf, sizeof buf, "%.0f", diff)
。 请注意,为便携式C中的纪元计算time_t
值实际上非常困难,因为大多数标准函数在本地时间工作,而您需要通用时间。 你可以使用gmtime
, mktime
和localtime
函数来解决这个问题,但它非常重要……
您可以将strftime
生成的文本表示与gmtime
结合使用。 表示将比二进制表示稍大,但不是很大。 例如,格式字符串"%Y%j%H%M%S"
产生时刻的13字节表示(不包括NUL字符)。
编辑 :忘记我之前使用ctime
建议; 使用localtime
,因此仅在客户端和服务器位于同一时区时才有效。 显然, asctime
是不安全的,所以使用strftime
。
您可以使用固定位数的文本forms发送它。 然后,您不必担心标志,大小不兼容,甚至字节顺序。
更新:似乎有一个库提供了您正在寻找的内容,请查看Apache Portable Runtime ,特别是按时间例程的页面 。 除此之外,我会说我的答案仍然提供了一种手动实现的方法,前提是所有系统都符合POSIX.1-2001标准。
我现在遇到类似的问题,我认为如何通过这方面的说明可能会很好。 请注意,这里的解决方案应该与POSIX.1-2001兼容(在Ubuntu 14.04上, man tzset
和man localtime
提供了这样的信息,而且我还没有使用其他来源,真的)。
使用localtime
将从调用中获得的数据转换为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
由于现在已知所有类型,您可以编写自己的转换工具,以使此数据可以移植。 需要考虑的事项:
- (最好在你的软件init中),你必须确定你的
int
和long
有多少字节(当然使用sizeof
) - 基于该信息,您可以对
struct tm
每个整数字段进行主机到网络转换(对于64位类型,使用htonl
或htons
,您必须自己编写)并将该信息发送给客户端(在无论你喜欢什么方式)。
如果您有时区差异,您还需要转换/发送时区信息,即存储在可变timezone
的数据(见上文)。 -
然后客户必须
- 将接收的数据转换为客户端字节顺序
- 将其写入
struct tm
(如果涉及时区,则将接收的时区数据写入long
) - 如果涉及时区 ,则将接收到的时区数据写入
long
并计算时区差异(计算接收到的timezone
数据的差异以及通过本地调用tzset
得到的数据…),然后调整在struct tm
创建的struct tm
变量第2步。 - 获得调整到客户端本地时区的
struct tm
后,使用mktime
将数据转换回time_t
- 考虑时区差异,如果有的话。