首页 > 解决方案 > Boost Asio 访问 asio::ip::address 底层数据和字节顺序

问题描述

我的目标是为所有 IP 地址 - 端口对创建一个唯一 ID。跨系统的 UID 必须相同(不同字节序系统没有冲突)。IPV4 UID 的大小为 6 个字节,而 ipv6 为 18 个字节。

uint8_t sourcePair[18];     /*ipv4=(4+2) bytes or ipv6=(16+2) bytes*/

我有两个函数将获取套接字的远程端点并获取所需的 UID。设计如下。

void CmdInterpreter::makeSourcePairV4(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, unsigned char(&binSourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v4();
    auto ipBin = addressClass.to_uint();
    memcpy(&binSourcePair[0], &ipBin, 4);
    memcpy(&binSourcePair[4], &portNum, 2);
}

void CmdInterpreter::makeSourcePairV6(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, unsigned char(&binSourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v6();
    auto ipBin = addressClass.to_bytes();
    memcpy(&binSourcePair[0], &ipBin[0], 16);
    memcpy(&binSourcePair[16], &portNum, 2);
}

这就是这些函数的调用方式

remoteEp = socketPtr->remote_endpoint();
if (remoteEp.address().is_v4())
    CmdInterpreter::makeSourcePairV4(remoteEp, remoteEp.port(), sourcePair);
else
    CmdInterpreter::makeSourcePairV6(remoteEp, remoteEp.port(), sourcePair);

这里的问题是访问 IPv6 底层数据的唯一方法是使用 to_byte() ,它将以网络字节顺序提供数据。另外,我正在做一个无符号短的内存复制,它的长度是多字节的。这行得通吗?这是一种安全的方式吗?他们有什么解决方法吗?

标签: c++boostnetwork-programmingboost-asioasyncsocket

解决方案


void CmdInterpreter::makeSourcePairV4(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, uint8_t(&sourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v4();
    auto ipBin = addressClass.to_bytes();
    memcpy(&sourcePair[0], &ipBin[0], 4);

    #ifdef BOOST_ENDIAN_LITTLE_BYTE
    byteSwap(portNum);
    #endif
    memcpy(&sourcePair[4], &portNum, 2);
}

void CmdInterpreter::makeSourcePairV6(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, uint8_t(&sourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v6();
    auto ipBin = addressClass.to_bytes();
    memcpy(&sourcePair[0], &ipBin[0], 16);
    #ifdef BOOST_ENDIAN_LITTLE_BYTE
    byteSwap(portNum);
    #endif
    memcpy(&sourcePair[16], &portNum, 2);
}

对于 IPv4 和 IPv6 地址,使用 to_byte() 函数以大端格式获取远程端点地址。对于 little-endian 主机,端口号会产生字节顺序问题,可以通过交换字节来解决。要将其编码为 base 64,我使用了 cppcodec 库。

UID = cppcodec::base64_rfc4648::encode(sourcePair, 6);
UID = cppcodec::base64_rfc4648::encode(sourcePair, 18);

用于交换端口号的模板函数是:

template <typename T>
void byteSwap(T& portNumber)
{
    char* startIndex = static_cast<char*>((void*)&portNumber);
    char* endIndex = startIndex + sizeof(T);
    std::reverse(startIndex, endIndex);
}

推荐阅读