C# 具有等待异步和任务的多线程 HttpListener
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11167183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Multi-threaded HttpListener with await async and Tasks
提问by halivingston
Would this be a good example of a scalable HttpListener that is multi-threaded?
这是多线程的可扩展 HttpListener 的一个很好的例子吗?
Is this how for example a real IIS would do it?
例如,这是一个真正的 IIS 会怎么做吗?
public class Program
{
private static readonly HttpListener Listener = new HttpListener();
public static void Main()
{
Listener.Prefixes.Add("http://+:80/");
Listener.Start();
Listen();
Console.WriteLine("Listening...");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static async void Listen()
{
while (true)
{
var context = await Listener.GetContextAsync();
Console.WriteLine("Client connected");
Task.Factory.StartNew(() => ProcessRequest(context));
}
Listener.Close();
}
private static void ProcessRequest(HttpListenerContext context)
{
System.Threading.Thread.Sleep(10*1000);
Console.WriteLine("Response");
}
}
I'm specifically looking for a scalable solution that DOES NOT rely on IIS. Instead only on http.sys (which is the httplistener class) -- The reason for not relying on iIS is because the govt. area I work in requires extremely reduced surface area of attack.
我特别在寻找不依赖 IIS 的可扩展解决方案。而是仅在 http.sys(这是 httplistener 类)上--不依赖 IIS 的原因是因为 govt.sys。我工作的领域需要极小的攻击表面积。
采纳答案by James Dunne
I've done something similar at https://github.com/JamesDunne/Aardwolfand have done some extensive testing on this.
我在https://github.com/JamesDunne/Aardwolf 上做了类似的事情,并对此做了一些广泛的测试。
See the code at https://github.com/JamesDunne/aardwolf/blob/master/Aardwolf/HttpAsyncHost.cs#L107for the core event loop's implementation.
有关核心事件循环的实现,请参阅https://github.com/JamesDunne/aardwolf/blob/master/Aardwolf/HttpAsyncHost.cs#L107 上的代码。
I find that using a Semaphoreto control how many concurrent GetContextAsyncrequests are active is the best approach. Essentially, the main loop continues running until the semaphore blocks the thread due to the count being reached. Then there will be N concurrent "connection accepts" active. Each time a connection is accepted, the semaphore is released and a new request can take its place.
我发现使用 aSemaphore来控制有多少并发GetContextAsync请求处于活动状态是最好的方法。本质上,主循环继续运行,直到信号量由于达到计数而阻塞线程。然后将有 N 个并发“连接接受”活动。每次接受连接时,信号量都会被释放,新的请求可以取而代之。
The semaphore's initial and max count values require some fine tuning, depending on the load you expect to receive. It's a delicate balancing act between the number of concurrent connections you expect vs. the average response times that your clients desire. Higher values mean more connections can be maintained yet at a much slower average response time; fewer connections will be rejected. Lower values mean less connections can be maintained yet at a much faster average response time; more connections will be rejected.
信号量的初始和最大计数值需要进行一些微调,具体取决于您期望接收的负载。这是您期望的并发连接数与客户期望的平均响应时间之间的微妙平衡。较高的值意味着可以维持更多的连接,但平均响应时间要慢得多;更少的连接将被拒绝。较低的值意味着可以维持更少的连接,但平均响应时间要快得多;更多的连接将被拒绝。
I've found, experimentally (on my hardware), that values around 128allow the server to handle large amounts of concurrent connections (up to 1,024) at acceptable response times. Test using your own hardware and tune your parameters accordingly.
我已经通过实验(在我的硬件上)发现,周围128的值允许服务器以可接受的响应时间处理大量并发连接(最多 1,024 个)。使用您自己的硬件进行测试并相应地调整您的参数。
I've also found that a single instance of WCAT does not like to handle more than 1,024 connections itself. So if you're serious about load-testing, use multiple client machines with WCAT against your server and be sure to test over a fast network e.g. 10 GbE and that your OS's limits are not slowing you down. Be sure to test on Windows Server SKUs because the Desktop SKUs are limited by default.
我还发现 WCAT 的单个实例本身不喜欢处理超过 1,024 个连接。因此,如果您认真对待负载测试,请针对您的服务器使用多台带有 WCAT 的客户端计算机,并确保通过快速网络(例如 10 GbE)进行测试,并且您的操作系统限制不会减慢您的速度。请务必在 Windows Server SKU 上进行测试,因为桌面 SKU 在默认情况下受到限制。
Summary:How you write your connection accept loop is critical to the scalability of your server.
简介:如何编写连接接受循环对服务器的可扩展性至关重要。
回答by Cellfish
Technically you're right. To make it scalable you probably want to have multiple GetContextAsync running at the same time (performance testing needed to know exactly how many, but "a few for each core" is probably the right answer).
从技术上讲,你是对的。为了使其可扩展,您可能希望同时运行多个 GetContextAsync(性能测试需要确切知道有多少,但“每个核心有几个”可能是正确的答案)。
Then naturally, as pointed out by comments; not using IIS means you need to be pretty serious about security for a lot of things IIS gives you "for free".
然后很自然地,正如评论所指出的那样;不使用 IIS 意味着您需要非常重视 IIS 为您“免费”提供的很多东西的安全性。
回答by joelc
I know I'm tremendously late to the party on this, but I published a library (source here https://github.com/jchristn/WatsonWebserver) on NuGet which encapsulates an async webserver.
我知道我在这个聚会上迟到了,但我在 NuGet 上发布了一个库(来源https://github.com/jchristn/WatsonWebserver),它封装了一个异步网络服务器。
回答by SteveC
Here's a pattern to use a cancellation token to shut the listener down cleanly:
这是使用取消令牌彻底关闭侦听器的模式:
try
{
while (active)
{
Task<HttpListenerContext> listenTask = httpListener.GetContextAsync();
listenTask.Wait(myCancelToken.Token);
HttpListenerContext listenerContext = listenTask.Result;
// Do something with listenerContext in a seperate thread or task..
}
}
catch (System.OperationCanceledException)
{
// This is expected!
}
httpListener.Close();
Note that this should be executed in its own thread or task to prevent blocking of other code.
请注意,这应该在其自己的线程或任务中执行,以防止阻塞其他代码。

