Как получить из клиента буфер целых и строк и правильно их хранить? (cpp сервер, клиент Python) - PullRequest
0 голосов
/ 23 мая 2019

У меня есть простой сервер cpp, который получает буфер char * от клиента Python и распаковывает его для использования данных. клиент Python отправляет буфер, который включает в себя 2 «разных» типа данных - string и int. буфер должен выглядеть так - buffer representation

, что означает, что если клиент хочет отправить сообщение с кодом 200 и данными «ок», он должен будет отправить буфер [2002ok]. Но я решил, что клиент отправит буфер в виде символов.

Итак, буфер будет выглядеть так- [Èok]

(as = значение ascii 200, = значение ascii 2) (редактировать: я не знаю почему, но значение ASCII, равное 2, не может быть показано здесь)

Проблема в том, что когда я распаковываю 3 части буфера, они как-то искажаются.

вот моя сторона клиента (Python):

msg = chr(200) + chr(0) + chr(0) + chr(0) + chr(2) + "ok"
print(">>>>" + (msg)) 
sock.send((msg.encode()))

и вот моя сторона сервера (CPP):

           uint8_t  msgCode = helpMe.getCode(client_socket);
           std::cout << "The message code is " << static_cast<unsigned int>(msgCode) << std::endl;
           int DataLen = helpMe.getLength(client_socket);
           std::string StrData = helpMe.getString(client_socket, DataLen);

Вот функции «Помощника», которые я использовал (распаковывая данные):



using std::string;

uint8_t Helper::getCode(SOCKET sc)
{
    uint8_t code;
    getPartFromSocket(sc, reinterpret_cast<char*>(&code), sizeof(code), 0);
    return code;
}

uint32_t Helper::getLength(SOCKET sc)
{
        uint32_t length;
        getPartFromSocket(sc, reinterpret_cast<char*>(&length), sizeof(length), 0);
        return length;
}

std::string Helper::getString(SOCKET sc, size_t length)
{
    std::string s(length + 1, 0);
    getPartFromSocket(sc, (char*)s.data(), length, 0);
    // possible since C++17   ^
    return s;
}

void Helper::getPartFromSocket(SOCKET sc, char * buffer, size_t bytesNum, int flags)
{
    if (bytesNum == 0)
    {
        return;
    }

    int res = recv(sc, buffer, bytesNum, flags);

    if (res == INVALID_SOCKET)
    {
        std::string s = "Error while recieving from socket: ";
        s += std::to_string(sc);
        throw std::exception(s.c_str());
    }

}

клиент работает нормально - вывод:

È ок

но вывод сервера, который должен быть -

Код сообщения 200

на самом деле

Код сообщения 10

Где моя ошибка? Спасибо, М.

1 Ответ

3 голосов
/ 23 мая 2019

Вам следует изменить способ получения данных:

void Helper::getPartFromSocket(SOCKET sc, char* buffer, size_t bytesNum, int flags);

вместо внутреннего создания массива.Тогда вы можете сделать:

uint8_t Helper::getCode(SOCKET sc)
{
    uint8_t code;
    getPartFromSocket(sc, reinterpret_cast<char*>(&code), sizeof(code), 0);
    return code;
}
uint32_t Helper::getLength(SOCKET sc)
{
    uint32_t length;
    getPartFromSocket(sc, reinterpret_cast<char*>(&length), sizeof(length), 0);
    return length;
}
std::string Helper::getString(SOCKET sc, size_t length)
{
    std::string s(length, 0);
    getPartFromSocket(sc, s.data(), length, 0);
    // possible since C++17   ^
    return s;
}

т.е. вы записываете данные непосредственно туда, где они должны быть размещены.В то же время вы решаете проблему утечки памяти ...

Проблема остается с порядком байтов ... Вы, очевидно, пишете байты с прямым порядком байтов на стороне Python, но, как показано выше, вы (скорее всего - этозависит от машины, но машины с прямым порядком байтов в наши дни становятся очень редкими ...)Чтобы получить независимость от порядка байтов машины на стороне C ++, вы также можете изменить код следующим образом:

uint32_t length = 0
for(unsigned int i = 0; i < sizeof(length); ++i)
{
    uint8_t byte;
    getPartFromSocket(sc, reinterpret_cast<char*>(&byte), sizeof(byte), 0);
    // little endian tranmitted:
    // length |= static_cast<uint32_t>(byte) << 8*i;
    // big endian transmitted:
    length |= static_cast<uint32_t>(byte) << 8*(sizeof(length) - (i + 1));
    // simpler: just adjust loop variable; = 1, <= sizeof            ^
}
return length;

Редактировать: некоторые замечания из комментариев, так как они были удалены:

Ну, на самом деле, уже есть функция, выполняющая такие вещи: ntohl (спасибо, WhozCraig , за подсказку), так что вы можете получить ее намногопроще:

uint32_t length;
getPartFromSocket(sc, reinterpret_cast<char*>(&length), sizeof(length), 0);
return ntohl(length);

Еще одна проблема, обнаруженная во время обсуждения, на этот раз на стороне Python:

sock.send((msg.encode()))

encode по умолчанию доставляет строку, кодированную utf-8, что, конечно, нечто мы хотим в этом случае (200 будет преобразовано в два байта).Вместо этого нам нужно использовать кодировку локальной машины (на хосте Windows, вполне вероятно, cp1252 для западной Европы или cp1250 для центральной и восточной Европы).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...