Ошибка отправки / получения вектора двойного сокета TCP (пропущенные данные) - PullRequest
0 голосов
/ 27 мая 2019

Я пытаюсь отправить данные из вектора через сокет TCP. Я работаю с вектором, который заполняю значениями от 0 до 4999, а затем отправляю его в сокет.

Клиентская сторона, я получаю данные в вектор, затем копирую их данные в другой вектор, пока не получу все данные с сервера.

Проблема, с которой я сталкиваюсь, заключается в том, что когда я получаю свои данные, иногда я получаю все эти данные, а иногда я получаю только правильные данные от 0 до 1625, а затем я получаю данные корзины до конца (см. изображение ниже). Я даже получил, например, от 0 до 2600 правильных данных, затем с 2601 до 3500 это мусор и, наконец, с 3501 до 4999, это снова правильно.

File containing received data
(левый столбец - номер строки, а правый столбец - данные).

Это сторона сервера:

vector<double> values2;
for(int i=0; i<5000; i++)
    values2.push_back(i);
skt.sendmsg(&values2[0], values2.size()*sizeof(double));

Функция sendmsg:

void Socket::sendmsg(const void *buf, size_t len){

    int bytes=-1;

    bytes = send(m_csock, buf, len, MSG_CONFIRM);

    cout << "Bytes sent: " << bytes << endl;

}

Клиентская сторона:

vector<double> final;
vector<double> msgrcvd(4096);

do{

    bytes += recv(sock, &msgrcvd[0], msgrcvd.size()*sizeof(double), 0);
    cout << "Bytes received: " << bytes << endl;

    //Get rid of the trailing zeros
    while(!msgrcvd.empty() && msgrcvd[msgrcvd.size() - 1] == 0){
        msgrcvd.pop_back();

    }

    //Insert buffer content into final vector
    final.insert(final.end(), msgrcvd.begin(), msgrcvd.end());


}while(bytes < sizeof(double)*5000);


//Write the received data in a txt file

for(int i=0; i<final.size(); i++)
    myfile << final[i] << endl;

myfile.close();


Выводы байтов правильные, сервер выводит 40 000 при отправке данных, а клиент также выводит 40 000 при получении данных.

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

Я действительно не знаю, проблема в том, когда я отправляю данные или когда я их получаю, а также я не понимаю, почему иногда (редко) я получаю все данные.

Ответы [ 2 ]

1 голос
/ 28 мая 2019

recv получает байт и не обязательно ожидает всех отправленных данных.Таким образом, вы можете получать часть двойного значения.

Ваш код работает, если вы получите полные значения double, но потерпит неудачу, когда получите часть значения.Вы должны получить ваши данные в буфере char, а затем распаковать их в двойные.(Возможно преобразование порядка байтов, если сервер и клиент различаются.)

#include <cstring>    // For memcpy

std::array<char, 1024> msgbuf;
double d;
char data[sizeof(double)];
int carryover = 0;

do {
    int b = recv(sock, &msgbuf[carryover], msgbuf.size() * sizeof(msgbuf[0]) - carryover, 0);
    bytes += b;
    b += carryover;
    const char *mp = &msgbuf[0];
    while (b >= sizeof(double)) {
        char *bp = data;
        for (int i = 0; i < sizeof(double); ++i) {
            *bp++ = *mp++;
        }
        std::memcpy(&d, data, sizeof(double));
        final.push_back(d);
        b -= sizeof(double);
    }
    carryover = b % sizeof(double);
    // Take care of the extra bytes.  Copy them down to the start of the buffer
    for (int j = 0; j < carryover; ++j) {
        msgbuf[j] = *mp++;
    }
} while (bytes < sizeof(double) * 5000);

При этом используется типирование типов из Как правильно использовать типизацию типа float для типа int и наоборот? чтобы преобразовать полученные двоичные данные в двойные, и предполагает, что порядковые номера клиента и сервера совпадают.

Кстати, как получатель узнает, сколько значений он получает?В коде вашего сервера есть сочетание жестко закодированных значений (5000) и динамических значений (.size()).

Примечание: код не скомпилирован или не проверен

0 голосов
/ 28 мая 2019

TL / DR: Никогда не отправляйте необработанные данные через сетевой сокет и ожидайте, что они будут правильно получены / распакованы на другой стороне.

Подробный ответ: Сетьпостроен на основе различных протоколов, и это не просто так.Когда вы отправляете что-то, нет гарантии, что ваш контрагент находится на той же ОС и той же версии программного обеспечения.Не существует стандарта, как примитивные типы должны кодироваться на уровне байтов.Нет ограничений на количество прерывистых узлов, которые могут быть вовлечены в доставку данных, и каждый из ваших send () может проходить по разным маршрутам.Таким образом, вы должны формализовать способ отправки данных, и тогда другая сторона может быть уверена, что это правильный способ извлечь их из сокета.

Самое простое решение: используйте заголовок перед вашимданные.Итак, вы планируете отправить 5000 дублей?Затем сначала отправьте DWORD, который содержит 40000 внутри (5k элементов, 8 байтов каждый -> 40k), и сразу после этого вытолкните все свои 5k двойников.Затем ваш контрагент должен сначала прочитать 4 байта из сокета, интерпретировать его как DWORD и понять, сколько байтов должно быть потом.

Следующий шаг: вы можете захотеть отправить не только двойные числа,но целые и строки также.Таким образом, вы должны расширить свой заголовок, чтобы он мог указывать

  • Общий размер дополнительных данных (так называемый размер полезной нагрузки)
  • Вид данных (массив значений типа double, строка,Single Int и т. д.)

Расширенное решение: Ознакомьтесь с готовыми решениями:

Удачного кодирования!

...