13

Я использую NetworkStream & TcpClient для асинхронного приема данных с помощью BeginRead. Мне нужно применить тайм-аут к этой операции, так что через определенное время считывание будет прервано.BeginReceive/BeginRead таймауты

Насколько я могу сказать, это не поддерживается в NetworkStream или TcpClient - есть свойство ReceiveTimeout, но это, похоже, применимо только к синхронному эквиваленту - «Чтение».

Даже основной класс Socket, похоже, не поддерживает таймауты в методе BeginReceive.

Я искал эту проблему, и единственная предлагаемая резолюция, которую я видел, - это создать еще один фоновый поток, чтобы отменить операцию, если она не завершена в течение периода ожидания. Это кажется ужасным взломом. Наверняка есть лучший способ?

ответ

2

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

Если вам нужно использовать фоновый поток для отмены операции, тем не менее не будет смысла продолжать использовать асинхронные методы Begin/End. Если вы собираетесь отделить фоновый поток, просто выполните синхронную операцию чтения из фонового потока, а затем вы можете использовать ReceiveTimeout.

+1

Joel указывает нам в правильном направлении вокруг не используя фоновый поток, чтобы отменить, но упаковывают это не очевидно, что многие из людей, это есть 1 поток (возможно, из рабочего опроса) периодически очищает мертвые соединения. Это необходимо для освобождения дескрипторов сокетов/и т. Д., И особенно важно, если вы когда-либо получаете что-либо вроде атаки отказа в обслуживании - намеренно или нет. – eselk

+0

Другой вариант - ['System.Threading.Timer'] (http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx), который не принимает поток. – hangar

0

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

private ManualResetEvent receiveDone = new ManualResetEvent(false); 

receiveDone.Reset(); 
socket.BeginReceive(...); 
if(!receiveDone.WaitOne(new TimeSpan(0, 0, 0, 30))) //wait for 30 sec. 
    throw new SocketException((int)SocketError.TimedOut); 

Внутри BeginReceive обратного вызова используйте

private void ReceiveCallBack(IAsyncResult ar) 
{ 
    /** Use ar to check if receive is correct and complete */ 
    receiveDone.Set(); 
} 
+1

Downvoting, потому что это именно то, что я (наивно) сделал, и он не работает. Каждый вызов 'BeginReceive', который не соответствует вызову' EndReceive', будет утечка ресурсов ядра. График активности процесса в чем-то вроде Perfmon и слежения за невыгружаемым пулом * медленно * утечка. Если это длится достаточно долго (в моем случае, часы), вы в конечном итоге получите исключение с кодом ошибки 'WSAENOBUFS'; «Операция сокета не может быть выполнена, потому что в системе недостаточно места для буфера или потому, что очередь заполнена». – Anthony

+0

Я еще не проверил его, но не можем ли мы исправить вашу проблему, вызвав 'socket.EndReceive (..)' перед тем, как выбрасывать исключение? IAsyncResult, который вам нужен для вызова «EndReceive», возвращается «BeginReceive». Повысьте свой комментарий. Спасибо за обновление. –

+0

Да, возможно, это сработает. – Anthony

Смежные вопросы