Davide
Davide

Reputation: 57

C++ Windows recv() doesn't return even if data are available

I'm writing a C++ program. I need to receive a file and I'm using recv() function over a TCP socket to do that.

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);
       }

   }
}

The problem is that the recv never returns. It receives the whole file except for few KB and it freeze on the recv(). The buffer size is 1460. I receive the file only if I print something to the console with cout every time the recv is called. Only in this case I receive the whole file.

Otherwise if I set as socket option the WAITALL and the client closes the connection after the file is sent, I receive the whole file. Here's the code for the Client side that sends the file:

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

EDIT

Here's how I send and read the file size between the Client and Server.

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

Then I use the send to send this string

Here's how I read the 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));

EDIT

I cleaned up the code and I found out that read_data() is obviously called once and I was updating the buffer variable wrongly. Hence I was tracking the size of the content within the buffer in a wrong way which make me call the recv() once more.

Upvotes: 0

Views: 1581

Answers (1)

catnip
catnip

Reputation: 25388

First thing: recv() will block if there are no bytes left to read but the connection is still open. So whatever you might say about what your code is doing, that must be what is happening here.

That could be for any of the following reasons:

  • the sender lied about the size of the file, or did not send the promised number of bytes
  • the file size was not interpreted correctly at the receiving end for whatever reason
  • the logic that 'counts down' the number of bytes left in the receiver is somehow flawed

Trouble is, looking at the code samples you have posted, it's hard to say which because the code is a bit muddled and, in my eyes, more complicated than it needs to be. I'm going to recommend you sort that out.

  1. Sending the size of the file.

Don't mess about sending this as a string. Send it instead in binary, using (say) htonll() at the sending end and ntohll() at the receiving end. Then, the receiver knows to read exactly 8 bytes to figure out what's coming next. It's hard to get that wrong.

  1. Sending the file itself.

TransmitFile() looks to be a good choice here. Stick with it.

  1. Receiving the file and counting down how many bytes are left.

Take a closer look at that code and consider rewriting it. It's a bit of a mess.

  1. What to do if it still doesn't work.

Check with WireShark that the expected data is being sent and then walk through the code in the receiver in the debugger. There is absolutely no excuse for not doing this unless you don't have a debugger for some reason, in which case please say so and somebody will try to help you. The fact that logging to cout fixes your problems is a red-herring. That just changes the timing and then it just happens to work right.

That's all. Best of luck.

Upvotes: 0

Related Questions