我正在尝试制作asio和SSL的朋友。 一切顺利,但有一件事造成不便:如何 检测对等体是否紧密连接,并将其与情况区分开来 同行只是在发送数据时暂时休息,目的是继续 几秒钟后呢?
我的困惑来自于asio行为不同的事实 普通套接字和SSL流。 如果我使用tcp :: socket - 我在对等关闭连接时收到EOF错误。 但对于boost :: asio :: ssl :: stream - 它是 不是这样的。相反,async_read_some在传输的字节数时返回0, 如果我尝试继续从SSL流中读取 - 返回short_error (http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/streams.html)。
所以,问题是:它是预期的行为,还是我错误配置了什么?
客户端代码段:
class client
{
public:
// bla-bla-bla-bla-bla ....
//
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_read_some(boost::asio::buffer(reply_, max_length),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cout << "Write failed: " << error.message() << "\n";
}
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
std::cout << "Bytes transfered: " << bytes_transferred << "\n";
if (!error)
{
std::cout << "Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
std::cout << "Reading...\n";
socket_.async_read_some(boost::asio::buffer(reply_, max_length),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else if (0 != bytes_transferred)
{
std::cout << "Read failed: " << error.message() << ":"
<< error.value() << "\n";
}
}
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
boost::asio::streambuf request_;
char reply_[max_length];
};
如果我们删除if(0!= bytes_transferred),我们将得到&#34;短读&#34; :(
如果我们使用代码as-ai,输出将是这样的:
请求是:
GET / HTTP / 1.0
Cookie:Nama-nama = Vala-vala
转移的字节数:1024
回复:HTTP / 1.0 200 ok 内容类型:text / html
..... bla-bla-bla ....
...读 转移的字节数:1024
..... bla-bla-bla .... ..... bla-bla-bla ....
...读 转移的字节数:482
..... bla-bla-bla ....
...读
转移的字节数:0
同时,如果我们编写代码async_read_some,那么 普通套接字将返回EOF:
boost::asio::async_read(socket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&client::handle_read_content, this,
boost::asio::placeholders::error));
然后对于SSL套接字,我们将在传输的字节数为0时获得0,然后使用short_read。
我知道如果是peer,则无法检测断开连接 例如,刚刚从网络中拔出。 但是如何检测显式干净的同伴断开情况 peer只是暂时不发送数据,但可能会这样做 稍后一点?
或者,我可能不理解某事?
WBR, 安德烈
一些addentum: SSL / TLS具有通知其他方关闭连接的表示法。 它close_notify警报。也可以关闭底层TCP套接字。
所以,基本上,我的问题是:为什么,在相同的条件下(TCP套接字被清楚地关闭)我在tcp :: socket的情况下收到EOF,并且没有收到任何关于boost :: asio :: ssl ::流。
是bug还是asio功能?
又一个addentum: 由于某些原因,如果SSL收到close_notify并且底层的TCP套接字已关闭,asio也没有给我一个EOF。
是的,我可以通过超时检测死连接。 但是,如何检测正确关闭的SSL连接?通过接收short_read?
答案 0 :(得分:14)
此处预计会出现SSL_R_SHORT_READ
错误。当服务器使用SSL_Shutdown
启动干净关闭时,会向客户端发送关闭通知关闭警报。 Asio实现将此映射为SSL_R_SHORT_READ
错误,类别为error::get_ssl_category()
。它通过检测对等体是否已通过SSL_get_shutdown
启动关闭来实现此目的。
通过检查asio/ssl/detail/impl/engine.ipp
标题,特别是函数engine::map_error_code(boost::system::error_code&)
可以看出这一点。
我相信ssl实现在boost 1.47中被重写,因此早期版本可能有不同的行为。
答案 1 :(得分:6)
您可能对这些讨论感兴趣:
基本上,当远程方断开普通TCP套接字时,有时(甚至大部分时间)获得EOF的事实只是运气。在一般情况下你不能依赖它,因为它不可能在不写入的情况下区分非活动套接字和套接字突然关闭。
您需要在应用程序协议级别定义一些分隔符,以了解何时停止读取。
在HTTP中,这可以通过结束标题的空行(用于标题),定义主体长度的Content-Length
标题或者在未知主体长度时的chunked transfer encoding分隔符完成提前。