Почему окна являются выбором () не всегда уведомляющий поток выбор B () когда поток завершения его конец пары сокета?

Ситуация, которую я имею под Windows XP (SP3), сводила меня с ума, и я достигаю конца своей привязи, поэтому возможно, кто-то может обеспечить некоторое вдохновение.

У меня есть C++ сетевая программа (не-GUI). Эта программа создается, чтобы скомпилировать и работать в соответствии с Windows, MacOS/X и Linux, таким образом, это использует выбор () и не блокирующийся ввод-вывод как основание для его цикла событий.

В дополнение к ее сетевым обязанностям эта программа должна считать текстовые команды из stdin и выйти корректно, когда stdin закрывается. В соответствии с Linux и MacOS/X, это достаточно легко - я просто включаю STDIN_FILENO в свое чтение fd_set для выбора (), и выбор () возвраты, когда stdin закрывается. Я проверяю, чтобы видеть, что FD_ISSET (STDIN_FILENO, &readSet) верен, попытайтесь считать некоторые данные с stdin, recv () возвращает 0/EOF, и таким образом, я выхожу из процесса.

В соответствии с Windows, с другой стороны, Вы не можете выбрать на STDIN_FILE_HANDLE, потому что это не реальный сокет. Вы не можете сделать неблокирующихся чтений на STDIN_FILE_HANDLE, также. Это означает, что нет никакого способа считать stdin из основного потока, так как ReadFile () мог бы заблокироваться неограниченно долго, заставив основной поток прекратить выполнять его сетевую функцию.

Без проблем, говорит, что я, я просто буду порождать поток для обработки stdin для меня. Этот поток будет работать в бесконечном цикле, блокирующемся в ReadFile (stdinHandle), и каждый раз, когда ReadFile () данные возвратов, stdin-поток запишет что данные в сокет TCP. Другой конец соединения того сокета будет выбором () 'd на основным потоком, таким образом, основной поток будет видеть, что stdin данные входят по соединению и обрабатывать "stdin" тем же путем, это было бы под любой другой ОС. И если ReadFile () возвращает false, чтобы указать, что stdin закрылся, stdin-поток просто закрывает свой конец парного сокетом так, чтобы основной поток был уведомлен через выбор (), как описано выше.

