2016-02-26 3 views
3

У меня есть проблема, где 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 байт не может читать больше или меньше байт)

+2

Вы можете предоставить [MCVE]? какую версию вы используете? –

+0

Где он говорит, что он должен заполнить буфер? Все методы чтения заданы для возврата количества фактически переданных байтов, поскольку они могут * не заполнять буфер. В случае сокета, если только 148 байтов поступают одновременно, это то, что вы получите. – EJP

+0

@EJP http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/transfer_exactly.html – sehe

ответ

0

«Решен» его сейчас, не обращая внимания на неверную операцию чтения. Мне повезло, что я мог справиться с неизвестным количеством недостающих данных и позже пересинхронировать с потоком. Но похоже, что в будущем мне придется отказаться от Boost :: Asio, когда я больше не могу терпеть пропущенные данные.

Смежные вопросы