我有一个简单的隧道程序,需要同时阻止标准输入和套接字。我目前有一个看起来像这样的程序(错误处理和锅炉板的东西省略):
HANDLE host = GetStdHandle(STD_INPUT_HANDLE);
SOCKET peer = ...; // socket(), connect()...
WSAEVENT gate = WSACreateEvent();
OVERLAPPED xfer;
ZeroMemory(&xfer, sizeof(xfer));
xfer.hEvent = gate;
WSABUF pbuf = ...; // allocate memory, set size.
// start an asynchronous transfer.
WSARecv(peer, &pbuf, 1, 0, &xfer, 0);
while ( running )
{
// wait until standard input has available data or the event
// is signaled to inform that socket read operation completed.
HANDLE handles[2] = { host, gate };
const DWORD which = WaitForMultipleObjects
(2, handles, FALSE, INFINITE) - WAIT_OBJECT_0;
if (which == 0)
{
// read stuff from standard input.
ReadFile(host, ...);
// process stuff received from host.
// ...
}
if (which == 1)
{
// process stuff received from peer.
// ...
// start another asynchronous transfer.
WSARecv(peer, &pbuf, 1, 0, &xfer, 0);
}
}
该程序就像一个魅力,我可以毫不费力地通过这个隧道程序传输东西。问题是它有一个微妙的错误。
如果我从cmd.exe
以交互模式启动此程序并且标准输入附加到键盘,则按下不产生输入的键(例如Ctrl
键)会使此程序阻塞并忽略套接字上收到的数据。我设法意识到这是因为按任何键表示标准输入句柄和WaitForMultipleObjects()
返回。正如预期的那样,控制进入if (which == 0)
块并调用ReadFile()
块,因为没有可用的输入。
有没有办法检测Win32流上有多少输入?如果是这样,我可以使用它来检查是否有任何输入可用,然后再调用ReadFile()
以避免阻塞。
我知道针对特定类型的流的一些解决方案(特别是串行端口ClearCommError()
和套接字ioctlsocket(socket,FIONBIO,&count)
),但我所知道的没有一种解决方案适用于CONIN$
流。
答案 0 :(得分:2)
使用重叠I / O.然后测试附加到I / O操作的事件,而不是句柄。
对于CONIN$
,您还可以查看控制台输入API,例如PeekConsoleInput
和GetNumberOfConsoleInputEvents
但我真的建议尽可能使用OVERLAPPED(背景)读取,而不是试图像WaitForMultipleObjects
那样处理select
。
由于控制台无法在重叠模式下重叠,因此最简单的选择是等待控制台句柄并使用ReadConsoleInput
(然后您必须手动处理控制序列),或者生成专用的工作线程同步ReadFile
。如果选择工作线程,则可能需要使用重叠管道读取连接该工作线程与主I / O循环之间的管道。
我从未尝试过的另一种可能性是等待控制台句柄并使用PeekConsoleInput
来确定是否要拨打ReadFile
或ReadConsoleInput
。这样你应该能够在熟食终端处理的同时获得非阻塞。 OTOH,将控制序列传递给ReadConsoleInput
可能会抑制他们应该采取的缓冲区操作行为。
如果两个流是独立处理的,或者几乎是这样,那么为每个流启动一个线程可能更有意义。然后,您可以使用标准输入的阻塞读取。