7

我已将Boost asio 教程的第 3 步改编为永远运行,并每秒显示一次“tick”和“tock”而不是计数器:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t, int* count)
{
    if( !((*count) % 2) )
        std::cout << "tick\n";
    else
        std::cout << "tock\n";

    ++(*count);

    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
    t->async_wait(boost::bind(print,
          boost::asio::placeholders::error, t, count));
}

int main()
{
  boost::asio::io_service io;

  int count = 0;
  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait(boost::bind(print,
        boost::asio::placeholders::error, &t, &count));

  io.run();

  std::cout << "Final count is " << count << "\n";

  return 0;
}

现在我希望能够异步处理标准输入上的按键。是否有一个 io_service 处理程序可以用来响应按键,而不会阻塞睡眠或等待?

例如,我希望能够实现类似于以下的处理函数:

void handle_keypress(const boost::error_code&,
    char c)
{
    std::cout << "Tap '" << c << "'\n";
}

我希望我对这个处理程序的调用类似于:

  char c = 0;
  boost::asio::stdin_receiver sr(io);
  st.async_wait(boost::bind(handle_keypress, boost::asio::placeholders::error, &c));

  io.run();

这是我可以用 asio 做的事情吗,要么使用内置的服务处理程序,要么自己编写?

编辑,详细说明:

我已经看到了这个问题,但是accepted answer中的链接到代码只是这样做main

 while (std::cin.getline(

我正在编写的应用程序不是我上面概述的这个简单的滴答滴答小玩意儿,而是一个多播服务器。几个工作线程将向多播组发送数据包,响应来自主线程的消息,并将消息发送回主线程。反过来,应用程序将由来自标准输入的输入“驱动”——例如,当用户按下“P”键时,多播广播将暂停,当按下“Q”时,整个事情将关闭. 在主线程中,我对这些输入的响应就是向工作线程发送消息。

上面的while循环在我的场景中不起作用,因为在等待用户的标准输入输入时,主线程将无法处理来自工作线程的消息。来自工作线程的一些消息将生成输出到标准输出。

4

3 回答 3

5

我不相信posix 聊天客户端使用 while 循环或调用std::getline,这是您在我之前的答案中链接到的示例代码。也许您指的是另一个例子?在任何情况下,您都不需要使用io_service::dispatch甚至单独的线程。流描述符的内置设施在这里工作得很好。请参阅我之前对类似问题的回答:使用 aposix::stream_descriptor并分配STDIN_FILENO给它。使用async_read和处理读取处理程序中的请求。

我已经用一种方法修改了你的示例代码来完成这个

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t, int* count)
{
    if( !((*count) % 2) )
        std::cout << "tick\n";
    else
        std::cout << "tock\n";

    ++(*count);

    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
    t->async_wait(boost::bind(print,
          boost::asio::placeholders::error, t, count));
}

class Input : public boost::enable_shared_from_this<Input>
{
public:
    typedef boost::shared_ptr<Input> Ptr;

public:
    static void create(
            boost::asio::io_service& io_service
            )
    {
        Ptr input(
                new Input( io_service )
                );
        input->read();
    }

private:
    explicit Input(
            boost::asio::io_service& io_service
         ) :
        _input( io_service )
    {
        _input.assign( STDIN_FILENO );
    }

    void read()
    {
        boost::asio::async_read(
                _input,
                boost::asio::buffer( &_command, sizeof(_command) ),
                boost::bind(
                    &Input::read_handler,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
                    )
                );
    }

    void read_handler(
            const boost::system::error_code& error,
            const size_t bytes_transferred
            )
    {
        if ( error ) {
            std::cerr << "read error: " << boost::system::system_error(error).what() << std::endl;
            return;
        }

        if ( _command != '\n' ) {
            std::cout << "command: " << _command << std::endl;
        }

        this->read();
    }

private:
    boost::asio::posix::stream_descriptor _input;
    char _command;
};

int main()
{
  boost::asio::io_service io;

  int count = 0;
  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait(boost::bind(print,
        boost::asio::placeholders::error, &t, &count));

  Input::create( io);

  io.run();

  std::cout << "Final count is " << count << "\n";

  return 0;
}

编译、链接和运行

samm:stackoverflow samm$ g++ -I /opt/local/include stdin.cc -L /opt/local/lib -lboost_system -Wl,-rpath,/opt/local/lib
samm:stackoverflow samm$ echo "hello world" | ./a.out
command: h
command: e
command: l
command: l
command: o
command:  
command: w
command: o
command: r
command: l
command: d
read error: End of file
tick
tock
tick
tock
tick
tock
tick
tock
tick
tock
^C
samm:stackoverflow samm$
于 2012-06-12T03:12:27.363 回答
2

始终可以选择在单独的线程中处理标准输入,并通过io_service::dispatch 将任何按键发布到主事件循环

此函数用于要求 io_service 执行给定的处理程序。

io_service 保证仅在当前调用 run()、run_one()、poll() 或 poll_one() 成员函数的线程中调用处理程序。

于 2012-06-07T21:28:07.953 回答
0

在 Linux 下,获取STDIN按键事件的关键在于终端配置。您可以ICANON通过取消设置标志tcsetattr(),然后您将获得每个按键事件。这是一个示例程序,它演示了这一点。请注意,缓冲区大于 1,因此我们也可以一次获取更多字符,如果它们可用(例如,当您复制和粘贴时)。

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

int main()
{
    boost::asio::io_service ioservice;
    boost::asio::posix::stream_descriptor in(ioservice, STDIN_FILENO);
    char buf[10];

    struct termios term_old;
    tcgetattr(STDIN_FILENO, &term_old);
    struct termios newt = term_old;

    newt.c_lflag &= ~(ICANON); // don't wait until EOL
    newt.c_lflag &= ~(ECHO);   // don't echo

    tcsetattr(STDIN_FILENO, TCSANOW, &newt);

    std::function<void(boost::system::error_code, size_t)> read_handler
        = [&](boost::system::error_code ec, size_t len) {
              if (ec) {
                  std::cerr << "exit: " << ec.message() << std::endl;
              } else {
                  buf[len] = '\0';
                  std::cout << "in: " << buf << std::endl;
                  in.async_read_some(boost::asio::buffer(buf), read_handler);
              }
          };

    in.async_read_some(boost::asio::buffer(buf), read_handler);

    ioservice.run();

    tcsetattr(STDIN_FILENO, TCSANOW, &term_old);
}
于 2021-10-29T13:24:19.543 回答