I’d suggest that you look into platform-dependent facilities. Both Windows and Linux support rate-limiting per-interface, per-process, so probably does OSX as well. That way you don’t need to do anything in your program except configuring the OS/socket – then you can even use synchronous socket I/O and simplify the program. Externally (to the program) configured BW limitation also gives the administrator more flexibility.
Otherwise, estimating transfer bandwidth is non-trivial as the first couple of async_write calls will succeed almost immediately (and will keep succeeding until the socket’s send buffer is full). Token bucket is a simple algorithm for rate-limiting (https://en.wikipedia.org/wiki/Token_bucket)
As to buffer handling: use asio::streambuf. The reader calls prepare() to fill the part of the streambuf with data, async_write calls data() to get the pointer to read data (you can send a subset), and the handler calls consume() with the written number of bytes.
From: Boost-users On Behalf Of Kyle Ketterer via Boost-users
Sent: Thursday, October 25, 2018 00:54
To: boost-users@lists.boost.org
Cc: Kyle Ketterer
Subject: [Boost-users] [asio] Advice on implementing rate-limiting for async_write
Hello fellow Boost friends,
I am looking into some advice on implementing rate-limiting using using Boost ASIO with async_write. I am working on a Enterprise File Backup solution named Vessel @ https://github.com/VesselEnterprise/vessel-backup. My project uses Boost through and through.
The use case is as follows:
* Many users on the same network/subnet may be transferring files at once
* I want to add the feature of being able to limit upload speed to the storage provider (AWS, Azure, etc)
The request starts as follows: (pseudo code)
Let's say we have a string of 1000 bytes we want to send to a server:
const std::string& request //Passed to member function and make_shared<>() to member var on stack to ensure lifetime of async operation
Let's also say we want to limit the transfer speed to 100 bytes/second
size_t transfer_max = 100;
Should we start the request with async_write() on the socket?
//Ensure that no more than transfer_max bytes are sent during the first write
boost::asio::async_write(*m_socket, boost::asio::buffer(http_request, transfer_max), boost::bind(&HttpClient::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred )) ;
Here is where I have questions. In the completion handler handle_write(...), we need to continue to call async_write() until all bytes have been written and determine the current transfer rate, and sleep the thread if necessary. When sending the next set of bytes, is it best to explicitly declare a buffer and consume or use a pointer to the char array?
void HttpClient::handle_write( const boost::system::error_code& e, size_t bytes_transferred )
{
/**
** Do some stuff here to determine transfer rate, and sleep if necessary...
*/
//Pseudocode
if ( transfer_rate > transfer_max )
{
//Possibility 1?
auto buffer = boost::asio::buffer(request, transfer_max);
buffer.consume(total_bytes_transferred_so_far);
boost::asio::async_write(*m_socket, buffer, boost::bind(&HttpClient::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred )) ;
//Possibility 2?
boost::asio::async_write(*m_socket, boost::asio::buffer(&request[total_bytes_transferred_so_far], transfer_max), boost::bind(&HttpClient::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred )) ;
return;
}
//...eof
}
Advice on the topic is greatly appreciated. If I am way off, just let me know :)
Thanks!
--
Kyle Ketterer
mailto:reminisc3@gmail.com>