Как правильно закрыть сокет (с IOCP) после отправки? - PullRequest
0 голосов
/ 27 марта 2019

У меня проблема с Winsock2, использующим IOCP (режим перекрытия ввода-вывода), когда мне нужно закрыть соединение после отправки запрошенных данных.

Я обнаружил, что если я отправлю некоторые данные и закрою сокет сразу после отправки, тогда данные не будут (или будут частично) отправлены, потому что нет времени для отправки пакета до закрытия.

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

Теперь текущая реализация выглядит примерно так:

  1. Отправка данных

  2. Установка флага , представляющего намерение закрыть соединение после отправки

  3. Когда происходит событие IOCP относительно события отправки , если установлен флаг и bytecount > 0, тогда я снова отправляю пустой буфер , просто чтобы убедиться, что все данные было отправлено

  4. Когда происходит событие IOCP относительно события отправки , если установлен флаг и bytecount == 0, тогда я сбрасываю флаг и call WSA_SendDisconnect() с пустым буфером , для отправки сообщения об отключении

  5. Когда происходит событие IOCP относительно события отправки без установленного флага и bytecount == 0, тогда я вызываю closesocket() и уничтожаю контекст.

С помощью этой процедуры я мог почти убедиться, что сокет отключится только после отправки всех данных, на самом деле он работает очень хорошо, но при стресс-тестировании программы в некоторых очень редких случаях 10-15 раз из 1000000 тестов что-то не так. (Было бы очень сложно определить точную проблему, программа работает как веб-сервер, и я тестирую ее с помощью apachebench и siege, после теста я получаю сводку, где иногда я вижу 10-15 неудачных запросов .)

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

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

Что было бы лучшим решением для закрывать сокет только после того, как весь буфер отправки действительно был записан в сеть?

Ps: я пытался играть с NoDelay и настройкой LingerState , они не помогли.

...