Ускорение доступа Asio к asio :: ip :: address лежащим в основе данным и упорядочению байтов - PullRequest
0 голосов
/ 10 апреля 2020

Моя цель - создать уникальный идентификатор для всей пары IP-адрес - порт. UID должен быть одинаковым во всех системах (не должно конфликтовать для разных систем с прямым порядком байтов). Размер UID IPV4 составляет 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 (), который выдаст данные в сетевом порядке байтов. Кроме того, я делаю memcopy в unsigned short, который является многобайтовым по длине. Это работает? Это безопасный способ? Есть ли какие-нибудь обходные пути?

1 Ответ

0 голосов
/ 10 апреля 2020
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 (), чтобы получить адрес удаленной конечной точки в формате с прямым порядком байтов. Для хоста с прямым порядком байтов номер порта создаст проблему с порядком байтов, которая может быть исправлена ​​путем замены байтов. Чтобы закодировать его в base 64, я использовал библиотеку cppcode c.

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);
}
...