Как прервать BeginReceive сокета()?

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

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

как я должен поступить?

6 ответов


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

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

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

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


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

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

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

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

для пример:

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

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

3) установить timeout больше

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


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


для соединений сокетов TCP вы можете использовать подключено свойство для определения состояния сокета перед попыткой доступа к любым удаленным методам. В MSDN:

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

поскольку он говорит "больше не подключен", это означает, что Close () ранее вызывался на разъем. Если вы проверите, подключен ли сокет в начале обратного вызова receive, не будет никаких исключений.


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


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

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

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