【问题标题】:C++ Windows recv() doesn't return even if data are available即使数据可用,C++ Windows recv() 也不会返回
【发布时间】:2018-11-19 09:26:15
【问题描述】:

我正在编写一个 C++ 程序。我需要接收一个文件,并且我正在 TCP 套接字上使用 recv() 函数来做到这一点。

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 永远不会返回。它接收除几个 KB 之外的整个文件,并在 recv() 上冻结。缓冲区大小为 1460。 只有在每次调用 recv 时使用 cout 将某些内容打印到控制台时,我才会收到该文件。只有在这种情况下,我才会收到整个文件。

否则,如果我将 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 << ' ';

然后我使用 send 发送这个字符串

这是我读取文件大小的方式

// 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()。

【问题讨论】:

  • 你怎么知道有数据可以接收?也许你应该考虑非阻塞套接字?
  • 我通过wireshark看到整个文件被发送并且每个发送的数据包的ACK都被接收。据我所知,仅当整个文件已在另一端传输和接收时,TransmitFIle 才会返回 true。无论如何,我用一开始忘记的更多代码行更新了这个问题
  • 您可能会尝试接收对方实际发送的更多字节,这就是 recv 永远不会返回的原因。
  • 确实,您可能会迭代一次到多次,并在实际收到所有数据后调用recv。请learn how to debug your programs。特别好的是使用调试器逐行单步执行您的代码,同时监控变量及其值。
  • TransmitFile 接收要发送的字节作为参数:file_request->file_size_ 这与我发送到服务器的值相同,因此它知道要接收多少字节(left_bytes 以这种方式确定) . file_request->file_size_ 值来自:GetFileSize(file_handle_, nullptr);窗口功能

标签: c++ windows sockets winsock


【解决方案1】:

第一件事:recv() 将阻塞如果没有剩余字节要读取但连接仍然打开。因此,无论您对代码在做什么说,必须就是这里发生的事情。

这可能是由于以下任何原因:

  • 发件人谎报文件大小,或未发送承诺的字节数
  • 由于某种原因,在接收端没有正确解释文件大小
  • “倒计时”接收器中剩余字节数的逻辑存在某种缺陷

麻烦的是,看看您发布的代码示例,很难说是哪一个,因为代码有点混乱,而且在我看来,比它需要的更复杂。我会建议你解决这个问题。

  1. 发送文件的大小。

不要把它作为字符串发送。以二进制形式发送它,在发送端使用(比如)htonll(),在接收端使用ntohll()。然后,接收者知道准确地读取 8 个字节来确定接下来会发生什么。很难弄错。

  1. 发送文件本身。

TransmitFile() 在这里看起来是个不错的选择。坚持下去。

  1. 接收文件并计算剩余字节数。

仔细查看该代码并考虑重写它。有点乱。

  1. 如果还是不行怎么办。

使用 WireShark 检查是否正在发送预期的数据,然后在调试器中遍历接收器中的代码。除非您出于某种原因拥有调试器,否则绝对没有理由不这样做,在这种情况下,请这样说,有人会尽力帮助您。登录到cout 可以解决您的问题这一事实是一个红鲱鱼。这只是改变了时间,然后它恰好可以正常工作。

就是这样。祝你好运。

【讨论】:

  • 非常感谢您的提示。我清理了代码,发现 read_data() 显然被调用了一次,并且我错误地更新了缓冲区变量。因此缓冲区中内容的大小是错误的,这让我再次调用了 recv()。
  • 好的,好东西。道德:不要忍受你无法理解的混乱代码。最好咬紧牙关并正确修复它(尽管我不得不承认,并不是我自己的所有代码都在这个部门中完全符合要求!)。
猜你喜欢
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
  • 2012-12-26
  • 1970-01-01
  • 2019-06-22
  • 2014-04-22
  • 2020-04-08
  • 1970-01-01
相关资源
最近更新 更多