2009-01-18 109 views
11

我正在尝试使用boost :: asio从串口上的设备读取和写入。当没有东西需要读取时,boost :: asio:read()和boost :: asio :: serial_port :: read_some()模块。相反,我想检测这种情况并写一个命令到端口来启动设备。如何使用asio执行非阻塞式读取?

我该如何检测没有数据可用?

如果有必要,我可以做所有事情都是异步的,我只想避免额外的复杂性。

回答

17

实际上,您有几个选项。您可以使用串口的内置async_read_some功能,也可以使用独立功能boost::asio::async_read(或async_read_some)。

您仍然会遇到有效“阻止”的情况,因为除非(1)数据已被读取或(2)发生错误,否则它们都不会调用回调。为了解决这个问题,你需要使用一个deadline_timer对象来设置超时。如果超时触发,则没有数据可用。否则,您将读取数据。

增加的复杂性并不是那么糟糕。您将最终得到两个具有类似行为的回调。如果“读取”或“超时”回调触发错误,则知道这是比赛失败者。如果任何一方发生错误,那么你知道这是比赛获胜者(你应该取消另一个电话)。在您将阻止拨打read_some的地方,您现在将拨打电话io_svc.run()。当您调用run时,您的功能仍会像以前一样阻止,但这次您可以控制持续时间。

下面是一个例子:

void foo() 
{ 
    io_service  io_svc; 
    serial_port ser_port(io_svc, "your string here"); 
    deadline_timer timeout(io_svc); 
    unsigned char my_buffer[1]; 
    bool   data_available = false; 

    ser_port.async_read_some(boost::asio::buffer(my_buffer), 
     boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout), 
        boost::asio::placeholders::error, 
        boost::asio::placeholders::bytes_transferred)); 
    timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>)); 
    timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port), 
        boost::asio::placeholders::error)); 

    io_svc.run(); // will block until async callbacks are finished 

    if (!data_available) 
    { 
    kick_start_the_device(); 
    } 
} 

void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred) 
{ 
    if (error || !bytes_transferred) 
    { 
    // No data was read! 
    data_available = false; 
    return; 
    } 

    timeout.cancel(); // will cause wait_callback to fire with an error 
    data_available = true; 
} 

void wait_callback(serial_port& ser_port, const boost::system::error_code& error) 
{ 
    if (error) 
    { 
    // Data was read and this timeout was canceled 
    return; 
    } 

    ser_port.cancel(); // will cause read_callback to fire with an error 
} 

这应该让你开始在这里和那里只有一些调整,以满足您的特定需求。我希望这有帮助!

另一个注意事项:不需要额外的线程来处理回调。一切都在run()的呼叫中处理。不知道你是否已经意识到了这一点......

+0

我已经使用这种方法,它运作良好,但只在第一次。下一次调用io_svc.run()会立即返回。我需要额外做些事吗?感谢 – Schiavini 2013-02-04 10:44:57

+3

显然我不得不在io_svc.run()之后调用io_svc.reset()。 – Schiavini 2013-02-04 11:31:23

1

你必须使用自由函数asio :: async_read。

1

它实际上是一个很多比答案更简单的在这里已经暗示,你可以同步做:

假设你的阻塞读是这样的:

size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint); 

然后你

socket.non_blocking(true); 
size_t len = 0; 
error = boost::asio::error::would_block; 
while (error == boost::asio::error::would_block) 
    //do other things here like go and make coffee 
    len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error); 
std::cout.write(recv_buf.data(), len); 

更换您使用的几乎所有的发送/接收方法有receive_from的替代重载形式。他们不幸采取了一个标志参数,但0似乎工作正常。