Отправить длину и данные через сокет C ++ - PullRequest
0 голосов
/ 30 августа 2018

Итак, я сейчас работаю над приложением, которое использует сокет, сейчас все работает, я просто хочу знать, есть ли более эффективный способ отправки длины данных и данных вместо двойной отправки.

Т.е.: отправка всего в одну строку.

void SendPackage(const SOCKET sock, const std::string package)
{
    int length = package.lenth();

    send(sock, std::to_string(length).c_str(), 10, 0);     //Buffer length is 10 assuming data length
    send(sock, package.c_str(), length, 0);        //will never be greater than 9,999,999,999
}

void ReceivePackage(const SOCKET sock, std::string &package, int bufferLength)
{
    std::vector<char> recvBuffer(10);
    int length, bytesProcessed;

    recv(sock, &recvBuffer[0], 10, 0); //Receiving length
    length = atoi(&recvBuffer[0]);
    recvBuffer.resize(length);

    for (int i = 0; i < length; i += bytesProcessed)
    {
        bytesProcessed = recv(sock, &recvBuffer[0] + i, bufferLength, 0);
        if (bytesProcessed < 0) break;
    }

    package = &recvBuffer[0];
}

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Что касается интерфейса сокета, вы не отправляете дважды. Сокет просто берет все, что вы кладете в один конец, одним или несколькими вызовами send и отправляет его в свое время (я предполагаю, что вы не превышаете буфер сокета при первом вызове, что является довольно безопасным предположением ). Точно так же вам не нужно сопоставлять ваши recv звонки с send звонками. Вы можете recv весь лот сразу, а потом разбить его позже, но на самом деле, не так уж и много.

Таким образом, это действительно вымысел, упаковываете ли вы сначала строку, а затем отправляете ее, или дважды звоните send. Время и оперативная память, необходимые для создания новой строки с длиной, сцепленной с началом, не будут более эффективными, чем просто дважды вызвать send.

Тем не менее, вы обязательно должны отправлять длину как int, а не как строку. Таким образом, он всегда будет занимать sizeof(int) байтов. Длина вашего буфера 10 обычно не будет истинной, и в результате будет прочитан конец строки. Например:

send(sock, &length, sizeof(length), 0);

и

recv(sock, &length, sizeof(length), 0); //Receiving length
0 голосов
/ 30 августа 2018
send(sock, std::to_string(length).c_str(), 10, 0);

Это UB. Вы говорите, что данные имеют 10 байтов, но это не правильно. Кроме того, нет необходимости преобразовывать ваш int в строку, а затем отправлять строку. Просто отправил int, и во избежание несовместимости платформ я рекомендую использовать тип фиксированного размера:

std::int32_t length = package.length();
send(sock, &length, sizeof(length), 0);

На принимающей стороне вы должны убедиться, что вы действительно ждете, пока все байты будут там. Вы не можете просто позвонить recv и предположить, что он дает вам все необходимые байты. Вам нужно вызвать его в цикле и проверить его возвращаемое значение, которое говорит вам, сколько байтов вы получили. Чтобы получить length, вам нужно прочитать соответствующее количество байтов в буфер, а затем заново интерпретировать этот буфер как тип, который вы выбрали (например, std::int32_t.

Кроме того, вы должны убедиться, что порядковый номер вашего int правильный. Это де-факто стандарт для использования данных с прямым порядком байтов для сетевых данных, но в конце концов это ваше дело. Не являются частью стандарта (afaik), но доступны на общих платформах вспомогательные функции htonl и ntohl, которые обозначают «хост-сеть, длинный» и «сеть-хост, длинный». Они берут и возвращают std::int32_t, и вы можете просто использовать их, чтобы убедиться, что порядок байтов работает для обеих сторон.

Отправитель:

std::int32_t length = htonl(package.length());
send(sock, &length, sizeof(length), 0);

Получатель:

// after successfully reading all bytes for the length value into an adequately sized buffer:
std::int32_t length = ntohl(*reinterpret_cast<std::int32_t*>(buffer));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...