【问题标题】:EOF with boost::asio::read带有 boost::asio::read 的 EOF
【发布时间】:2011-10-08 22:58:20
【问题描述】:

我的 C++ 客户端/服务器应用程序有一个小问题。它使用 boost::asio 进行远程通信,使用协议缓冲区进行序列化。这里是客户:

// Time to write
char sizeMessage[20];
string content = request.SerializeAsString();
size_t request_length = content.size();            

boost::asio::write(s, boost::asio::buffer(content.c_str(), request_length));
boost::system::error_code error;

boost::asio::read(s, boost::asio::buffer(sizeMessage, 20));

int bufferSize = atoi(sizeMessage);
char* responseString = new char[bufferSize];        
size_t reply_length = boost::asio::read(s, boost::asio::buffer(responseString, bufferSize), boost::asio::transfer_all(), error);
if(!error){
    response.ParseFromArray((const void*)responseString, reply_length);
    success = true;                
}else{
    response.set_status(ExecResponse::ERROR);                
}


delete[] responseString;

在我的应用程序的这个特定场景中,服务器响应的大小可能会有所不同,从几个字节到几 MB 不等。出于这个原因,服务器首先写入输出的大小,然后我用它来为我的缓冲区分配适量的内存。出于某种原因,我在读取数据时出现错误(始终正确读取大小)。一点调试发现 asio.misc:2 通过谷歌搜索我发现它是 EOF (实际上读取的字节数小于缓冲区大小)。这里是服务器端:

ExecResponse response;
ExecutionServerHandler execHandler(this->sharedData);
execHandler.process(rd, request, response);   
string output = response.SerializeAsString();
//First write the length
ostringstream stringLength;
stringLength << output.size();
response.PrintDebugString();
string lengthString = stringLength.str();    
size_t bw = ba::write(bsocket_, ba::buffer(lengthString));
bw = ba::write(bsocket_, ba::buffer(output));

这实际上是我第一次使用 boost,所以我不是一个经验丰富的开发人员。此外,值得一提的是,在我的应用程序的其他部分,这种机制有效,但我的其他消息有一些不同:虽然它们的大小仍然可变,但它们总是很小(几百字节)。服务器端是相同的,但在客户端我使用的是 boost::asio::transfer_at_least(1) 并且我没有提前写大小。如果我将这些更改应用于这种情况,我注意到 read 调用能够检索多达 65536 个字节并且不再检索(过早返回 EOF)。我的开发机器是 64 位的 Debian 5 和 Ubuntu 10.04,而 Boost 1.40 和 1.47 的行为相同。如果有人意识到我做错了什么,我会很感激指出。我迫不及待地想要让它发挥作用。

【问题讨论】:

  • 我也不是 boost::asio 方面的专家,但是在服务器端写一个可变长度的字符串 (lengthString) 并且总是在客户端读取 20 个字节对我来说看起来很可疑.每当lengthString 小于20 字节时,您已经将部分有效负载读入sizeMessage,因此没有足够的字节来填充responseString
  • 您好,感谢您的评论。它给了我一个线索。我用服务器端的长度调整了字符串的大小,使其始终包含 20 个字符(用 \0 填充剩余空间),同时强制客户端全部使用。这解决了这个问题。我的假设是第二次调用 read 正在读取包含长度的字符串的剩余字符,因此我得到了一个过早的 EOF。非常感谢!
  • @reima 评论应该是一个答案

标签: c++ networking boost client-server boost-asio


【解决方案1】:

在我看来,您的协议可能存在问题: 您的客户如何知道 lengthString 的长度?您正在将任意数字转换为字符串,然后将其写入缓冲区,而不使用填充或任何东西?没有看到更多代码很难看到,但这看起来很可疑。

通常当有可变长度消息时,您定义某种数据包格式,例如长度字段是 4 个字节(为您的消息选择正确的字节数),然后是消息,然后您可以将(在这种情况下)int 发送到客户端,该客户端将首先读取 4 个字节,然后知道还有多少字节从套接字读取。你可能想看看htons等函数。

如果您有一个可变长度的标头,您将需要读取某种分隔符。这种类型的标头在 asio HTTP 示例中进行了说明,其中 "\r\n\r\n" 是使用的分隔符。这个例子也可能对您有用,因为正如 reima 所说,您需要更改您的客户端以先读取标头,然后再读取消息正文。

【讨论】:

  • 感谢您的建议!我检查了 HTTP 示例,我同意,这将是最优雅的解决方案。不幸的是,字符串的结构对我来说是一种黑盒子,因为我使用协议缓冲区进行序列化,所以我不能只读取消息的片段并依赖分隔符。
  • 在这种情况下,我将使用第一种方法,即发送 4 个字节以指示网络主机顺序中的字符串长度,然后发送可变长度字符串。在您的示例代码中,您将长度作为字符串发送,这可能是问题所在。
【解决方案2】:

您似乎正在向服务器发送请求以询问即将到来的回复的大小,但由于 TCP 流本身没有分隔符,您可能会在期待回复时收到大小以及部分或全部回复只有尺寸! 在尝试读取回复的其余部分(显然等待更少的字节)之前,您应该考虑这部分数据。

【讨论】:

    猜你喜欢
    • 2021-07-28
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    相关资源
    最近更新 更多