Ситуация, которая возникла у меня под Windows XP (SP3), сводила меня с ума, и я дошел до конца, так что, возможно, кто-то может вдохновить меня.
У меня есть сетевая программа на C ++ (без графического интерфейса). Эта программа создана для компиляции и запуска под Windows, MacOS / X и Linux, поэтому она использует select () и неблокирующий ввод-вывод в качестве основы для своего цикла событий.
В дополнение к своим сетевым обязанностям этой программе необходимо читать текстовые команды из stdin и корректно завершать работу при закрытии stdin. В Linux и MacOS / X это достаточно просто - я просто включаю STDIN_FILENO в свой read fd_set для select (), а select () возвращается при закрытии stdin. Я проверяю, что FD_ISSET (STDIN_FILENO, & readSet) истинно, пытаюсь прочитать некоторые данные из stdin, recv () возвращает 0 / EOF, и поэтому я выхожу из процесса.
С другой стороны, под Windows вы не можете выбрать STDIN_FILE_HANDLE, потому что это не настоящий сокет. Вы также не можете выполнять неблокирующее чтение на STDIN_FILE_HANDLE. Это означает, что нет возможности читать stdin из основного потока, поскольку ReadFile () может блокироваться на неопределенное время, в результате чего основной поток перестает обслуживать свою сетевую функцию.
Нет проблем, говорю я, я просто создам поток для обработки стандартного ввода за меня. Этот поток будет работать в бесконечном цикле с блокировкой в ReadFile (stdinHandle), и всякий раз, когда ReadFile () возвращает данные, поток stdin будет записывать эти данные в сокет TCP. Другой конец соединения этого сокета будет выбран () d на основном потоке, поэтому основной поток будет видеть данные stdin, поступающие через соединение, и обрабатывать "stdin" так же, как и в любой другой ОС. И если ReadFile () возвращает false, чтобы указать, что stdin закрыт, stdin-thread просто закрывает свой конец пары сокетов, так что основной поток будет уведомлен через select (), как описано выше.
Конечно, в Windows нет красивой функции socketpair (), поэтому мне пришлось свернуть свою собственную, используя listen (), connect () и accept () (как видно из функции CreateConnectedSocketPair () здесь. Но я сделал это, и, похоже, в целом это работает.
Проблема в том, что он не работает на 100%. В частности, если stdin закрывается в течение нескольких сотен миллисекунд после запуска программы, примерно в половине случаев основной поток не получает никакого уведомления о закрытии stdin-end пары сокетов. Я имею в виду, что я вижу (по моей printf () - отладке), что stdin-thread вызвал closesocket () в своем сокете, и я вижу, что основной поток select () - ing на связанном socket (то есть другой конец пары сокетов), но select () никогда не возвращается, как должен ... и если он действительно возвращается из-за того, что какой-то другой сокет выбирает готовый для чего угодно, FD_ISSET (main_thread_socket_for_socket_pair, & readSet) возвращает 0, как если бы соединение не было закрыто.
На данный момент у меня есть единственная гипотеза, что в реализации select () Windows есть ошибка, из-за которой функция select () основного потока не замечает, что другой конец пары сокетов закрыт потоком stdin. Есть другое объяснение? (Обратите внимание, что об этой проблеме также сообщалось в Windows 7, хотя я лично не рассматривал ее на этой платформе)