我已经编写了一个自定义HTTP服务器,它可以正常运行,直到浏览器发出一个字节范围的请求。在尝试加载视频时(似乎是因为每次都不会发生超过一定大小的文件),浏览器会使用此标题请求视频文件:
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();
}
}
}
我试图忽略带有范围的请求,但是没有抛出任何错误,浏览器会因为视频没有下载而抛出错误。
如何处理这些范围请求并避免IO错误?
答案 0 :(得分:2)
我解决了这个问题,但它涉及报废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错误的任何愚蠢问题。
答案 1 :(得分:0)
我知道它来晚了,但是我认为您的解决方案存在一些错误。您的代码不适用于Safari。
Safari始终请求2个字节来检查,但不幸的是没有信息。 其他浏览器的容忍度更高。