使用HttpListener处理范围请求

我编写了一个自定义HTTP服务器,它可以正常工作,直到浏览器发出一个字节范围的请求。 在尝试加载video时(看似某些大小的文件因为每次都不会发生),浏览器会使用此标头请求video文件:

method: GET /media/mp4/32.mp4 Connection - keep-alive Accept - */* Accept-Encoding - identity;q=1/*;q=0 Accept-Language - en-us/en;q=0.8 Host - localhost:20809 Referer - ... Range - bytes=0- User-Agent - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Safari/537.36 

所以服务器发送请求的文件……然后,之后立即发出请求:

 method: GET /media/mp4/32.mp4 Connection - keep-alive Accept - */* Accept-Encoding - identity;q=1/*;q=0 Accept-Language - en-us/en;q=0.8 Host - localhost:20809 Referer - ... Range - bytes=40-3689973 User-Agent - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Safari/537.36 

所以我将请求的字节写入输出流,但它总是在第二个请求上出错并出错。 这几乎就像服务器在浏览器发送另一个请求时仍在尝试发送文件。

由于线程退出或应用程序请求,I / O操作已中止

以下是处理范围请求的代码:

 public void StartServer() { _server = new HttpListener(); _server.Prefixes.Add("http://localhost:" + _port.ToString() + "/"); LogWebserver("Listening..."); _server.Start(); th = new Thread(new ThreadStart(startlistener)); th.Start(); } private void startlistener() { while (true) { ////blocks until a client has connected to the server ProcessRequest(); } } private void ProcessRequest() { var result = _server.BeginGetContext(ListenerCallback, _server); result.AsyncWaitHandle.WaitOne(); } private void ListenerCallback(IAsyncResult result) { var context = _server.EndGetContext(result); HandleContext(context); } private void HandleContext(HttpListenerContext context) { HttpListenerRequest req = context.Request; ...stuff... using (HttpListenerResponse resp = context.Response) { .... stuff.... byte[] buffer = File.ReadAllBytes(localFile); if (mime.ToString().Contains("video") || mime.ToString().Contains("audio")) { resp.StatusCode = 206; resp.StatusDescription = "Partial Content"; int startByte = -1; int endByte = -1; int byteRange = -1; if (req.Headers.GetValues("Range") != null) { string rangeHeader = req.Headers.GetValues("Range")[0].Replace("bytes=", ""); string[] range = rangeHeader.Split('-'); startByte = int.Parse(range[0]); if (range[1].Trim().Length > 0) int.TryParse(range[1], out endByte); if (endByte == -1) endByte = buffer.Length; } else { startByte = 0; endByte = buffer.Length; } byteRange = endByte - startByte; resp.ContentLength64 = byteRange; resp.Headers.Add("Accept-Ranges", "bytes"); resp.Headers.Add("Content-Range", string.Format("bytes {0}-{1}/{2}", startByte, byteRange - 1, byteRange)); resp.Headers.Add("X-Content-Duration", "0.0"); resp.Headers.Add("Content-Duration", "0.0"); resp.OutputStream.Write(buffer, startByte, byteRange);/* this is where it gives the IO error */ resp.OutputStream.Close(); resp.Close(); } else { resp.ContentLength64 = buffer.Length; resp.OutputStream.Write(buffer, 0, buffer.Length); resp.OutputStream.Close(); resp.Close(); } } } 

我试过忽略带有范围的请求,但是没有抛出错误,浏览器抛出错误,因为没有下载video。

如何处理这些范围请求并避免IO错误?

我解决了这个问题,但它涉及废弃HttpListener并使用TcpListener代替。

我在这里使用代码作为服务器的基础: http : //www.codeproject.com/Articles/137979/Simple-HTTP-Server-in-C

然后我用这个修改了handleGETRequest函数:

 if (mime.ToString().Contains("video") || mime.ToString().Contains("audio")) { using (FileStream fs = new FileStream(localFile, FileMode.Open)) { int startByte = -1; int endByte = -1; if (p.httpHeaders.Contains("Range")) { string rangeHeader = p.httpHeaders["Range"].ToString().Replace("bytes=", ""); string[] range = rangeHeader.Split('-'); startByte = int.Parse(range[0]); if (range[1].Trim().Length > 0) int.TryParse(range[1], out endByte); if (endByte == -1) endByte = (int)fs.Length; } else { startByte = 0; endByte = (int)fs.Length; } byte[] buffer = new byte[endByte - startByte]; fs.Position = startByte; int read = fs.Read(buffer,0, endByte-startByte); fs.Flush(); fs.Close(); p.outputStream.AutoFlush = true; p.outputStream.WriteLine("HTTP/1.0 206 Partial Content"); p.outputStream.WriteLine("Content-Type: " + mime); p.outputStream.WriteLine("Accept-Ranges: bytes"); int totalCount = startByte + buffer.Length; p.outputStream.WriteLine(string.Format("Content-Range: bytes {0}-{1}/{2}", startByte, totalCount - 1, totalCount)); p.outputStream.WriteLine("Content-Length: " + buffer.Length.ToString()); p.outputStream.WriteLine("Connection: keep-alive"); p.outputStream.WriteLine(""); p.outputStream.AutoFlush = false; p.outputStream.BaseStream.Write(buffer, 0, buffer.Length); p.outputStream.BaseStream.Flush(); } } else { byte[] buffer = File.ReadAllBytes(localFile); p.outputStream.AutoFlush = true; p.outputStream.WriteLine("HTTP/1.0 200 OK"); p.outputStream.WriteLine("Content-Type: " + mime); p.outputStream.WriteLine("Connection: close"); p.outputStream.WriteLine("Content-Length: " + buffer.Length.ToString()); p.outputStream.WriteLine(""); p.outputStream.AutoFlush = false; p.outputStream.BaseStream.Write(buffer, 0, buffer.Length); p.outputStream.BaseStream.Flush(); } 

使用TcpListener可以避免任何导致响应流上的I / O错误的愚蠢问题。