Как надежно обнаружить отключенный сокет TCP с помощью MsgWaitForMultipleObjects?

Twisted включает в себя реактор, реализованный поверх MsgWaitForMultipleObjects . По-видимому, у реактора есть проблемы с определением момента окончания TCP-соединения, по крайней мере, в случае, когда одноранговый узел отправляет несколько байтов, а затем быстро закрывает соединение. Что, кажется, происходит:

  1. Реактор вызывает MsgWaitForMultipleObjects с некоторым сокетом ручки и QS_ALLINPUT.
  2. Вызов завершается и указывает, что дескриптор сокета в этом состоянии (т. е. имеет байты, ожидающие чтения и закрытые узлом) активен.
  3. Реактор отправляет это уведомление общая реализация TCP.
  4. Реализация TCP считывает доступные байты из сокета. Есть некоторые, они доставляются в код приложения.
  5. Управление возвращается к реактору, который в конце концов снова вызывает MsgWaitForMultipleObjects.
  6. MsgWaitForMultipleObjects больше никогда не указывает, что дескриптор активен. Реализация TCP больше никогда не просматривает сокет, поэтому она никогда не сможет обнаружить, что соединение закрыто.

Это создает впечатление, что MsgWaitForMultipleObjects — это механизм уведомления, запускаемый фронтом. документация MSDN говорит:

Waits until one or all of the specified objects are in the signaled state
or the time-out interval elapses.

Это не похоже на краевое срабатывание. Это похоже на срабатывание уровня.

Действительно ли MsgWaitForMultipleObjects срабатывает по фронту? Или это вызвано уровнем, и это неправильное поведение вызвано каким-то другим аспектом его поведения?

Дополнение Документы MSDN для WSAEventSelect немного подробнее объясняют, что здесь происходит, включая указание на то, что FD_CLOSE в основном является разовым событием. После того, как он сигнализируется один раз, вы никогда не получите его снова. Это в какой-то мере объясняет, почему у Twisted такая проблема. Однако мне все еще интересно узнать, как эффективно использовать MsgWaitForMultipleObjects с учетом этого ограничения.


person Jean-Paul Calderone    schedule 29.09.2011    source источник
comment
Я не могу понять код, но он нигде не вызывает WSAEnumNetworkEvents(), чтобы определить, какие сетевые события действительно произошли. Я считаю, что вы правы в том, что FD_CLOSE — это одноразовая сделка, поэтому вам нужно где-то установить флаг, чтобы сообщить себе, что соединение было закрыто.   -  person Luke    schedule 29.09.2011


Ответы (1)


Чтобы использовать WSAEventSelect и дифференцировать действия, вам нужно вызвать WSAEnumNetworkEvents. Убедитесь, что вы обрабатываете каждое зарегистрированное событие, а не только первое.

WSAAsyncSelect позволяет легко определить причину и часто используется вместе с MsgWaitForMultipleObjects.

Таким образом, вы можете использовать WSAAsyncSelect вместо WSAEventSelect.

Кроме того, я думаю, что у вас есть фундаментальное непонимание разницы между запуском по фронту и запуском по уровню. Ваши рассуждения, похоже, больше связаны с автоматическим сбросом, а не с ручным сбросом.

person Ben Voigt    schedule 29.09.2011
comment
Спасибо. WSAAsyncSelect выглядит интересно. Из его документации кажется, что несколько событий могут не понадобиться, поскольку FD_CLOSE не доставляется до тех пор, пока не будут прочитаны все ожидающие данные. Как вы думаете, достаточно ли этой гарантии, чтобы сделать подход с одним событием работоспособным? Кроме того, что касается срабатывания по фронту и уровню, я написал эти комментарии до того, как узнал, что сброс происходит с некоторыми событиями; в свете сброса терминология края и уровня вообще не кажется применимой. Ты бы согласился с этим? - person Jean-Paul Calderone; 29.09.2011
comment
@Jean-PaulCalderone: Следующее предложение: Приложение должно проверять оставшиеся данные после получения FD_CLOSE, чтобы избежать любой возможности потери данных, что, кажется, противоречит предыдущему. Но похоже, что ваша проблема не в том, чтобы определить, читать ли данные после закрытия, а в том, чтобы определить разницу между закрытым сокетом и данными в буфере для начала. - person Ben Voigt; 29.09.2011
comment
Вы можете связать с сокетом только один объект события через WSAEventSelect, но этот объект события может уведомлять вас о нескольких (типах) сетевых событиях. Обычно вас интересуют только FD_CONNECT/FD_ACCEPT, FD_READ и FD_CLOSE. - person Luke; 29.09.2011
comment
@Luke: Но как узнать, какие сетевые события устанавливают событие ядра? Ах, WSAEnumNetworkEvents недостающая часть. - person Ben Voigt; 29.09.2011
comment
Комбинация WSAEventSelect/WSAEnumNetworkEvents кажется выигрышной. WSAAsyncSelect оказывается не очень полезным, так как у меня нет окна. Спасибо! - person Jean-Paul Calderone; 30.09.2011