我使用Boost Asio实现了一个非常简单的协议。我发送一个简单的查询,我得到一个可变长度的响应。异步发送似乎工作,并调用写入处理程序。 由于我不知道响应有多长,我首先阅读固定的8字节标题。这将始终存在,并包含剩余部分的长度。相关电话:
char input[256]; // Large enough to also hold the variable part.
async_read(socket, buffer(input), transfer_at_least(8), callback);
在callback
中,我得到一个boost::system::error_code
,其中包含"文件结束"。果然,插座不再打开了。但它是一个TCP套接字。在输入结束和关闭套接字时失败的重点是什么?更多输入将到达。我知道远程端没有关闭套接字,这是已知的正确实现该协议的生产代码。
异步读取的全部原因当然是不阻止等待响应。那么如何在前8个字节之后得到一个回调,而没有Asio关闭我的套接字?
现有问题相似但不同:要么他们不使用异步读取,要么他们不进行固定大小的读取,或者只有在第一次阅读后才会出现问题,或者他们遇到问题{ {1}}(我不会在EOF错误发生后按预期返回)
答案 0 :(得分:3)
文件结束(boost::asio::error::eof
)表示远程对等方关闭了连接。它并不表示该流没有可供读取的数据。 Boost.Asio streams documentation声明:
流的结尾可能导致
read
,async_read
,read_until
或async_read_until
函数违反合同。例如。由于N
,EOF
字节的读取可能会提前完成。
虽然可能由于未定义的行为而发生此错误。 Boost.Asio要求提供给async_read()
(input
)的缓冲区必须保持有效,直到调用处理程序(callback
)。
此外,如果套接字已在本地关闭,则操作错误将为boost::asio::error::operation_aborted
。
这是一个可用于演示此行为的基本应用程序:
#include <boost/array.hpp>
#include <boost/asio.hpp>
void on_read(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "read " << bytes_transferred << " bytes with "
<< error.message() << std::endl;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <port>\n";
return 1;
}
// Create socket and connet to local port.
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
ip::tcp::socket socket(io_service);
socket.connect(ip::tcp::endpoint(
ip::address::from_string("127.0.0.1"), std::atoi(argv[1])));
// Start read operation.
boost::array<char, 512> buffer;
async_read(socket,
boost::asio::buffer(buffer),
boost::asio::transfer_at_least(7),
&on_read);
// Run the service.
io_service.run();
}
以下演示了两次写入TCP连接。第一次写入足够小,客户端将在下一次写入之前读取所有流的数据。
$ nc -l 12345 & [1] 11709 $ ./a.out 12345 & [2] 11712 $ fg 1 nc -l 12345 helloenter worldenter read 12 bytes with Success
相同的程序,但连接被明确杀死:
$ nc -l 12345 & [1] 11773 $ ./a.out 12345 & [2] 11775 $ fg 1 nc -l 12345 helloenter worldctrl-c read 6 bytes with End of file
答案 1 :(得分:0)
原则上是协议实施中的竞争条件。如果未接受初始消息,则远程端关闭套接字。发送初始消息后,本地方确实检查了这一点。但是,在转移到异步操作之后,在本地返回异步写入之后以及异步读取之前完成套接字检查。
无法准确预测远程端何时关闭套接字,您无法等待。你唯一知道的是,如果你收到这8个字节,它就没有关闭套接字。因此,异步读取时的EOF结果是我必须处理的事情。
我怀疑现有的(同步)代码可能有类似的时序问题尚未浮出水面:(