closesocket() не завершает ожидающие операции IOCP

В настоящее время я работаю над серверным приложением на C++. Моими главными вдохновителями являются эти примеры:

Пример Windows SDK IOCP

Пример программы порта завершения ввода-вывода IPv4/IPv6

Мое приложение сильно похоже на эти (socketobj, packageobj,...).

В целом у меня приложение работает без проблем. Единственные вещи, которые до сих пор вызывают у меня проблемы, это полуоткрытые соединения.

Моя стратегия для этого такова: я проверяю каждого подключенного клиента за определенный период времени и подсчитываю «счетчик бездействия». Если происходит одно завершение, я сбрасываю этот таймер. Если счетчик Idle становится слишком большим, я устанавливаю логическое значение, чтобы другие потоки не публиковали операции, а затем вызываю closesocket().

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

Короче говоря: это не мой случай. Я провел несколько тестов с моим приложением testclient и отладкой cout и точек останова и обнаружил, что ожидающие операции для закрытых сокетов не завершаются (даже после ожидания 10 минут). Я также уже пробовал с вызовом shutdown() перед closesocket(), и оба не возвращали ошибок.

Что я делаю неправильно? Это случается с кем-то еще? Документация MSDN неверна? Какие есть альтернативы?

Сейчас я подумываю о функции "задержки" или о явной отмене каждой операции с помощью функции CancelIoEx()

Изменить: (спасибо за ответы)

Вчера вечером я добавил связанный список для каждого sockedobj, чтобы хранить per io obj ожидающих операций. С этим я попробовал функцию CancelIOEx(). Функция вернула 0, а GetLastError() вернула ERROR_NOT_FOUND для большинства операций.

Безопасно ли в этом случае просто освобождать Io Obj?

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

Отрезанный код следует как можно скорее.


person Woife    schedule 12.10.2017    source источник
comment
Предоставьте минимально воспроизводимый пример.   -  person Harry Johnston    schedule 12.10.2017
comment
Если получатель не получает, конечно отправитель заблокируется, если только он не находится в неблокирующем режиме, которым не является IOCP. Ваш тест недействителен.   -  person user207421    schedule 13.10.2017
comment
@EJP Означает ли это, что у меня нет возможности обнаружить это заранее? Кажется, что WSASend() можно опубликовать, но он никогда не будет завершен. Как я могу улучшить тест?   -  person Woife    schedule 13.10.2017
comment
ERROR_NOT_FOUND, вероятно, означает, что в рассматриваемых сокетах нет ожидающих операций ввода-вывода. Что, вероятно, означает, что либо Windows, либо ваш код удалил исходные уведомления о завершении, гораздо более вероятно, что это ваш код.   -  person Harry Johnston    schedule 13.10.2017
comment
Ваш тестовый клиент должен прочитать все, что доступно в сокете.   -  person user207421    schedule 13.10.2017
comment
когда последний дескриптор сокета закрывается и он привязывается к iocp - все операции ввода-вывода на нем отменяются (как минимум начинаются с win7), и это небезопасная и фатальная ошибка, просто освободите объект Io Obj до завершения ввода-вывода   -  person RbMm    schedule 19.10.2017


Ответы (1)


  • Параметр «задержка» может использоваться для сброса соединения, но таким образом вы (а) потеряете данные и (б) доставите сброс партнеру, что может напугать его.
  • Если вы думаете о положительном тайм-ауте, это не очень поможет.
  • Отключение для чтения должно завершать операции чтения, но отключение для записи ставится в очередь только после ожидающих операций записи, поэтому оно совсем не помогает.
  • Если проблема заключается в незавершенных операциях записи, их придется отменить.
person user207421    schedule 12.10.2017
comment
Спасибо за ваш ответ. Я бы не заботился о потере данных. Цель моего приложения состоит в том, чтобы клиенты обычно подключались навсегда и циклически отправляли одни и те же данные, поэтому, если клиент теряет соединение и оставляет полуоткрытый сокет, у меня нет проблем с тем, чтобы просто убить его. Что для меня важно: приложение должно уметь восстанавливать память и все, чтобы нормально шло дальше. Что вы имеете в виду под напугать сверстников? - person Woife; 12.10.2017
comment
Подожди. Если клиент закроет соединение, сервер read() вернет -1, после чего вы закроете канал. Вам вообще не нужно принимать никаких специальных мер по этому поводу: это просто нормальная часть сетевого программирования. «Напугать партнера» означает, что он получит сброс соединения вместо обычного завершения потока, как только что было описано: но если это клиент делает закрытие, это, конечно, не применяется. Мне казалось, что единственный способ, при котором ваши операции ввода-вывода не будут завершены, должен заключаться в том, что они ожидают записи. Теперь кажется, что у вас есть ошибка в коде обработки чтения. - person user207421; 12.10.2017
comment
Извините за это очень долгое время ответа. В конце концов все было так, как вы все предположили: проблема была во мне. Функция closesocket() соответствует назначению. Однажды у меня была проблема, когда одно соединение не было завершено должным образом, но я не мог воспроизвести его. Я отметил это как ответ, потому что думаю, что это хорошее резюме упомянутых возможностей. Я хотел бы добавить одну вещь: если вы разрабатываете приложение IOCP, вы также должны позаботиться о частичных отправках (потому что они обычно не должны происходить) и разорвать соединение, если это произойдет. В большинстве примеров это никого не волнует, но это важно. - person Woife; 24.11.2017