Boost Asio 访问 asio::ip::address 底层数据和字节顺序

Posted

技术标签:

【中文标题】Boost Asio 访问 asio::ip::address 底层数据和字节顺序【英文标题】:Boost Asio accessing asio::ip::address underlying data and byte ordering 【发布时间】:2020-04-10 01:12:25 【问题描述】:

我的目标是为所有 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() ,它将以网络字节顺序提供数据。另外,我正在做一个无符号短的内存复制,它的长度是多字节的。这行得通吗?这是一种安全的方式吗?他们有什么解决方法吗?

【问题讨论】:

唯一想到的问题是端口号是本机字节顺序的,并且您没有显示可能位于 IPv4 二进制缓冲区末尾的垃圾。 @ZuodianHu 感谢评论,有人说“memcpy() 不修改字节顺序”。如果这是真的,我只关心网络字节顺序中的 IPv6。 SourcePair 仅在 IPv4 的情况下使用 [0-5](它是硬编码的)。 【参考方案1】:
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);

【讨论】:

以上是关于Boost Asio 访问 asio::ip::address 底层数据和字节顺序的主要内容,如果未能解决你的问题,请参考以下文章

Boost Asio 访问 asio::ip::address 底层数据和字节顺序

boost::asio: “strand”类型的同步原语有啥名字吗?

Boost.Asio 的同步和并发数据结构模式

Boost asio——读写同时进行

Boost Asio总结class strand

asio核心概念和功能