以正确的字节序发送 UDP 数据包

本文关键字:UDP 数据包 字节 | 更新日期: 2023-09-27 18:37:15

我很难理解网络字节顺序以及通过UDP发送和接收数据的顺序。我正在使用 C#。

我有一个结构持有:

message.start_id         = 0x7777CCCC;
message.message _id      = 0xBBB67000;
     more data

消息定义具有 [StructLayout(LayoutKind.Sequential)] 。我首先使用以下方法将结构转换为字节数组:

public byte[] StructureToByteArray(object obj)
{ 
    int len = Marshal.SizeOf(obj);
    byte[] arr = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.StructureToPtr(obj, ptr, true);
    Marshal.Copy(ptr, arr, 0, len);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

然后我检查字节序:

if (BitConverter.IsLittleEndian)
{
    Array.Reverse(packet);
}

然后我发送它

socket.SendTo(packet, endPoint);

当我发送这个时,接收端收到应用了正确字节序的消息,但字节顺序相反,所以在 wireshark 中查找我得到:

其余的...BBB67000 7777CCCC

当我期待:7777CCCC BBB67000 ....................其余的。

我所期望的是正确的,还是结构中的第一个字节是最后一个到达的字节是正常的?

以正确的字节序发送 UDP 数据包

我在理解网络字节排序以及通过 UDP 发送和接收数据的顺序时遇到问题

数据通过 UDP 发送和接收的字节顺序与提供给套接字的字节顺序完全相同。您只发送或接收字节数组,UDP 根本不会对数据报中的字节重新排序。

那么问题来了,如何处理字节序?

好吧,在许多情况下,答案是"没有"。首先,您今天遇到的大多数计算机都运行x86架构,并且始终使用小端序。在许多情况下,无论如何,您都可以控制两端,因此始终可以坚持其中一端。如果您的 API 有办法将内容与字节流相互转换,您可以这样做并直接使用 UDP 套接字发送和接收这些字节。

但是,是的,有时您需要能够以与运行程序的体系结构上本机支持的字节序不同的字节序处理传输数据。

在你的特定示例中,您似乎选择使用大端序作为协议的字节顺序(我从一点代码中推断出这一点......如果没有一个好的最小,完整和可验证的示例,就不可能确定)。这很好;最初的 BSD 套接字 API 包含"网络字节顺序"的概念,这是大端序。套接字库包括用于转换的函数,例如从"主机顺序"到"网络顺序"的 16 位整数并返回。

但请务必了解,字节序会影响数据中每个基元级别的数据。您不能一次反转整个字节数组,因为如果这样做,不仅会改变每个基元的字节序,还会改变基元本身的顺序。果然,你在 Wireshark 跟踪中看到了这一点:数据结构中的两个字段的顺序已经交换。

要正确处理字节序,您必须遍历数据结构的每个字段并单独交换字节。您将保留字节,short值(16 位)将交换其字节对,int值(32 位)将反转其四字节序列,long值(64 位)将反转 8 个字节,依此类推。

更复杂的

是,一些数据结构完全不受字节序的影响(例如UTF8编码的文本),而其他数据结构则具有更复杂的规则(例如Windows GUID/UUID,这是一个128位值,实际上被定义为复杂的数据结构,具有多个不同大小的字段)。

要记住的重要一点是,字节序始终应用于每个单独的基元数据值的级别,同时考虑到基元数据值使用的实际字节数。