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 底层数据和字节顺序