检查Win32文件流以获取可用输入

时间:2011-12-01 20:13:29

标签: winapi

我有一个简单的隧道程序,需要同时阻止标准输入和套接字。我目前有一个看起来像这样的程序(错误处理和锅炉板的东西省略):

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$流。

1 个答案:

答案 0 :(得分:2)

使用重叠I / O.然后测试附加到I / O操作的事件,而不是句柄。

对于CONIN$,您还可以查看控制台输入API,例如PeekConsoleInputGetNumberOfConsoleInputEvents

但我真的建议尽可能使用OVERLAPPED(背景)读取,而不是试图像WaitForMultipleObjects那样处理select

由于控制台无法在重叠模式下重叠,因此最简单的选择是等待控制台句柄并使用ReadConsoleInput(然后您必须手动处理控制序列),或者生成专用的工作线程同步ReadFile。如果选择工作线程,则可能需要使用重叠管道读取连接该工作线程与主I / O循环之间的管道。

我从未尝试过的另一种可能性是等待控制台句柄并使用PeekConsoleInput来确定是否要拨打ReadFileReadConsoleInput。这样你应该能够在熟食终端处理的同时获得非阻塞。 OTOH,将控制序列传递给ReadConsoleInput可能会抑制他们应该采取的缓冲区操作行为。


如果两个流是独立处理的,或者几乎是这样,那么为每个流启动一个线程可能更有意义。然后,您可以使用标准输入的阻塞读取。