在C#中将2个字节转换为短整型

29
我正在尝试将两个字节转换为无符号短整型,以便检索实际的服务器端口值。我基于协议规范下的回复格式进行操作。我尝试使用BitConverter.ToUInt16()来实现这一点,但问题是它似乎没有返回期望的值。请参见以下示例实现:
int bytesRead = 0;

while (bytesRead < ms.Length)
{
    int first = ms.ReadByte() & 0xFF;
    int second = ms.ReadByte() & 0xFF;
    int third = ms.ReadByte() & 0xFF;
    int fourth = ms.ReadByte() & 0xFF;
    int port1 = ms.ReadByte();
    int port2 = ms.ReadByte();
    int actualPort = BitConverter.ToUInt16(new byte[2] {(byte)port1 , (byte)port2 }, 0);
    string ip = String.Format("{0}.{1}.{2}.{3}:{4}-{5} = {6}", first, second, third, fourth, port1, port2, actualPort);
    Debug.WriteLine(ip);
    bytesRead += 6;
}

假设有一组样本数据,其中包含两个字节值105和135,预期的转换后端口值应该是27015,但使用BitConverter得到的值却是34665。

我是否操作错误?

3个回答

37
如果你在BitConverter调用中颠倒数值,就会得到预期的结果:
int actualPort = BitConverter.ToUInt16(new byte[2] {(byte)port2 , (byte)port1 }, 0);

在小端架构中,低位字节需要在数组的第二个位置。正如lasseespeholt在评论中指出的那样,在大端架构中,您需要反转顺序。这可以通过BitConverter.IsLittleEndian属性检查。或者总体上使用IPAddress.HostToNetworkOrder可能是更好的解决方案(首先转换值,然后调用该方法将字节按正确的顺序放置,无论字节顺序如何)。


1
你确定如果应用程序在不同的架构上运行,这个会起作用吗? - Lasse Espeholt
@lasseespeholt:这是一个很好的观点。可能需要使用IsLittleEndian进行检查。对于大端架构,顺序应该与OP中给出的顺序相同。 - Mark Wilkins
@BrokenGlass的解决方案是:在速度关键的情况下,应该首选ushort value2 = (ushort)(port1 + (port2 << 8)); 这种方式,因为使用BitConverter和分配额外数组可能会很慢。 - shelbypereira

13

BitConverter 做的是正确的事情,你只是把低字节和高字节混淆了 - 你可以手动使用位移验证:

byte port1 = 105;
byte port2 = 135;

ushort value = BitConverter.ToUInt16(new byte[2] { (byte)port1, (byte)port2 }, 0);
ushort value2 = (ushort)(port1 + (port2 << 8)); //same output

3
手动操作的方法得到了加分。在我看来,BitConverter 进行了太多的检查和端序检查,这可能会破坏代码。 - Lasse Espeholt
从您的解决方案中:ushort value2 = (ushort)(port1 + (port2 << 8)); //相同的输出,这是大多数情况下需要进行此类型转换的方式,并且速度很可能成为一个问题。我目前在图像应用程序中使用它。创建数组和使用BitConverter的开销可能会很大。 - shelbypereira
以免其他人走上我曾经走过的路。在 C#中,小于16位的算术运算会被转换为16位,以避免操作期间的溢出。 因此,value2操作将port1和(port2 << 8)转换为int,将它们相加,然后将其强制转换回ushort。因此,@shelypereira可能正确,BitConverter具有开销,基本数学运算也一样,只是看不见而已。请参阅以下堆栈溢出链接:https://dev59.com/nmkw5IYBdhLWcg3wMHum - Jroonk

11

为了在小端和大端架构上工作,你必须像这样做:

if (BitConverter.IsLittleEndian)
    actualPort = BitConverter.ToUInt16(new byte[2] {(byte)port2 , (byte)port1 }, 0);
else
    actualPort = BitConverter.ToUInt16(new byte[2] {(byte)port1 , (byte)port2 }, 0);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接