将 uint16_t 转换为 char[2] 以通过套接字(unix)发送

Posted

技术标签:

【中文标题】将 uint16_t 转换为 char[2] 以通过套接字(unix)发送【英文标题】:Convert a uint16_t to char[2] to be sent over socket (unix) 【发布时间】:2012-10-28 01:23:50 【问题描述】:

我知道在这方面大致有一些事情。但是我的大脑很痛,我找不到任何东西来完成这项工作……

我正在尝试通过 unix 套接字发送一个 16 位无符号整数。为此,我需要将 uint16_t 转换为两个字符,然后我需要在连接的另一端读取它们并将其转换回来转换为 unsigned int 或 uint16_t,此时使用 2bytes 或 4bytes 都没有关系(我正在运行 64bit,这就是为什么我不能使用 unsigned int :)

我在 C 中这样做

谢谢

【问题讨论】:

我从大学回家后会试一试:\ 这学期真是地狱,一切都变得糟糕起来......我有很多延期,现在我所有的作业都在学习休息......太糟糕了学习无法获得扩展=.=必须去大学进行物理修订讨论感谢大家的快速响应 您确定要使用 plain char 而不是 unsigned char - 或者更好的 uint8_t?跨度> 【参考方案1】:

基本上,您通过套接字发送 2 个字节,这就是套接字需要知道的所有内容,不管字节顺序、符号等...只需将您的 uint16 分解为 2 个字节并通过套接字发送。

char byte0 = u16 & 0xFF;
char byte1 = u16 >> 8;

在另一端以相反的方式进行转换

【讨论】:

这是正确的,但高字节的'& 0xFF'是不必要的。当然,它会被优化掉,但它是多余的,可能会让人类感到困惑。 您还应该强制转换 - 特别是在您的示例中,您将一个 UNSIGNED 值放入 CHAR 中(取决于实现)可能是有符号或无符号的。最好使用 unsigned charuint8_t 将 0-0xff 转换为有符号字符可能会溢出,这在技术上是未定义的行为,应该避免。 当你只需要一个原始字节 [0x00-0xFF] 并且你对它的整数表示不感兴趣时​​,没有这样的“溢出”概念。未定义的行为?什么?用 0xFF 屏蔽会导致未定义的行为吗?你是认真的吗?【参考方案2】:

为什么不用掩码和移位将其分解为字节?

 uint16_t value = 12345;
 char lo = value & 0xFF;
 char hi = value >> 8;

(编辑)

在另一端,你用相反的方式组装:

 uint16_t value = lo | uint16_t(hi) << 8;

在我的脑海中,不确定是否需要演员表。

【讨论】:

好吧,这个答案让我省了很多痛苦,即使在你写完之后我用了一年多。谢谢。 一年后它仍然有用,所以不要停止传播良好的氛围 对不起,我忘记了这个问题...在我的个人资料上做了一些春季大扫除,注意到了这一点,然后就去了 oopsies :( 从一百万年前我就给了这个正确的答案,这确实像我做的那样敲响了警钟......而且每个人似乎都喜欢这个答案。对不起@StevenSudit 我可能做错了,但反过来对36345 这样的大数不起作用,除非我先将lo 转换为uint8_tuint16_t value = (uint8_t)lo | hi &lt;&lt; 8;【参考方案3】:
char* pUint16 = (char*)&u16;

即转换uint16_t的地址。

char c16[2];
uint16_t ui16 = 0xdead;
memcpy( c16, ui16, 2 );

c16 现在包含 u16 的 2 个字节。在远端,您可以简单地反转该过程。

char* pC16 = /*blah*/
uint16_t ui16;
memcpy( &ui16, pC16, 2 );

有趣的是,尽管调用了 memcpy,但几乎每个编译器都会对其进行优化,因为它的大小是固定的。

正如 Steven sudt 指出的那样,您可能会遇到大端序问题。为了解决这个问题,您可以使用 htons(host-to-network short)函数。

uint16_t ui16correct = htons( 0xdead );

在远端使用 ntohs(网络到主机的简称)

uint16_t ui16correct = ntohs( ui16 );

在 little-endian 机器上,这会将 short-endian 转换为 big-endian,然后在远端从 big-endian 转换回来。在大端机器上,这两个函数什么都不做。

当然,如果您知道网络上两台机器的架构使用相同的字节序,那么您可以避免这一步。

查找 ntohl 和 htonl 以处理 32 位整数。大多数平台也支持 64 位的 ntohll 和 htonll。

【讨论】:

好的,这将为您提供一个指向 int16 的 char 指针,但您在第一个元素中找到的内容将取决于 big-endian 与 small-endian。 你说得对,memcpy 通常会被优化掉,但这仍然系统依赖于大端/小端。 好的,这样更好,但对于非常简单的事情来说,ntohs() 似乎确实有很多开销。 它真的不是......在许多平台上它是一条指令。另一方面,它只是一个指令修饰符......我不会担心它,tbh。 @StevenSudit 如果函数被内联,函数调用开销为0,任何合理的编译器都会内联单行函数,它不是“巨大的”。【参考方案4】:

听起来你需要使用bit mask and shift operators。

将一个 16 位数字拆分为两个 8 位数字:

您使用按位与运算符(C 中的 &)屏蔽低 8 位,以便高 8 位全部变为 0,然后将该结果分配给一个字符。 您使用右移位运算符(C 中的 >>)将高 8 位右移,这样低 8 位都被推出整数,只留下高 8 位,并将其分配给另一个字符.

然后,当您通过连接发送这两个字符时,您会执行相反的操作:将过去的前 8 位向左移动 8 位,然后使用按位或将其与其他 8 位组合。

【讨论】:

以上是关于将 uint16_t 转换为 char[2] 以通过套接字(unix)发送的主要内容,如果未能解决你的问题,请参考以下文章

如何将 uint32_t 数字转换为 char[8]?

将 uint8_t 数组转换为 C 中的 uint16_t 值

防止将 uint64_t 转换为 uint16_t

将 int16_t 变量转换为 uint8_t 以传递给函数

如何将 32 字符(0/1)的序列转换为 32 位(uint32_t)?

为啥在 gcc 和 clang 上通过优化将大 double 转换为 uint16_t 会给出不同的答案