为什么在断点上继续过早会导致我的服务器发送无效响应

时间:2014-08-08 22:38:24

标签: c# wcf iis-8

我正在通过IIS返回一个视频文件,用于WCF服务中的范围请求。

代码的结尾如下所示:

WriteResponseHeaders(stuff);
while (remainingBytes > 0)
{
    if (response.IsClientConnected) // response is a System.Web.HttpResponse
    {
        int chunkSize = stream.Read(buffer, 0, 10240 < remainingBytes ? 10240 : remainingBytes);
        response.OutputStream.Write(buffer, 0, chunkSize);
        remainingBytes -= chunkSize;
        response.Flush();
    }
    else
    {
        return;
    }
}

在Firefox,Internet Explorer和Opera中,它可以正常工作。在Chrome中,视频会在结束前停止播放一段时间。 Fiddler显示504错误:

  

[Fiddler] ReadResponse()失败:服务器未返回此请求的响应。服务器返回16556397字节。

如果我在循环之后粘贴断点,并让程序坐在那里直到视频已经超过其停止点,Chrome将播放完整的视频而没有任何问题,Fiddler将显示所有正确标题的响应等等。在该断点和调用结束之间执行的唯一代码是刷新日志流。

作为测试,我坚持:

while (response.IsClientConnected)
{
    System.Threading.Thread.Sleep(1000);
}

循环播放后,所有浏览器播放都很好。我对Fiddler的反应也很好。当然,这有太多问题需要成为一个合适的解决方案,但它似乎告诉我,这是一个更多的时间问题而不是行为问题。

为什么允许代码过早地超过这一点会导致问题,如何阻止它这样做呢?

1 个答案:

答案 0 :(得分:2)

尝试返回Stream而不是写入response.OutputStream。

[ServiceContract]
public interface IStreamingService
{
    [OperationContract]
    [WebGet(BodyStyle=WebMessageBodyStyle.Bare, UriTemplate = "/video?id={id}")]
    Stream GetVideo(string id);
}

public class StreamingService : IStreamingService
{
    public System.IO.Stream GetVideo(string id)
    {
        Stream stream = File.OpenRead("c:\\Temp\\Video.mp4");
        //WriteResponseHeaders(stuff);
        return stream;
    }
}

更新

如果你想支持搜索,你可以将块复制到byte []并返回一个MemoryStream,或者你可以将你的流包装在只返回整个文件的一部分的代理中。

public class PartialStream : Stream
{
    private Stream underlying;

    private long offset;

    private long length;

    public PartialStream(Stream underlying, long offset, long length)
    {
        this.underlying = underlying;
        this.offset = offset;
        if (offset + length > underlying.Length) {
            this.length = underlying.Length - offset;
        } else {
            this.length = length;
        }

        this.underlying.Seek(offset, SeekOrigin.Begin);
    }

    public override bool CanRead { get { return true; } }

    public override bool CanSeek { get { return false; } }

    public override bool CanWrite { get { return false; } }

    public override void Flush()
    {
        throw new NotSupportedException();
    }

    public override long Length
    {
        get { return this.length; }
    }

    public override long Position
    {
        get
        {
            return this.underlying.Position - offset;
        }
        set
        {
            this.underlying.Position = offset + Math.Min(value,this.length) ;
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (this.Position + offset >= this.length)
            return 0;

        if (this.Position + offset + count > this.length) {
            count = (int)(this.length - this.Position - offset);
        }


        return underlying.Read(buffer, offset, count);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        this.underlying.Dispose();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

}

你必须尊重Range请求标题。

public System.IO.Stream GetVideo(string id)
{
    RangeHeaderValue rangeHeader;

    bool hasRangeHeader = RangeHeaderValue.TryParse(
                WebOperationContext.Current.IncomingRequest.Headers["Range"], 
                out rangeHeader);

    var response = WebOperationContext.Current.OutgoingResponse;
    Stream stream = File.OpenRead("c:\\Temp\\Video.mp4");

    var offset = hasRangeHeader ? rangeHeader.Ranges.First().From.Value : 0;

    response.Headers.Add("Accept-Ranges", "bytes");
    response.ContentType = "video/mp4";

    if (hasRangeHeader) {
        response.StatusCode = System.Net.HttpStatusCode.PartialContent;
        var totalLength = stream.Length;
        stream = new PartialStream(stream, offset, 10 * 1024 * 1024);


        var header = new ContentRangeHeaderValue(offset, offset + stream.Length - 1,totalLength);
        response.Headers.Add("Content-Range", header.ToString());
    }
    response.ContentLength = stream.Length;

    return stream;
}