C ++ Windows recv () не возвращается, даже если данные доступны - PullRequest
0 голосов
/ 09 июня 2018

Я пишу программу на C ++.Мне нужно получить файл, и я использую функцию recv () через сокет TCP для этого.

download_file() {
    while (left_bytes != 0 && !connection_closed) {
        if (left_bytes >= buffer_max_size)
            bytes_to_download = buffer_max_size;
        else
            bytes_to_download = left_bytes;

        if (request.conn->read_data(buffer, bytes_to_download))            
        {
            left_bytes -= buffer->get_size();
            temporary_file.write_data(buffer);
        } else connection_closed = true;

    }
}

read_data() {
    while (bytes_received < size && alive_) {
        bytes_read = recv(sock_, read_buffer, size, 0);

        if (bytes_read == SOCKET_ERROR) {
            delete[] local_buffer;
            throw SocketException(WSAGetLastError());
        }

       // the connection is closed
       if (bytes_read == 0) alive_ = false;
       else {
           bytes_received += bytes_read;                    
           buffer->add(local_buffer, bytes_read);
       }

   }
}

Проблема в том, что recv никогда не возвращается.Он получает весь файл, за исключением нескольких килобайт, и останавливается в recv ().Размер буфера составляет 1460. Я получаю файл, только если что-то печатаю на консоль cout каждый раз, когда вызывается recv.Только в этом случае я получаю весь файл.

В противном случае, если я установлю в качестве опции сокета WAITALL и клиент закроет соединение после отправки файла, я получу весь файл.Вот код для клиентской стороны, которая отправляет файл:

TransmitFile(file_request->connection_->get_handle_socket(), file_handler.get_file_handle(), file_request->file_size_, 65535, nullptr, nullptr, TF_USE_SYSTEM_THREAD)

РЕДАКТИРОВАТЬ

Вот как я могу отправить и прочитать размер файла между клиентом и сервером.

std::stringstream stream_;
stream_.str(std::string());
// append the file size
const __int64 file_size = htonll(GetFileSize(file_handle_, nullptr););
stream_ << ' ' << file_size << ' ';

Затем я посылаю эту строку с помощью отправки

Вот как я читаю размер файла

// Within stream_ there is all the content of the received packet
std::string message;
std::getline(stream_, message, ' ');
this->request_body_.file_size_ = ntohll(strtoll(message.c_str(), nullptr, 0));

РЕДАКТИРОВАТЬ

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

1 Ответ

0 голосов
/ 09 июня 2018

Первое: recv() заблокируется, если не осталось байтов для чтения, но соединение все еще открыто.Поэтому, что бы вы ни говорили о том, что делает ваш код, должно быть тем, что здесь происходит.

Это может быть по любой из следующих причин:

  • отправитель солгал о размере файла или не отправил обещанное количество байтов
  • размер файла был неверно истолкован на принимающей стороне по какой-либо причине
  • логика, которая 'отсчитывает 'количество байтов, оставшихся в приемнике, как-то некорректно

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

  1. Отправка размера файла.

Не связывайтесь с отправкой в ​​виде строки.Вместо этого отправьте его в двоичном формате, используя, скажем, htonll() на отправляющей стороне и ntohll() на получающей стороне.Затем получатель знает, что нужно прочитать ровно 8 байтов, чтобы выяснить, что будет дальше.Трудно ошибиться.

Отправка самого файла.

TransmitFile() выглядит здесь хорошим выбором.Придерживайтесь этого.

Получение файла и отсчет количества оставшихся байтов.

. Внимательно посмотрите на этот код и подумайте о его перезаписи.Это немного беспорядок.

Что делать, если он все еще не работает.

Проверьте с помощью WireShark, что ожидаемые данные отправляются, а затем просмотрите код в приемнике в отладчике.Нет абсолютно никакого оправдания тому, чтобы этого не делать, если у вас нет отладчика по какой-то причине, в таком случае, пожалуйста, скажите об этом, и кто-то попытается вам помочь.Тот факт, что вход в систему cout решает ваши проблемы, является красной селедкой.Это просто меняет время, а потом все работает правильно.

Вот и все.Желаем удачи.

...