如何在Windows中使用boost asio从命令行异步读取输入?

nic*_*ckb 8 c++ windows boost boost-asio

我发现这个问题询问如何异步读取输入,但只适用于POSIX流描述符,这在Windows上不起作用.所以,我发现这个教程显示我可以使用a而不是使用POSIX流描述符boost::asio::windows::stream_handle.

按照这两个例子,我想出了下面的代码.当我运行它时,我无法在命令提示符中输入任何内容,因为程序会立即终止.我希望它能捕获用户的任何输入,可能是一个std::string,同时允许我的程序中的其他逻辑执行(即从Windows控制台执行异步I/O).

基本上,我试图避免在尝试读取时阻止我的程序stdin.我不知道这是否可以在Windows中使用,因为我还发现这篇文章详述了另一个用户在尝试做同样事情时遇到的问题.

#define _WIN32_WINNT 0x0501
#define INPUT_BUFFER_LENGTH 512

#include <cstdio>
#include <iostream>

#define BOOST_THREAD_USE_LIB // For MinGW 4.5 - (https://svn.boost.org/trac/boost/ticket/4878)
#include <boost/bind.hpp>
#include <boost/asio.hpp>

class Example {
    public:
        Example( boost::asio::io_service& io_service)
            : input_buffer( INPUT_BUFFER_LENGTH), input_handle( io_service)
        {
            // Read a line of input.
            boost::asio::async_read_until( input_handle, input_buffer, "\r\n",
                boost::bind( &Example::handle_read, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
        }
        void handle_read( const boost::system::error_code& error, std::size_t length);
        void handle_write( const boost::system::error_code& error);
    private:
        boost::asio::streambuf input_buffer;
        boost::asio::windows::stream_handle input_handle;
};

void Example::handle_read( const boost::system::error_code& error, std::size_t length)
{
    if (!error)
    {
        // Remove newline from input.
        input_buffer.consume(1);
        input_buffer.commit( length - 1);

        std::istream is(&input_buffer);
        std::string s;
        is >> s;

        std::cout << s << std::endl;

        boost::asio::async_read_until(input_handle, input_buffer, "\r\n",
           boost::bind( &Example::handle_read, this,
               boost::asio::placeholders::error,
               boost::asio::placeholders::bytes_transferred));
    }
    else if( error == boost::asio::error::not_found)
    {
        std::cout << "Did not receive ending character!" << std::endl;
    }
}

void Example::handle_write( const boost::system::error_code& error)
{
    if (!error)
    {
        // Read a line of input.
        boost::asio::async_read_until(input_handle, input_buffer, "\r\n",
           boost::bind( &Example::handle_read, this,
               boost::asio::placeholders::error,
               boost::asio::placeholders::bytes_transferred));
    }
}

int main( int argc, char ** argv)
{
    try {
        boost::asio::io_service io_service;
        Example obj( io_service);
        io_service.run();
    } catch( std::exception & e)
    {
        std::cout << e.what() << std::endl;
    }
    std::cout << "Program has ended" << std::endl;
    getchar();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Sam*_*ler 5

您需要调用io_service::run()启动异步操作的事件处理循环.

class Example {
    public:
        Example( boost::asio::io_service& io_service )
            : io_service(io_service), input_buffer( INPUT_BUFFER_LENGTH), input_handle( io_service)
        {
        }
        void start_reading();
        void handle_read( const boost::system::error_code& error, std::size_t length);
        void handle_write( const boost::system::error_code& error);
    private:
        boost::asio::io_service& io_service;
        boost::asio::streambuf input_buffer;
        boost::asio::windows::stream_handle input_handle;
};

int main( int argc, char * argv)
{
    boost::asio::io_service io_service;
    Example obj( io_service );
    obj.start_reading();

    io_service.run();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • @nickb因为你从未将句柄分配给任何东西,所以你无法从中读取. (2认同)

And*_*hko 5

我只花了一两个小时调查这个话题,所以决定发帖以防止别人浪费时间.

Windows不支持标准输入/输出句柄的IOCP.拿到手柄时GetStdHandle(STD_INPUT_HANDLE),手柄没有FILE_FLAG_OVERLAPPED设置,因此它不支持重叠(异步)IO.但即使你

CreateFile(L"CONIN$",
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
    NULL);
Run Code Online (Sandbox Code Playgroud)

WinAPI只是忽略dwFlagsAndAttributes并再次返回不支持重叠IO的句柄.获取控制台输入/输出的异步IO的唯一方法是使用WaitForSingleObject带有0超时的句柄,这样您就可以检查是否有任何内容可以读取非阻塞.不完全是async IO,但如果它是一个目标,可以避免多线程.

有关控制台API的更多详细信息:https://msdn.microsoft.com/en-us/library/ms686971(v = VS.85).aspx

返回的句柄之间的区别是什么GetStdHandle,CreateFile如下所述:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v = vs.85).aspx.简而言之,差异仅适用于子进程,CreateFile即使在父进程中重定向,也可以访问其控制台输入缓冲区.