Команда C ++ Server recv () не всегда получает send () от клиента - PullRequest
0 голосов
/ 07 марта 2020

У меня есть сервер, который подключается к клиенту и выполняет следующую связь:

void communicate(promise<long> && divisions, promise<long> && result, int sockt, long number, long prime) {
    send(sockt, number);
    send(sockt, prime);
    long div = receive(sockt);
    long res = receive(sockt);
    divisions.set_value(div);
    result.set_value(res);
}

void send(int sockt, long number) {
    string numberString;
    stringstream strstream;
    strstream << number;
    strstream >> numberString;
    cout << "Sending: " << number << endl;
    write(sockt, numberString.c_str(), sizeof numberString);
}

long receive(int sockt) {
    // receive string
    const unsigned int MAX_BUF_LENGTH = 1024;
    std::vector<char> buffer(MAX_BUF_LENGTH);
    std::string rcv;
    recv(sockt, &buffer[0], buffer.size(), 0);
    rcv.append(buffer.cbegin(), buffer.cend());

    //convert string to long
    string::size_type sz = 0;
    long val = stoll(rcv, &sz, 0);
    cout << "Received: " << val << endl;

    return val;
}

Клиент выполняет ту же связь, но в обратном порядке:

void communicate(){
    string number = receive(sockt);
    string prime = receive(sockt);
    // do stuff to the data
    send(sockt, divisions);
    send(sockt, val);
}

Проблема заключается в Сервер иногда (после более 100 раз зацикливания) не получает второе отправленное сообщение от клиента и зависает. Клиент никогда не испытывает проблем с получением сообщения от сервера, но сервер всегда зависает. Сервер печатает:

Sending: 344486269
Sending: 7
Received: 0

Затем не завершается, поэтому я знаю, что он ожидает второго значения, которое никогда не приходит. Клиент печатает:

Received: 344486269
Received: 7
Sending: 0
Sending: 344486269

Итак, я знаю, что клиент успешно выполняет свои команды send().

Что может быть причиной того, что сервер неправильно получил второе сообщение?

1 Ответ

0 голосов
/ 07 марта 2020

В void send(int sockt, long number) ваш вызов на write() является ошибочным по нескольким причинам:

  • sizeof numberString - неправильное количество байтов для отправки. Это размер во время компиляции класса string, а не размер во время выполнения байтовых символьных данных, на которые он указывает.

  • вы не разграничиваете отправленные данные в любом способ, позволяющий получателю узнать, когда заканчиваются данные. При отправке данных переменной длины вы ДОЛЖНЫ либо отправить длину данных до отправки данных, либо отправить уникальный терминатор после данных.

  • вы не учитываете вероятность того, что write() может отправить меньше байтов, чем запрошено. Вы должны вызывать его в al oop, пока все желаемые байты не будут полностью отправлены.

Аналогично, в long receive(int sockt) у вас есть похожие проблемы, связанные с recv(). Вы звоните только один раз, независимо от размера отправляемых данных. И вы игнорируете его возвращаемое значение, чтобы узнать, сколько фактически было прочитано байтов.

Вместо этого вам нужно что-то более похожее на следующее:

void send(int sockt, long number) {
    ostringstream oss;
    oss << number;
    string numberString = oss.str();
    // or simply:
    // string numberString = to_string(number);

    cout << "Sending: " << numberString << endl;

    const char *ptr = numberString.c_str();
    string::size_type size = numberString.size() + 1;
    do {
        int written = write(sockt, ptr, size);
        if (written < 0) {
            // error handling as needed...
            return;
        }
        ptr += written;
        size -= written;
    }
    while (size > 0);
}

long receive(int sockt) {
    // receive string
    char ch;
    std::string rcv;
    do {
        if (recv(sockt, &ch, 1, 0) <= 0) {
            // error handling as needed...
            return -1;
        }
        if (ch == '\0') break;
        rcv += ch;
    }
    while (true);

    //convert string to long
    string::size_type sz = 0;
    long val = stoll(rcv, &sz, 0);
    cout << "Received: " << val << endl;

    return val;
}

Однако гораздо лучшим решением было бы просто отправьте данные long в двоичном формате фиксированного размера вместо строки переменной длины:

void send(int sockt, long number) {
    uint32_t tmp = htonl(number);
    cout << "Sending: " << number << endl;
    char *ptr = reinterpret_cast<char*>(&tmp);
    size_t size = sizeof(tmp);
    do {
        int written = write(sockt, ptr, size);
        if (written < 0) {
            // error handling as needed...
            return;
        }
        ptr += written;
        size -= written;
    }
    while (size > 0);
}

long receive(int sockt) {
    // receive long
    uint32_t tmp;
    char *ptr = reinterpret_cast<char*>(&tmp);
    size_t size = sizeof(tmp);
    do {
        int recvd = recv(sockt, ptr, size, 0);
        if (recvd <= 0) {
            // error handling as needed...
            return -1;
        }
        ptr += recvd;
        size -= recvd;
    }
    while (size > 0);

    long val = ntohl(tmp);
    cout << "Received: " << val << endl;

    return val;
}
...