Как прервать сокет BeginReceive ()? - PullRequest
40 голосов
/ 11 января 2011

Естественно, BeginReceive() никогда не закончится, если нет данных. MSDN предлагает , что вызов Close() прервет BeginReceive().

Однако, вызов Close() для сокета также выполняет Dispose() для него, как показано в этот великий ответ , и, следовательно, EndReceive() вызовет исключение, поскольку объект уже удален (и это так!).

Как мне поступить?

Ответы [ 6 ]

49 голосов
/ 12 января 2011

Похоже, что это (очень тупой) дизайн. Вы должны выбросить это исключение в ваш код.

MSDN действительно об этом молчит, но если вы посмотрите на документацию другого асинхронного метода сокетов, BeginConnect () , вот что мы находим:

Чтобы отменить ожидающий вызов на BeginConnect (), закройте Разъем. Когда метод Close () вызывается при асинхронной операции Идет обратный звонок в метод BeginConnect () является называется. Последующий звонок в Метод EndConnect (IAsyncResult) будет бросить исключение ObjectDisposedException в указать, что операция была отменен.

Если это правильный способ для BeginConnect, то, вероятно, так же и для BeginReceive. Это, конечно, плохой дизайн со стороны асинхронного API от Microsoft, потому что заставить пользователя обязательно генерировать и перехватывать исключение, как часть нормального потока, будет раздражать отладчик. У вас действительно нет возможности «ждать», пока операция не будет завершена, потому что Close () - это то, что завершает ее в первую очередь.

2 голосов
/ 26 декабря 2014

Я удивлен, что никто не рекомендовал использовать SocketOptions.

Как только стек выполняет операцию отправки или получения, он связывается с параметрами сокета сокета.

Используйте небольшой тайм-аут для отправки или получения и используйте его перед операцией, поэтому вам все равно, будет ли он изменен во время той же операции на что-то более короткое или длинное.

Это приведет к большему переключению контекста, но не потребует закрытия сокета ни при каком протоколе.

Например:

1) Установить небольшой тайм-аут

2) Выполнять операции

3) Установить тайм-аут больше

Это похоже на использование Blocking = false, но с указанным вами автоматическим таймаутом.

0 голосов
/ 19 сентября 2017

Для соединений через сокет TCP вы можете использовать свойство Connected , чтобы определить состояние сокета, прежде чем пытаться получить доступ к каким-либо удаленным методам.По MSDN:

"Свойство Connected получает состояние подключения Socket на момент последней операции ввода-вывода. Когда возвращается значение false, Socket либо никогда не подключался, либо больше не подключен."

Поскольку он говорит «больше не подключен», это означает, что Close () был ранее вызван в сокете.Если вы проверите, подключен ли сокет в начале получения обратного вызова, исключений не будет.

0 голосов
/ 08 декабря 2016

Вы можете прочитать мое решение этой проблемы здесь (используя комментарий Павла Радзивиловского здесь): UdpClient.ReceiveAsync исправить досрочное завершение

0 голосов
/ 16 февраля 2014

Я тоже боролся с этим, но насколько я могу судить, использование простого логического флага перед вызовом .BeginReceive() также будет работать (поэтому не будет необходимости в обработке исключений). Поскольку у меня уже была обработка начала / остановки, это исправление было связано с одним оператором if (прокрутите вниз до нижней части метода OnReceive()).

if (_running)
{
    _mainSocket.BeginReceive(_data, 0, _data.Length, SocketFlags.None, OnReceive, null);
}                

Если бы я что-то упустил при таком подходе, дайте мне знать!

0 голосов
/ 12 марта 2013

Другое решение - отправить «себе» «управляющее сообщение», используя сокет, связанный с другим портом. Это не совсем прерывание, но оно завершит вашу асинхронную операцию.

...