Конечно, Windows не имеет хорошего socketpair () функцией, таким образом, я должен был прокрутиться, мое собственное использование слушают (), подключение (), и принимают () (как замечено в CreateConnectedSocketPair () функция здесь. Но я сделал это, и это, кажется, работает в целом.

Проблема состоит в том, что это не работает 100%. В частности, если stdin закрывается в нескольких сотнях миллисекунд того, когда программа запускает, приблизительно половина времени, основной поток не получает уведомления, что stdin-конец парного сокетом был закрыт. Под чем я подразумеваю то есть, я вижу (моим printf () - отладка), что stdin-поток назвал closesocket () на его сокете, и я вижу, что основной поток является выбором () - луг на связанном сокете (т.е. другой конец парного сокетом), но выбор () никогда не возвращается, как это должно... и если это действительно возвращается, из-за некоторого другого сокета, выбирающего ready-whatever, FD_ISSET (main_thread_socket_for_socket_pair, &readSet) возвращается 0, как будто соединение не было закрыто.

На данном этапе единственная гипотеза, которую я имею, - то, что существует ошибка в выборе Windows () реализация, которая заставляет выбор основного потока () не замечать, что другой конец парного сокетом закрылся stdin-потоком. Есть ли другое объяснение? (Обратите внимание, что об этой проблеме сообщили в соответствии с Windows 7 также, хотя я не посмотрел на него лично на той платформе),

6
задан Jeremy Friesner 28 January 2010 в 05:20
поделиться

6 ответов

Для записи, эта проблема оказалась совсем другой проблемой, не имеет отношения к потокам, Windows или стандартному вводу. Фактическая проблема заключалась в взаимоблокировке между процессами, когда родительский процесс был заблокирован, ожидая завершения дочерних процессов, но иногда дочерние процессы блокировались одновременно, ожидая, пока родительский процесс предоставит им некоторые данные, и поэтому ничто не могло двигаться вперед.

Приносим извинения всем за то, что потратили время на отвлекающий маневр; если есть стандартный способ закрыть это дело как необоснованное, дайте мне знать, и я это сделаю.

-Jeremy

1
ответ дан 17 December 2019 в 22:13
поделиться

Я учиюсь в вашем коде. В CreateConnectedsocketPair () Socket1 используется для прослушивания (), и NewFD используется для данных отправки / RECV. Итак, почему «Socket1 = NewFD»? Как закрыть ListenFD тогда?

0
ответ дан 17 December 2019 в 22:13
поделиться

Возможно ли, что у вас гоночное состояние? Eg. Убедитесь ли вы, что функция CreateConnectedSocketPair() определенно вернулась до того, как у stdin-thread появится возможность попробовать закрыть свой сокет?

.
0
ответ дан 17 December 2019 в 22:13
поделиться

Не решение, но в качестве обходного пути, не могли бы вы отправить какое-то волшебное сообщение "stdin has closed" через TCP-сокет и отключите принимающую сторону его сокет, когда он это увидит, и запустит любой обработчик 'stdin has closed'?

0
ответ дан 17 December 2019 в 22:13
поделиться

Честно говоря, ваш код слишком длинный, и у меня сейчас нет времени на него тратить.

Скорее всего, проблема в том, что в некоторых случаях закрытие сокета не вызывает постепенного (FIN) завершения работы.

Проверка исключений, возвращаемых из вашего выбора, может выявить остальные случаи. Существует также (небольшая) вероятность того, что в сокет не отправляется уведомление о том, что другой конец закрыт. В этом случае нет другого способа, кроме тайм-аутов или сообщений «keep alive» / ping между конечными точками, чтобы узнать, что сокет закрыт.

Если вы хотите точно выяснить, что происходит, отключите wirehark и поищите FIN и RST (и отсутствие чего-либо). Если вы видите правильную последовательность FIN, когда ваш сокет закрыт, значит, проблема в вашем коде. если вы видите RST, он может быть пойман исключениями, а если вы ничего не видите, вам нужно будет разработать способ в своем протоколе «пинговать» каждую сторону соединения, чтобы убедиться, что они все еще живы, или установить достаточно короткий тайм-аут для дополнительных данных.

0
ответ дан 17 December 2019 в 22:13
поделиться

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

Вы сказали: "Нельзя делать неблокирующие чтения и на STDIN_FILE_HANDLE. Это означает, что нет возможности читать stdin из главного потока, так как ReadFile() может блокировать на неопределенное время", но это просто не вся история. Посмотрите на ReadConsoleInput, WSAEventSelect и WaitForMultipleObjects. Хэндл stdin будет сигнализировать только тогда, когда есть вход, а ReadConsoleInput вернётся немедленно (практически та же идея, что и select() в Unix).

Или используйте ReadFileEx и WaitForMultipleObjectsEx, чтобы консоль считывала с APC (что не так уж и асинхронно, она запускается в главном потоке и только во время WaitForMultipleObjectsEx или другой явной функции ожидания).

Если вы хотите придерживаться использования второго потока для получения асинхронного ввода/вывода на stdin, то вы можете попробовать закрыть передаваемый хэндл, чтобы выбрать, вместо того, чтобы делать выключение сокета (через closesocket на другом конце). По моему опыту, select() имеет тенденцию возвращаться очень быстро, когда закрывается один из fds, которого он ждёт.

Или, может быть, ваша проблема в другом. В документах select говорится: "Для сокетов, ориентированных на соединение, читабельность может также указывать на то, что запрос на закрытие сокета был получен от равного пользователя". Обычно вы посылаете этот "запрос на закрытие сокета", вызывая shutdown(), а не closesocket().

0
ответ дан 17 December 2019 в 22:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: