Мне просто нужно прочитать до N байтов от SslStream, но если байт не был получен до истечения таймаута, отмените его, оставив поток в допустимом состоянии, чтобы повторить попытку позже. (*)
Это можно легко сделать для потоков без SSL, т.е. NetworkStream, просто используя его свойство ReadTimeout, которое сделает поток генерирует исключение в таймаут. К сожалению, этот подход не работает на SslStream в официальных документах:
SslStream предполагает, что тайм-аут вместе с любым другим исключением IO, когда он выкинут из внутреннего потока, будет считаться фатальным его вызывающим. Повторное использование экземпляра SslStream после таймаута возвращает мусор. Приложение должно закрыть SslStream и выбросить исключение в этих случаях.
[Обновлено 1] Я пробовал использовать другой подход:
task = stream->ReadAsync(buffer, 0, buffer->Length);
if (task->Wait(timeout_ms)) {
count = task->Result;
...
}
Но это не работает, если Wait() возвращен false: при вызове ReadAsync() снова он выдает исключение:
Исключение выбрано: System.NotSupportedException в System.dll Tests.exe Предупреждение: 0: Ошибка чтения из сокета: System.NotSupportedException: метод BeginRead не может быть вызван при ожидании другой операции чтения.
[Обновить 2]. Я попытался использовать еще один подход для реализации тайм-аутов, вызвав Poll(timeout, ...READ) в базовом сокете TcpClient: если он возвращает true, затем вызовите Read() в SslStream, или если он возвращает false, тогда у нас есть тайм-аут. Это также не работает: поскольку SslStream предположительно использует собственные внутренние промежуточные буферы, Poll() может возвращать false, даже если в SslStream есть данные, которые нужно прочитать.
[Обновить 3]. Еще одна возможность - создать пользовательский подкласс Stream, который будет находиться между NetworkStream и SslStream и захватить исключение тайм-аута и вернуть 0 байт вместо SslStream. Я не уверен, как это сделать, и, что более важно, я понятия не имею, что возвращение 0 байтов, прочитанных в SslStream, все равно не испортило бы его.
(*) Причина, по которой я пытаюсь это сделать, заключается в том, что чтение синхронно с тайм-аутом из небезопасного или защищенного сокета - это шаблон, который я уже использую в iOS, OS X, Linux и Android для некоторого креста -платформенный код. Он работает для небезопасных сокетов в .NET, поэтому единственным оставшимся случаем является SslStream.