将数据转换为big-endian



我使用WinSock向服务器发送UDP数据包,我需要用big-endian发送数据。我不知道在发送之前如何转换结构的字节顺序。

我有一个这样的结构:

struct ConnectIn
{
    std::int64_t ConnectionID = 0x41727101980;
    std::int32_t Action = 0;
    std::int32_t TransactionID;
    ConnectIn(std::int32_t transactionID)
    {
        TransactionID = transactionID;
    }
};

现在我发送的是这样的:

ConnectIn msg(123);
int len = sizeof(msg);
int bytesSent = sendto(s, (char*)&msg, len, 0, (SOCKADDR*)&dest, sizeof(address));

如何在发送之前将msg的字节顺序转换为big-endian

如果你好奇的话,我发送的数据是针对Bit Torrent UDP跟踪器协议的。

如果您想手动执行此操作,那么您需要分别交换每个成员。您可以将成员从主机的字节顺序转换为网络的字节顺序。在Win32上,htonll()用于64位整数,htonl()用于32位整数:

#include <Winsock2.h>
ConnectIn msg(123);
msg.ConnectionID = htonll(msg.ConnectionID);
msg.Action = htonl(msg.Action);
msg.TransactionID= htonl(msg.TransactionID);

然后,您可能还希望单独发送成员,以避免依赖于主机系统的结构布局。Windows ABI不会在这个结构中插入任何填充,但对于其他结构,您可能会使用它。所以这里有一个基本的想法:

char buf[sizeof msg.ConnectionID + sizeof msg.Action + sizeof msg.TransactionID];
char *bufi = buf;
std::memcpy(bufi, &msg.ConnectionID, sizeof msg.ConnectionID);
bufi += sizeof msg.ConnectionID;
std::memcpy(bufi, &msg.Action, sizeof msg.Action);
bufi += sizeof msg.Action;
std::memcpy(bufi, &msg.TransactionID, sizeof msg.TransactionID);
bufi += sizeof msg.TransactionID;
int len = sizeof buf;
int bytesSent = sendto(s, buf, len, 0, (SOCKADDR*)&dest, sizeof(address));

然后在接收端使用适用于64位和32位类型的ntoh*()函数,将网络的字节顺序转换为接收主机的字节顺序。

是的,网络字节顺序(NBO)是Big Endian,因此您需要找到在web上发送该结构的方法。

您当前正在做的操作不起作用:您正在发送整个结构,但接收器可能有不同的endianness、padding等。
最简单的选择是:

  • 使用协议定义的布局发送每个字段
  • 处理序列化的第三部分库:GoogleProtobuf是最常见的库之一

对于第一个选项,在Winsock2库中有一些函数负责处理这个问题。这些是:

  • (WSA)ntoht(网络到主机t,其中t可以是shortunsigned
  • (WSA)htont(主机到网络t,其中t可以是shortunsigned

WSA函数有点不同,仅适用于Windows。


网络编程指南
Winsock参考

一个选项是单独转换每个数字的

对于GCC:

int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)

对于MSVC:

unsigned short _byteswap_ushort(unsigned short value);
unsigned long _byteswap_ulong(unsigned long value);
unsigned __int64 _byteswap_uint64(unsigned __int64 value);

最新更新