带有boost :: asio :: read的EOF

时间:2011-10-08 22:58:20

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

我在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),我不提前写大小。如果我将这些更改应用于这种情况,我注意到读取调用最多可以检索65536个字节而不是更多(过早返回EOF)。我的开发机器是64位的Debian 5和Ubuntu 10.04,我对Boost 1.40和1.47有相同的行为。如果有人意识到我做错了什么,我将不胜感激。我很绝望,试图让它发挥作用。

2 个答案:

答案 0 :(得分:4)

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

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

如果你有一个可变长度的标题,你可以使用某种分隔符来读取。这种类型的标头在asio HTTP示例中说明,其中“\ r \ n \ r \ n”是使用的分隔符。这个例子对你也很有用,因为在reima声明的时候,你需要更改你的客户端,先读取标题,然后再读取消息体。

答案 1 :(得分:1)

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