EOF 与 boost::asio::read
我的 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 也有相同的行为。如果有人意识到我做错了什么,我很乐意指出。我拼命地想让它发挥作用。
I am having a small problem with my client/server application in C++. It uses boost::asio for remote communication and protocol buffers for serialization. Here the client:
// 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;
In this particular scenario of my application, the server's response can vary in size, going from a few bytes up to some MBs. For that reason the server first writes the size of the output which I then use to allocate the right amount of memory for my buffer. For some reason I get an error when reading the data (the size is always correctly read). A little bit of debugging revealed asio.misc:2 which by googling I figured out it is EOF (effectively the number of read bytes is smaller than the buffer size). Here the server side:
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));
This is actually my first time working with boost so I am not an experienced developer. Moreover, it is important to mention that in other parts of my application, this mechanism works but my other messages have some differences: although they are still variable in size, they are always small (a few hundred bytes). The server side is identical but in the client side I am using boost::asio::transfer_at_least(1) and I do not write the size in advance. If I apply those changes to this case I noticed the read call is able to retrieve up to 65536 bytes and no more (returning EOF prematurely). My development machines are Debian 5 and Ubuntu 10.04 for 64 bits and I get the same behaviour for Boost 1.40 and 1.47. If somebody realizes I am doing something wrong, I would appreciate to point it out. I am desperated trying to make it work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在我看来,您的协议可能存在问题:
你的客户如何知道lengthString的长度?您正在将任意数字转换为字符串,然后将其写入缓冲区,而不进行填充或任何其他操作?如果没有看到更多代码,就很难看出,但这看起来确实很可疑。
通常,当具有可变长度消息时,您定义某种数据包格式,例如长度字段为 4 个字节(为您的消息选择正确的字节数),然后是消息,然后您可以将(在本例中)int 发送到客户端首先会读取 4 个字节,然后知道还要从套接字读取多少字节。您可能想查看 htons 等函数。
如果如果你有一个可变长度的标头,你就会有某种分隔符来读取。这种类型的标头在 asio HTTP 示例中进行了说明,其中“\r\n\r\n”是使用的分隔符。这个示例可能对您也有用,因为正如 reima 所说,您需要更改客户端以首先读取标头,然后读取消息正文。
Looks to me like there could be a problem in your protocol:
How does your client know the length of lengthString? You are converting an arbitrary number into a string and then writing that to the buffer, without padding or anything? Without seeing more of the code it is difficult to see, but this definitely looks suspicious.
Typically when having variable length messages, you define some kind of packet format e.g. the length field is 4 bytes ( choose the correct number of bytes for your messages ) followed by the message and then you can send the (in this case) int to the client which will firstly read 4 bytes, and then know how many more bytes to read from the socket. You might want to look at functions like htons, etc.
If you have a variable length header, you would have some kind of delimiter to read up to. This type of header is illustrated in the asio HTTP examples where "\r\n\r\n" is the delimiter used. This example might also be useful to you since as reima stated, you would then need to change your client to first read the header and then the message body.
您似乎正在向服务器发送请求以询问即将到来的回复的大小,但由于 TCP 流本身没有分隔符,因此您可能会收到大小以及回复的部分或全部内容,同时只期望大小!
在尝试读取回复的其余部分(并且显然等待更少的字节)之前,您应该考虑这部分数据。
It seems that you are sending a request to the server to ask the size of the upcoming reply, but because TCP streams have no delimiter in themselves you might be receiving the size together with a part or the whole of the reply while expecting the size only!
You should take that part of the data into consideration before attempting to read the remainder of the reply (and obviously wait for less bytes).