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