如何在 Windows 中使用 boost asio 从命令行异步读取输入?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7855222/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to asynchronously read input from command line using boost asio in Windows?
提问by nickb
I found this questionwhich asks how to read input asynchronously, but will only work with POSIX stream descriptors, which won't work on Windows. So, I found this tutorialwhich shows that instead of using a POSIX stream descriptor I can use a boost::asio::windows::stream_handle
.
我发现这个问题询问如何异步读取输入,但只适用于 POSIX 流描述符,在 Windows 上不起作用。因此,我发现本教程表明,我可以使用boost::asio::windows::stream_handle
.
Following both examples I came up with the code below. When I run it, I cannot type anything into the command prompt, as the program immediately terminates. I'd like it to capture any input from the user, possibly into a std::string
, while allowing other logic within my program to execute (i.e. perform asynchronous I/O from a Windows console).
按照这两个例子,我想出了下面的代码。当我运行它时,我无法在命令提示符中输入任何内容,因为程序会立即终止。我希望它捕获来自用户的任何输入,可能是输入到std::string
,同时允许我的程序中的其他逻辑执行(即从 Windows 控制台执行异步 I/O)。
Essentially, I'm trying to avoid blocking my program when it attempts to read from stdin
. I do not know if this is possible in Windows, as I also found this postwhich details problems another user encountered when trying to do the same thing.
本质上,当我的程序试图从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;
}
采纳答案by Andriy Tylychko
I just spent an hour or two investigating this topic so decided to post to prevent others to waste their time.
我只花了一两个小时调查这个话题,所以决定发帖以防止其他人浪费时间。
Windows doesn't support IOCP for standard input/output handles. When you take the handle by GetStdHandle(STD_INPUT_HANDLE)
, the handle doesn't have FILE_FLAG_OVERLAPPED
set so it doesn't support overlapped (async) IO. But even if you
Windows 不支持标准输入/输出句柄的 IOCP。当您使用句柄 by 时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);
WinAPI just ignore dwFlagsAndAttributes
and again returns the handle that doesn't support overlapped IO. The only way to get async IO of console input/output is to use the handle with WaitForSingleObject
with 0 timeout so you can check if there's anything to read non-blocking. Not exactly async IO but can avoid multithreading if it's a goal.
WinAPI 只是忽略dwFlagsAndAttributes
并再次返回不支持重叠 IO 的句柄。获得控制台输入/输出的异步 IO 的唯一方法是使用WaitForSingleObject
0 超时的句柄,以便您可以检查是否有任何非阻塞读取。不完全是异步 IO 但如果它是一个目标,可以避免多线程。
More details about console API: https://msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx
有关控制台 API 的更多详细信息:https: //msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx
What's the difference between handles returned by GetStdHandle
and CreateFile
is described here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx. In short the difference is only for a child processes when CreateFile
can give access to its console input buffer even if it was redirected in the parent process.
返回的句柄GetStdHandle
和CreateFile
此处描述的句柄有什么区别:https: //msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx。简而言之,区别仅在于子进程CreateFile
可以访问其控制台输入缓冲区,即使它在父进程中被重定向。
回答by Sam Miller
You need to invoke io_service::run()
to startthe event processing loop for asynchronous operations.
您需要调用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;
}
回答by SoapBox
You need to initialize your stream_handle to the console input handle. You can't use the same stream_handle for input and for output because those are two different handles.
您需要将 stream_handle 初始化为控制台输入句柄。您不能将相同的 stream_handle 用于输入和输出,因为它们是两个不同的句柄。
For input:
对于输入:
Example()
: /* ... */ input_handle( io_service, GetStdHandle(STD_INPUT_HANDLE) )
For output you would use CONSOLE_OUTPUT_HANDLE
. But that is probably overkill, you're unlikely to be pushing that much data into stdout on windows that you'd need to use an async write.
对于输出,您将使用CONSOLE_OUTPUT_HANDLE
. 但这可能是矫枉过正,您不太可能将那么多数据推送到 Windows 上的标准输出中,而您需要使用异步写入。