【问题标题】:send int over socket: signed vs unsigned通过套接字发送 int:有符号与无符号
【发布时间】:2014-09-02 17:17:17
【问题描述】:

假设我想通过网络发送 4 字节整数。由于使用来自stdint 的类型,整数具有固定大小。 我的问题是:我是否尝试使用这 4 个字节发送有符号或无符号整数? (假设我在客户端和服务器端都使用相同的方法将整数序列化/反序列化到/从字节)。会不会有其他问题? (我也没有提到字节序问题)

【问题讨论】:

  • 签名只是解释。
  • @MitchWheat:我不知道我在某处看到这样的评论感到困惑:“有符号整数也可以,除非你的机器不使用二进制补码表示。”。 (特别是这里:stackoverflow.com/questions/8000851/…
  • 只有一个可能的问题:发送者和接收者的解释不同。请参阅endianness 以及,因为您询问有符号数、补码、补码和符号和大小。

标签: c++ c


【解决方案1】:

这个问题很少得到应有的重视。

正如 Floris 所观察到的,只有表示的字节会被发送。 C 和 C++ 定义了无符号数的按位表示*,而不是有符号数,因此将有符号数作为字节发送会造成兼容性差距。

“修复”传输格式很容易。将有符号的 int 转换为其对应的无符号类型可以保证生成二进制补码表示。但是如何转换回来?当你想要一个负数时,将一个无符号整数转换为其有符号对应物会产生有符号整数溢出,这会产生一个未指定的结果——你可以得到任何东西。

为了真正安全,请使用分支:

signed int deserialize_sint( unsigned int nonnegative ) {
    if ( nonnegative < INT_MAX ) return nonnegative;
    else return - (int) ( - nonnegative ); // Only cast an unsigned number < INT_MAX
}

如果运气好,编译器会发现两种情况相同,并消除分支。

上面的函数是用C写的;向 C++ 人群道歉。

如果你想更加偏执,你可以在执行强制转换之前检查- nonnegative &lt; INT_MAX,因为二进制补码中的最大负数仍然会溢出一个补码机器。对于 nonnegative == - nonnegative 的情况,您可以做的最好的事情是返回一个更广泛的类型,或者如果这是不可能的,则标记一个运行时错误。

* 但是,当位被分成一个字节序列时,字节序变得不明确。

【讨论】:

  • 我无法理解这一点。 -(int)(-nonnegative) 何时给出与 (int)nonnegative 不同的结果?
  • @Nairou 转换为 int 被定义为保留数值。因此 is 永远不能从正值产生明确定义的负值。双重否定避免了数值转换的溢出。
  • 我知道这样做是因为“有符号溢出”未定义,但这是否意味着“无符号溢出”和“无符号到有符号强制转换”(或“由于无符号强制转换导致的有符号下溢”)都定义了吗?
  • @Nairou 没错,没有无符号溢出之类的东西,因为- 运算符被定义为执行二进制补码否定,从而产生较小的正值。然后无符号到有符号的转换很好,因为正值足够小,可以在 int 中。
【解决方案2】:

因为标准不要求有符号类型的特定表示:

3.9.1 基本类型 [basic.fundamental] n3936 的第 7 段

类型bool、char、char16_t、char32_t、wchar_t,有符号和无符号整数类型统称为整数类型。整数类型的同义词是整数类型。整数类型的表示应使用纯二进制计数系统定义值。 [示例:本国际标准允许整数类型的 2 的补码、1 的补码和有符号幅度表示。 ——结束示例]

以二进制表示形式发送带符号整数值的定义并不明确(除非您将其明确指定为协议的一部分并进行一些手动工作以确保您知道如何读取/写入该二进制表示形式)。

根据具体要求,有几种解决方案。

  • 如果速度不是主要考虑因素,那么您可以使用英语(您选择的替代语言)表示并将整数序列化到文本/从文本序列化。对于很多问题,这不是一个糟糕的解决方案,因为主要的减速不是序列化成本,而是网络延迟。在大多数情况下(但并非总是如此),网络延迟是主要问题。
  • 因此,如果您需要二进制表示(因为您对其进行了计时,并且您的数字的体积/密度需要它)。那么由于htonl() 和家人,字节序问题并不难解决。它涵盖了所有无符号整数类型(至少 16/32 位值)。
    • 因此,您真正需要解决的只是有符号值的表示。所以选择一个(对您使用的机器使用最常见的表示,然后翻译通常是无操作的)。但是,如果您知道在线表示(因为它在您的协议中指定),那么您可以在本机不支持此表示的机器上转换为/从此表示(通常此成本很小(有条件的添加))。

【讨论】:

  • 好吧,除了跟踪:大小和字节序 - 还必须注意符号问题?后来就不知道了。在这方面似乎使用 unsigned int 更安全?
  • @dmcr_code 是的,我认为这总结得差不多了。
  • @Floris:所以如果使用 unsigned int,这个符号问题就不会再出现了吗?
  • @dmcr_code : 是的,无符号整数的表示是由标准固定的,只要双方就字节顺序和大小达成一致,当 is 没有符号时,您就可以避免符号问题...
【解决方案3】:

当您通过套接字发送数字时,它只是字节。

现在如果你想发送一个负数,而负数在接收端的表示是不同的,那么你可能会遇到问题。否则,它只是字节。

因此,如果在接收端有可能会误解负数的二进制表示,那么您需要进行一些翻译(可能发送一个符号字节后跟四个幅度字节,并将它们放在一起另一端)。

不过这不太可能。

【讨论】:

  • @Floris:我明白了,就这样?从这个意义上说,通过网络发送时只使用无符号整数更安全?
  • 不,必须以已知顺序发送多字节值的字节。正是出于这个原因,有一些函数可以在 C 中将整数值与“网络字节顺序”相互转换。
  • 使用htonl() and family 将整数与主机特定的表示进行转换。
  • @LokiAstari:我指的不是字节序问题。更多与标志相关的问题;因为在一些广受欢迎的答案中没有提到这一点,例如:stackoverflow.com/questions/1577161/…
猜你喜欢
  • 2016-12-14
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 2017-10-09
  • 2011-11-25
  • 2015-11-19
  • 2014-02-09
相关资源
最近更新 更多