在Stream Read上实现超时属性

时间:2012-03-21 12:54:58

标签: c# windows-phone-7 stream

我正在从HttpWebResponse.GetResponseStream()获取一个我正在读取数据的流。 现在我想实现一个Timeout属性。最简单的方法是stream.ReadTimeout = timeout,但这会引发InvalidOperationException -> Timeouts are not supported on this stream

鉴于此,我正在尝试自己实现超时属性,但却陷入了处置。这是我到目前为止所得到的:

public class MyStream : Stream {
    private readonly Stream _src;
    public override int ReadTimeout { get; set; }

    public MyStream (Stream src, int timeout) {
       ReadTimeout = timeout;
       _src = src;
    }

    public override int Read(byte[] buffer, int offset, int count) {
        var timer = new AutoResetEvent(false);
        int read = 0;

        ThreadPool.QueueUserWorkItem(
            _ => {
                read = _src.Read(buffer, offset, count);
                timer.Set();
            });
        bool completed = timer.WaitOne(ReadTimeout);
        if (completed) {
            return read;
        }
        throw new TimeoutException(string.Format("waited {0} miliseconds", ReadTimeout));
    }

此代码的问题是在抛出正在某处正确处理的TimeoutException之后。它会在_src.Read(buffer, offset, count)上抛出一个异常,说明_src流被处理掉了。

有没有办法取消ThreadPool方法,还是应该使用更好的方法和哪一种?

由于

修改

正如@JotaBe所问,我是从HttpWebResponse获取流的代码:

_httpRequest = WebRequest.CreateHttp(url);

_httpRequest.AllowReadStreamBuffering = false;
_httpRequest.BeginGetResponse(
    result =>
        {
            try {
                _httpResponse = (HttpWebResponse)_httpRequest.EndGetResponse(result);
                stream = _httpResponse.GetResponseStream();
            }
            catch (WebException) {
                downloadCompleted.Set();
                Abort();
            }
            finally {
                downloadCompleted.Set();
            }
        },
        null);

    bool completed = downloadCompleted.WaitOne(15 * 1000);
    if (completed) {
        return new MyStream(stream, 10000);
    }

2 个答案:

答案 0 :(得分:2)

如果你没有收到并通过网络服务器回复,你试图超时。你试图在错误的地方做这件事。

要获得回复,您通常会发出请求:

HttpWebResponse response = (HttpWebResponse)request.GetResponse ();

这是可以超时的操作。您必须使用Begin / End异步模式以不同的方式调用它。

有一个完整的例子 MSDN doc for HttpWebRequest.BeginGetResponse Method

此示例使用回调函数。但是,有许多不同的方法可以使用Begin / End。例如,您可以使用IAsyncResult中提供的WaitHandle,如下所示:

IAsyncResult ar = req.BeginGetResponse(yourCallback, null);
bool completed = ar.AsyncWaitHandle.WaitOne(15000 /*your timeout in miliseconds*/);

这将等待15秒。如果响应在此之前到达,则完成将为真。如果没有,完成将是假的。然后,您可以使用HttpWebRequest.Abort()方法中止请求。

Begin / End模式负责管理必要的线程。

答案 1 :(得分:-1)

我最终使用了Nomad101建议并用try / catch包围了读取。