我这里有个问题。在下面的代码中,异步/等待模式与HttpListener一起使用。当通过HTTP“延迟”查询字符串参数发送请求时,它的值将导致服务器将上述请求处理延迟给定时间。我需要服务器处理挂起的请求,即使服务器停止接收新的请求。
static void Main(string[] args)
{
HttpListener httpListener = new HttpListener();
CountdownEvent sessions = new CountdownEvent(1);
bool stopRequested = false;
httpListener.Prefixes.Add("http://+:9000/GetData/");
httpListener.Start();
Task listenerTask = Task.Run(async () =>
{
while (true)
{
try
{
var context = await httpListener.GetContextAsync();
sessions.AddCount();
Task childTask = Task.Run(async () =>
{
try
{
Console.WriteLine($"Request accepted: {context.Request.RawUrl}");
int delay = int.Parse(context.Request.QueryString["delay"]);
await Task.Delay(delay);
using (StreamWriter sw = new StreamWriter(context.Response.OutputStream, Encoding.Default, 4096, true))
{
await sw.WriteAsync("<html><body><h1>Hello world</h1></body></html>");
}
context.Response.Close();
}
finally
{
sessions.Signal();
}
});
}
catch (HttpListenerException ex)
{
if (stopRequested && ex.ErrorCode == 995)
{
break;
}
throw;
}
}
});
Console.WriteLine("Server is running. ENTER to stop...");
Console.ReadLine();
sessions.Signal();
stopRequested = true;
httpListener.Stop();
Console.WriteLine("Stopped accepting requests. Waiting for the pendings...");
listenerTask.Wait();
sessions.Wait();
Console.WriteLine("Finished");
Console.ReadLine();
httpListener.Close();
}这里的确切问题是,当服务器停止时,将调用HttpListener.Stop,但是所有挂起的请求都会立即中止,即代码无法将响应发回。
在非异步/等待模式(即基于简单线程的实现)中,我可以选择中止线程(我认为这很糟糕),这将允许我处理挂起的请求,因为这只是HttpListener.GetContext调用。
请您指出,我做错了什么,如何防止HttpListener在异步/等待模式中中止挂起的请求?
发布于 2016-04-20 08:04:47
当HttpListener关闭请求队列句柄时,正在进行的请求似乎被中止。据我所知,没有办法避免让HttpListener这样做-显然,这是一个兼容性的问题。无论如何,它的GetContext-ending系统就是这样工作的--当句柄关闭时,用于实际获取请求上下文的本机方法GetContext调用将立即返回一个错误。
Thread.Abort没有帮助--实际上,我还没有看到在“应用程序域卸载”场景之外正确使用Thread.Abort的地方。Thread.Abort只能中止托管代码。由于您的代码当前正在运行本机,所以只有当它返回到托管代码时才会中止--这几乎完全等同于执行以下操作:
var context = await httpListener.GetContextAsync();
if (stopRequested) return;..。而且,由于没有更好的HttpListener取消API,如果您想坚持使用HttpListener,这确实是您唯一的选择。
关机将如下所示:
stopRequested = true;
sessions.Wait();
httpListener.Dispose();
listenerTask.Wait();我还建议使用CancellationToken而不是bool标志-它为您处理所有同步问题。如果出于某种原因这是不可取的,请确保您以契约的方式同步对标志的访问,允许编译器省略检查,因为标记不可能在单线程代码中更改。
如果您愿意,您可以在设置listenerTask后立即发送一个虚拟HTTP请求,从而使stopRequested更快地完成--这将导致GetContext立即与新请求一起返回,并且您可以返回。这是一种在处理不支持“很好”取消的API时常用的方法,例如UdpClient.Receive。
https://stackoverflow.com/questions/36712136
复制相似问题