Проверка файловых потоков Win32 на доступный ввод - PullRequest
0 голосов
/ 02 декабря 2011

У меня есть простая туннельная программа, которая должна одновременно блокировать стандартный ввод и сокет. В настоящее время у меня есть программа, которая выглядит следующим образом (обработка ошибок и прочее),

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 Ответ

2 голосов
/ 02 декабря 2011

Используйте перекрывающийся ввод / вывод. Затем протестируйте событие, присоединенное к операции ввода-вывода, а не к дескриптору.

Для CONIN$, в частности, вы также можете взглянуть на API ввода консоли, такие как PeekConsoleInput и GetNumberOfConsoleInputEvents

Но я действительно рекомендую использовать OVERLAPPED (фоновое) чтение везде, где это возможно, и не пытаться обрабатывать WaitForMultipleObjects как select.

Поскольку консоль не может перекрываться в режиме перекрытия, вашими простейшими вариантами являются ожидание на дескрипторе консоли и использование ReadConsoleInput (тогда вам придется обрабатывать управляющие последовательности вручную) или порождение выделенного рабочего потока для синхронного ReadFile. Если вы выбираете рабочий поток, вы можете затем соединить канал между этим рабочим и основным циклом ввода-вывода, используя чтение перекрывающихся каналов.

Другой возможностью, которую я никогда не пробовал, было бы подождать на дескрипторе консоли и использовать PeekConsoleInput, чтобы выяснить, вызывать ли ReadFile или ReadConsoleInput. Таким образом, вы сможете получить неблокирование вместе с обработкой обработанного терминала. OTOH, передача управляющих последовательностей в ReadConsoleInput может препятствовать действиям по манипулированию буфером, которые они должны были выполнять.


Если два потока обрабатываются независимо или почти независимо, может иметь смысл начинать поток для каждого из них. Затем вы можете использовать блокировку чтения со стандартного ввода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...