У меня проблема с Winsock2, использующим IOCP (режим перекрытия ввода-вывода), когда мне нужно закрыть соединение после отправки запрошенных данных.
Я обнаружил, что если я отправлю некоторые данные и закрою сокет сразу после отправки, тогда данные не будут (или будут частично) отправлены, потому что нет времени для отправки пакета до закрытия.
Я также обнаружил, что есть несколько методов отключения, например WSA_SendDisconnect()
, которые я сейчас использую.
Теперь текущая реализация выглядит примерно так:
Отправка данных
Установка флага , представляющего намерение закрыть соединение после отправки
Когда происходит событие IOCP относительно события отправки , если установлен флаг и bytecount > 0
, тогда я снова отправляю пустой буфер , просто чтобы убедиться, что все данные было отправлено
Когда происходит событие IOCP относительно события отправки , если установлен флаг и bytecount == 0
, тогда я сбрасываю флаг и call
WSA_SendDisconnect()
с пустым буфером , для отправки сообщения об отключении
- Когда происходит событие IOCP относительно события отправки без установленного флага и
bytecount == 0
, тогда я вызываю closesocket()
и уничтожаю контекст.
С помощью этой процедуры я мог почти убедиться, что сокет отключится только после отправки всех данных, на самом деле он работает очень хорошо, но при стресс-тестировании программы в некоторых очень редких случаях 10-15 раз из 1000000 тестов что-то не так. (Было бы очень сложно определить точную проблему, программа работает как веб-сервер, и я тестирую ее с помощью apachebench и siege, после теста я получаю сводку, где иногда я вижу 10-15 неудачных запросов .)
Я был почти уверен, что ошибка в том, что сокет не может отправить весь пакет перед закрытием, поэтому я поместил небольшую задержку до socketclose()
, и с тех пор я получаю ноль неудачных запросов, но, конечно, это не так решение, просто способ отфильтровать то, что может вызвать проблему.
В методе, который я реализовал, очевидно, нет надлежащего способа определить, отправил ли сокет все перед закрытием, даже если я начинаю закрывать только после завершения последней фиктивной записи.
Что было бы лучшим решением для закрывать сокет только после того, как весь буфер отправки действительно был записан в сеть?
Ps: я пытался играть с NoDelay и настройкой LingerState , они не помогли.