C/通过套接字发送time_t的最安全方法
c/safest way to send time_t over socket
>我已经设置了一个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
。然后使用您用于序列化int64_t
的任何正常方法,以不受字节序差异的影响。
另一方面,如果您想"绝对"便携,您应该计算自己的"纪元"time_t
值(标准纪元或您自己选择的纪元),然后使用difftime
转换为表示"自纪元以来的秒数"的双精度,并用snprintf(buf, sizeof buf, "%.0f", diff)
格式化double
。请注意,在可移植 C 中计算纪元的time_t
值实际上非常困难,因为大多数标准函数都以本地时间工作,而您需要通用时间。你可以用gmtime
、mktime
和localtime
函数来做一些技巧来解决这个问题,但这并不平凡......
您可以发送由strftime
与gmtime
结合使用生成的文本表示。该表示形式将比二进制表示形式大一点,但并不大。例如,格式字符串"%Y%j%H%M%S"
生成时刻的 13 字节表示形式(不包括 NUL 字符)。
编辑:忘记我之前使用ctime
的建议;它使用localtime
,因此只有在客户端和服务器在同一时区时才有效。显然,asctime
是不安全的,所以使用strftime
.
您可以以文本形式发送它,具有固定数量的数字。这样,您就不必担心符号,大小不兼容甚至字节顺序。
更新:似乎有一个库可以提供您正在寻找的内容,请查看 Apache Portable Runtime,特别是这个关于时间例程的页面。除此之外,我想说我的回答仍然提供了一种手动实现的方法,前提是所有系统都符合 POSIX.1-2001。
我现在遇到了类似的问题,我认为有一些关于如何解决这个问题的说明可能会很好。请注意,这里的解决方案应该是 POSIX.1-2001 兼容的(在 Ubuntu 14.04 上,man tzset
和man 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
由于现在已知所有类型,因此您可以编写自己的转换工具以使此数据网络可移植。需要考虑的事项:
- (最好在您的软件 init 中),您必须确定您的
int
和long
有多少字节(当然,使用sizeof
) - 根据该信息,您可以对
struct tm
的每个整数字段进行主机到网络的转换(使用htonl
或htons
- 对于 64 位类型,您必须编写自己的)并将该信息发送到客户端(以您喜欢的任何方式)。
如果您有时区差异,您还需要转换/发送时区信息,即存储在变量timezone
中的数据(见上文)。 然后,客户将不得不
- 将接收到的数据转换为客户端字节顺序
- 写入
struct tm
(如果涉及时区,请将收到的时区数据写入long
) - 如果涉及时区,请将接收到的时区数据写入
long
并计算时区差(计算接收到的timezone
数据的差异以及通过在本地调用tzset
得到的内容...),然后调整在步骤 2 中创建的struct tm
变量。 - 获得根据客户端本地时区调整的
struct tm
后,使用mktime
将数据转换回time_t
- 考虑时区差异(如果有)。
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 将传入的网络"char*"数据转换为"uint8_t"并返回的安全方法是什么?
- 使用 std::vector::swap 方法在C++中交换两个不同的向量是否安全?
- 在 c++ 中从执行的 shell 命令获取返回状态的安全方法是什么?
- 并发安全堆栈接口方法:正确与否?
- 将积分类型的数组作为另一个不相关的积分类型的阵列进行访问的安全且符合标准的方法
- 有没有一种简单的方法来检查C++中的不安全表达式
- 用非零值初始化void指针的正确(或最安全)方法
- 使用范围解析运算符时,在构造函数中调用虚拟方法是否安全?
- 是否有一种安全的方法可以断言字符串视图是否以 null 终止?
- 在C++线程内实现多个计时器的最安全方法
- 解决方法:QPixmap:在GUI线程之外使用pixmap是不安全的
- 最有效的安全方法将 std::map<int, std::shared_ptr> 转换为 std::<Base>map<int, std::shared_ptr<D
- 在移出向量上调用 size() 方法是否安全?
- 重新分配指针阵列的一部分的安全方法
- 最快和/或最安全的方法,在C 中加入两个宽字符串
- 一种安全、符合标准的方法,使类模板专用化仅在实例化时才无法使用"static_assert"进行编译
- 这种线程间通信方法安全吗?
- 这种加密方法安全吗
- 构造一个临时对象并调用一个返回指针的方法——安全吗?