У меня есть проблема, где boost::asio::async_read
терпит неудачу на второй вызов, странным образом:Почему boost :: asio :: async_read не считывает запрошенное количество байтов?
std::atomic<error_code> ec(asio::error::would_block);
size_t len = 0;
// 1st call
asio::async_read(socket,
asio::buffer(buffer+X),
asio::transfer_exactly(512-X),
[&] (error_code const& err, size_t bytesTransferred)
{
len = bytesTransferred;
ec.store(err, std::memory_order_release);
}
);
/////// ... wait for read to complete ...
// 2nd call
asio::async_read(socket,
asio::buffer(buffer),
asio::transfer_exactly(512),
[&] (error_code const& err, size_t bytesTransferred)
{
len = bytesTransferred;
ec.store(err, std::memory_order_release);
}
);
Константа X
потому, что у меня есть какие-то данные уже, что я по-другому, поэтому первое чтение меньше. Скажем, X = 364, тогда bytesTransferred
будет 148 в первый раз. Моя проблема, однако, в том, что второй прочитал снова возвращает 148 байтов, хотя что чтение было для 512 байтов точно.
Я сбит с толку. Второй вызов не имеет условия ошибки (я проверил err
). bytesTransferred
- это аргумент, переданный мне aync_read
, и это 148 байтов в два раза. В первый раз он соответствует asio::transfer_exactly(148)
выше в стеке. Во второй раз столбец явно имеет asio::transfer_exactly(512)
. Что здесь происходит?
В частности, этот второй вызов Третий вызов снова считывает 512 байт, но также получает 512 байт.
[MCVE]
#include <iostream>
#include <atomic>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/read.hpp>
// Minimal example, code that works has error checking removed. Class members turned itno globals etc.
namespace {
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::socket sock(io_service);
std::vector<char> data(512);
boost::asio::mutable_buffers_1 buffer(&data[0], data.size());
unsigned read_counter = 1;
std::atomic<unsigned> read_timeout;
}
boost::system::error_code openSocket(const std::string &server,
const std::string &port)
{
boost::system::error_code error = boost::asio::error::host_not_found;
using boost::asio::ip::tcp;
tcp::resolver::query query(server, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
while (error && endpoint_iterator != end)
{
sock.close();
sock.connect(*endpoint_iterator++, error);
}
if (error)
{
std::cerr << "No route\n";
sock.close(); // Would be wrong to leave it open.
}
return error;
}
int read(size_t bytesNeeded)
{
size_t buffer_len = boost::asio::buffer_size(buffer);
size_t byteShift = buffer_len - bytesNeeded; // Read into back of buffer.
const int timeoutSeconds = 10;
boost::asio::deadline_timer deadline(io_service);
deadline.expires_from_now(boost::posix_time::seconds(timeoutSeconds)); // This will reset any outstanding timer
read_counter += 2; // If we'd use +1, after 4 billion cycles it would reset to 0
read_timeout.store(0, std::memory_order_release); // 0 = no timeout.
unsigned read_counter_copy = read_counter; // Can't capture global.
deadline.async_wait([read_counter_copy](boost::system::error_code const&) {
// read_timeout is very intentionally captured by value - timeout events are numbered
read_timeout.store(read_counter_copy, std::memory_order_release); }
);
// Start reading "asynchronously", wait for completion or timeout:
std::atomic<boost::system::error_code> ec(boost::asio::error::would_block);
size_t len = 0;
boost::asio::async_read(sock, boost::asio::buffer(buffer + byteShift), boost::asio::transfer_exactly(bytesNeeded),
[&, bytesNeeded](boost::system::error_code const& err, size_t bytesTransferred)
{
if (bytesTransferred != bytesNeeded) {
std::cout << bytesTransferred << " , " << err.message() << std::endl;
}
len = bytesTransferred;
ec.store(err, std::memory_order_release);
}
);
do {
io_service.run_one();
} while (read_timeout.load(std::memory_order_acquire) != read_counter && // Continue if the **last** read didn't time out
(ec.load(std::memory_order_acquire) == boost::asio::error::would_block) && // ec.store() not called,
!io_service.stopped()); // and program still running.
deadline.cancel(); // This will set read_timeout, if it wasn't set yet. But we ignore it from now on.
if (ec.load(std::memory_order_acquire))
{
std::cerr << "oops\n"; // Real error handling omitted.
throw std::runtime_error("");
}
else if (read_timeout == read_counter)
{
std::cerr << "timeout\n";
}
else if (len != bytesNeeded)
{
// This is the real problem.
std::cerr << "Asked " << bytesNeeded << " got " << len;
}
return (int)len;
}
int main(int argc, char* argv[])
{
do try {
::openSocket("192.168.6.30", "80");
read(148); // Assume that data[] already has 364 bytes on the first call.
for (;;)
{
read(512); // Full buffers on every subsequent call.
// Do something with data[] here.
}
}
catch (std::runtime_error) { } while (true);
}
do try catch while
необходимо потому, что ошибка происходит только после того, как я отключаю другую сторону. После второго вызова read(148)
следующее чтение (512) `не выполняется.
[обновление] Это не только transfer_exactly
. С transfer_at_least(512)
Я также получаю ту же проблему, один лишний 148 байт прочитал. (Оба должны вести себя так же, как чтение по крайней мере 512 байт в буфер, что это только 512 байт не может читать больше или меньше байт)
Вы можете предоставить [MCVE]? какую версию вы используете? –
Где он говорит, что он должен заполнить буфер? Все методы чтения заданы для возврата количества фактически переданных байтов, поскольку они могут * не заполнять буфер. В случае сокета, если только 148 байтов поступают одновременно, это то, что вы получите. – EJP
@EJP http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/transfer_exactly.html – sehe