2015-08-11 2 views
2

Псевдо-кодповышения :: ASIO :: async_receive и 0 байт в гнезде

boost::asio::streambuf my_buffer; 
boost::asio::ip::tcp::socket my_socket; 

auto read_handler = [this](const boost::system::error_code& ec, size_t bytes_transferred) { 
         // my logic 
        }; 

my_socket.async_receive(my_buffer.prepare(512), 
         read_handler); 

При использовании традиционного recv с неблокируемой розеткой, она возвращает -1, когда нет ничего, чтобы читать из сокета.

Но использование async_receive не звонит read_handler, если данных нет, и он бесконечно ждет.

Как реализовать такую ​​логику (асинхронно), которая вызывает read_handler с bytes_transferred == 0 (возможно, с кодом ошибки), когда читать сокета нечего?

(async_read_some имеет такое же поведение).

+1

возвращает -1 * с 'ERRNO == EAGAIN/EWOULDBLOCK' *, когда читать нечего. Zero должен указать, что сверстник отключен. – EJP

+0

@EJP да, 'recv' действует, как вы говорите. Как это сделать с помощью 'async_receive'? – vladon

+0

Я бы ожидал, что он будет действовать таким же образом, если не задокументирован иначе. – EJP

ответ

3

Короче говоря, сразу после начала операции async_receive() отмените ее. Если обработчик завершения вызывается с ошибкой boost::asio::error::operation_aborted, операция блокируется. В противном случае операция чтения завершилась с успехом и прочитана из сокета или не выполнена по другим причинам, например удаленный одноранговый узел, закрывающий соединение.

socket.async_receive(boost::asio::buffer(buffer), handler); 
socket.cancel(); 

В функции инициирования асинхронной операции, неблокирующие чтения будет пытаться быть сделано. Такое поведение тонкость отмечается в документации async_receive():

Независимо от того, завершает асинхронную операцию немедленно или нет, [...]

Следовательно, если операция завершается немедленно с успехом или ошибкой, то обработчик завершения будет готов для вызова и не будет отменен. С другой стороны, если операция блокируется, то она будет помещена в реактор для мониторинга, где она будет отменена.

Можно также получить аналогичное поведение при синхронных операциях, включив режим non-blocking на сокете. Когда сокет установлен на неблокирующийся, синхронные операции, которые будут блокироваться, будут работать с boost::asio::error::would_block.

socket.non_blocking(true); 
auto bytes_transferred = socket.receive(
    boost::asio::buffer(buffer), 0 /* flags */, error); 

Вот полный пример demonstrating эти модели поведения:

#include <array> 
#include <iostream> 
#include <boost/asio.hpp> 
#include <boost/bind.hpp> 

// This example is not interested in the handlers, so provide a noop function 
// that will be passed to bind to meet the handler concept requirements. 
void noop() {} 

void print_status(
    const boost::system::error_code& error, 
    std::size_t bytes_transferred) 
{ 
    std::cout << "error = (" << error << ") " << error.message() << "; " 
       "bytes_transferred = " << bytes_transferred 
      << std::endl; 
} 

int main() 
{ 
    using boost::asio::ip::tcp; 

    // Create all I/O objects. 
    boost::asio::io_service io_service; 
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0)); 
    tcp::socket socket1(io_service); 
    tcp::socket socket2(io_service); 

    // Connect the sockets. 
    acceptor.async_accept(socket1, boost::bind(&noop)); 
    socket2.async_connect(acceptor.local_endpoint(), boost::bind(&noop)); 
    io_service.run(); 
    io_service.reset(); 

    std::array<char, 512> buffer; 

    // Scenario: async_receive when socket has no data. 
    // Within the intiating asynchronous read function, an attempt to read 
    // data will be made. If it fails, it will be added to the reactor, 
    // for monitoring where it can be cancelled. 
    { 
    std::cout << "Scenario: async_receive when socket has no data" 
       << std::endl; 
    socket1.async_receive(boost::asio::buffer(buffer), &print_status); 
    socket1.cancel(); 
    io_service.run(); 
    io_service.reset(); 
    } 

    // Scenario: async_receive when socket has data. 
    // The operation will complete within the initiating function, and is 
    // not available for cancellation. 
    { 
    std::cout << "Scenario: async_receive when socket has data" << std::endl; 
    boost::asio::write(socket2, boost::asio::buffer("hello")); 
    socket1.async_receive(boost::asio::buffer(buffer), &print_status); 
    socket1.cancel(); 
    io_service.run(); 
    } 
    // One can also get the same behavior with synchronous operations by 
    // enabling non_blocking mode. 
    boost::system::error_code error; 
    std::size_t bytes_transferred = 0; 
    socket1.non_blocking(true); 

    // Scenario: non-blocking synchronous read when socket has no data. 
    { 
    std::cout << "Scenario: non-blocking synchronous read when socket" 
       " has no data." << std::endl; 
    bytes_transferred = socket1.receive(
     boost::asio::buffer(buffer), 0 /* flags */, error); 
    assert(error == boost::asio::error::would_block); 
    print_status(error, bytes_transferred); 
    } 

    // Scenario: non-blocking synchronous read when socket has data. 
    { 
    std::cout << "Scenario: non-blocking synchronous read when socket" 
       " has data." << std::endl; 
    boost::asio::write(socket2, boost::asio::buffer("hello")); 
    bytes_transferred = socket1.receive(
     boost::asio::buffer(buffer), 0 /* flags */, error); 
    print_status(error, bytes_transferred); 
    } 
} 

Выход:

Scenario: async_receive when socket has no data 
error = (system:125) Operation canceled; bytes_transferred = 0 
Scenario: async_receive when socket has data 
error = (system:0) Success; bytes_transferred = 6 
Scenario: non-blocking synchronous read when socket has no data. 
error = (system:11) Resource temporarily unavailable; bytes_transferred = 0 
Scenario: non-blocking synchronous read when socket has no data. 
error = (system:0) Success; bytes_transferred = 6