Boost.Asio - 轮询命名管道

Posted

技术标签:

【中文标题】Boost.Asio - 轮询命名管道【英文标题】:Boost.Asio - polling a named pipe 【发布时间】:2016-01-19 07:07:30 【问题描述】:

我正在尝试侦听命名管道上的输入。我在 Linux 下使用 Boost.Asio 的 stream_descriptor 和 async_read。问题是,对 io_service::run() 的调用只会像我想要的那样阻塞,直到第一次读取。之后,即使我尝试将更多 async_reads 附加到它,它也会立即使用“文件结束”错误调用处理程序。我拥有的代码相当于以下内容:

boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true)

    // buffer and handler probably aren't important for the purposes of this question
    boost::asio::async_read(fifo, buffer, handler);
    io_service.run();

只有第一个 async_read 可以按我的预期工作。随后的 async_reads 立即返回。我发现让它按我想要的方式工作的唯一方法是关闭并重新打开命名管道,但这似乎是一个 hack:

boost::asio::io_service io_service;
while (true)

    int fifo_d = open("/tmp/fifo", O_RDONLY);
    boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
    boost::asio::async_read(fifo, buffer, handler);
    io_service.run();
    close(fifo_d);

谁能告诉我我做错了什么?

更新:这是一个简单的“读取”版本,它允许一些代码简化,问题仍然存在:

int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true) 
    try 
        boost::asio::read(fifo, boost::asio::buffer(buffer));
    
    catch (boost::system::system_error& err) 
        // It loops here with "read: End of file" error
        std::cout << err.what() << std::endl;
    

【问题讨论】:

【参考方案1】:

这不是工作原理。 run() 不打算在循环中调用。如果您坚持,您需要在两者之间致电reset()(根据文档)。

另外,如果您/想要/阻止行为,为什么要使用async_* 接口?

演示

    考虑使用一个简单的iostream来读取fd:

    Live On Coliru

    #include <iostream>
    #include <fstream>
    
    int main() 
        std::ifstream fifo("/tmp/fifo");
    
        std::string word;
        size_t lineno = 0;
        while (fifo >> word) 
            std::cout << "word: " << ++lineno << "\t" << word << "\n";
        
    
    

    或者,如果您必须附加到您从其他地方获得的一些 fd,请使用来自 Boost IOstreams 的 file_descriptor

    Live On Coliru

    #include <boost/iostreams/device/file_descriptor.hpp>
    #include <boost/iostreams/stream.hpp>
    #include <iostream>
    #include <fcntl.h>
    
    int main() 
        namespace io = boost::iostreams;
        using src = io::file_descriptor_source;
        io::stream<src> fifo(src(open("./fifo", O_RDONLY), io::file_descriptor_flags::close_handle));
    
        std::string word;
        size_t number = 0;
        while (fifo >> word) 
            std::cout << "word: " << ++number << "\t" << word << "\n";
        
    
    

两个例子都打印出预期的结果:

word: 1 hello
word: 2 world

【讨论】:

reset() 对我来说并没有真正改变任何东西。我打算稍后在我的程序中使用 async_read,但你是对的,就我的问题而言,简单的读取就足够了。我更新了问题。现在,您的第一个示例中的代码(仅从 ifstream 中读取)具有几乎相同的效果 - 它会阻塞,直到我将一些文本发送到管道,打印该文本然后立即退出。我想做的是无限期地收听该管道上的输入,以便任何其他程序可以随时向我发送数据。我怎样才能做到这一点? 好的,再一次,重新打开管道工作:while (true) std::ifstream fifo("/tmp/fifo"); std::string 行; while (fifo >> line) std::cout @user2478832 确实如此。这只是表明管道的发送端每次都关闭管道。 好的,你的回答帮我找到了我的问题,所以我接受了。谢谢。【参考方案2】:

正如 sehe 所报道的,这不是 boost::asio 的工作方式。 ioservice::run() 方法在有一些工作时以阻塞模式运行。当 ioservice 停止工作时,您必须在进行其他工作之前调用 reset() 方法,这就是为什么在您的第一个代码中 async_read 只执行一次。

这种情况下的常见模式如下所示:

void handler(...) 
    if (!error) 
      // do your work
      boost::asio::async_read(fifo, buffer, handler); // <-- at the end of the handler a subsequent async_read is put to the ioservice, so it never goes out-of-work
    


boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler); // <-- you call async_read only once here.
io_service.run(); //<-- this blocks till an error occurs

【讨论】:

问题是,他明确想要一个阻塞接口。 我试过这样做,但没有成功。经过一些试验,我来到了我发布的循环,以消除我的问题是因为我试图从处理程序中添加一个 async_read 的可能性。无论如何,这几乎是我拥有的代码的第一个版本,并且从未调用处理程序中的 if 条件。在第一次调用时,处理程序将收到“文件结束”错误。

以上是关于Boost.Asio - 轮询命名管道的主要内容,如果未能解决你的问题,请参考以下文章

C++ 使用命名管道

非阻塞命名管道

在命名管道(FIFO)上使用 poll() 时,看起来 OS X 确实存在错误......专家可以确认吗?

命名管道(C#)

Linux进程间通信

Linux - 进程间通信 - 命名管道