Boost.Asio - 轮询命名管道

Boost.Asio - polling a named pipe

我正在尝试监听命名管道上的输入。我在 Linux 下使用 Boost.Asio 的 stream_descriptor and async_read。问题是,对 io_service::run() 的调用只会像我希望的那样阻塞,直到第一次读取。在那之后,它只是继续立即调用处理程序并出现 "End of file" 错误,即使我尝试向其附加更多 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 立即 return。我发现让它像我想要的那样工作的唯一方法是关闭并重新打开命名管道,但这似乎是一个 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);
}

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

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

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;
    }
}

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

此外,如果您/想要/阻止行为,您为什么要使用 async_* 界面?

演示

  1. 考虑使用简单的 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";
        }
    }
    
  2. 或者,如果您必须附加到一些您从其他地方获得的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

据 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