boost::asio::async_write和超过65536字节的缓冲区

boost::asio::async_write and buffers over 65536 bytes

本文关键字:65536 字节 缓冲区 asio async write boost      更新时间:2023-10-16

我有一个非常简单的方法,目的是响应传入消息,然后关闭连接:

void respond ( const std::string message )
{ 
std::string str = "<?xml version="1.0"?>";
Controller & controller = Controller::Singleton(); 
if ( auto m = handleNewMessage( _message ) )
{
auto reply = controller.FIFO( m );
str.append( reply );
}
else
str.append ( "<Error/>" );
std::size_t bytes = str.size() * sizeof( std::string::value_type );
std::cout << "Reply bytesize " << bytes << std::endl;
boost::asio::async_write(
socket_,
boost::asio::buffer( str ),
boost::bind(
&TCPConnection::handle_write,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
));
}

void handle_write ( const boost::system::error_code & error, size_t bytes_transferred )
{
if ( error )
{
std::cerr << "handle_write Error: " << error.message() << std::endl;
std::cerr << "handle_write Bytes sent: " << bytes_transferred << std::endl;
}
else
{
std::cerr << "handle_write Bytes sent: " << bytes_transferred << std::endl;
socket_.close();
}
}

我知道问题是boost::asio::async_write没有完成写入操作,因为以上操作的输出是:

Reply bytesize: 354275
handle_write Bytes sent: 65536

意味着最大缓冲区大小(65536)不足以写入数据?

在Stack Overflow周围搜索时,我发现我的问题是由以下方法创建的缓冲区:

boost::asio::buffer( str )

在操作有机会完成发送所有数据之前超出范围。

我似乎不能使用boost::asio::mutable_buffer,而只能使用的boost:

此外,更重要的是,第二个错误抱怨实际的boost::asio::async_write被传递了boost:

/usr/include/boost/asio/detail/consuming_buffers.hpp:164:5: error: no type named ‘const_iterator’ in ‘class boost::asio::mutable_buffer’
const_iterator;
^
/usr/include/boost/asio/detail/consuming_buffers.hpp:261:36: error: no type named ‘const_iterator’ in ‘class boost::asio::mutable_buffer’
typename Buffers::const_iterator begin_remainder_;

所以我只有一个选择:使用增强::asio::streambuf

我尝试过使用:

boost::asio::streambuf _out_buffer;

作为类成员,然后使方法响应:

std::ostream os( &_out_buffer );
os << str;
boost::asio::async_write(
socket_,
_out_buffer,
boost::asio::transfer_exactly( bytes ),
boost::bind(
&TCPConnection::handle_write,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
));

然而,尽管我没有收到任何错误,但并不是所有的数据都被发送了!所以,我猜,不是整个字符串都写进了streambuf吗?

或者,我很想知道使用boost::asio::async_write(大于65536字节的数据)最优雅的写入方式是什么!

Alex,您理解异步操作是错误的。您的问题都与缓冲区和套接字的使用寿命有关。缓冲区必须在整个传输时间内处于活动状态并打开套接字(从asio::async_write调用到handle_write回调由Asioio_service调度器调用)。为了更好地理解它是如何工作的,请考虑每次执行boost::asio::async_{operation}时,都会将指向操作数据的指针和指向回调函数的指针发布到作业队列。何时执行你的工作是阿西奥的决定(但它当然会尽可能快地完成)。当整个(可能很大的)I/O操作完成时,Asio会使用指定的回调通知您。然后你可以自由地释放资源。

因此,要使代码正常工作,必须确保std::string str仍然存在,并且_sockethandle_write回调之前不会关闭。您可以用协定_socket的类中的某个成员变量替换堆栈分配的std::string str变量。并将socket_.close();行从respond函数移动到handle_write

希望,我帮了你。

附言:当您执行boost::asio::buffer( str ),时,您不复制字符串的内容,而只是在字符串的数据之上创建一个精简的wpapper。

代码:

_out_buffer( static_cast<void*>( &str.front() ), bytes );

仅在初始化_out_buffer时有效,即在类的构造函数体开始之前。

这个代码相当于

_out_buffer.operator()( static_cast<void*>(&str.front()), bytes )

当然,类mutable_buffer中没有这样的运算符,而这正是编译器所抱怨的。

我认为最简单的事情(但不是最好的)是将该行更改为:

_out_buffer = boost::asio::mutable_buffer(
static_cast<void*>( &str.front() ),
bytes
);