如何使用HttpListener同时处理多个连接?

在我构建的应用程序中,需要能够同时为多个客户端提供服务的Web服务器。
为此,我使用HttpListener对象。 使用Async方法\事件BeginGetContextEndGetContext
在委托方法中,有一个监听器再次开始监听的呼叫,它主要起作用。

提供的代码是我在这里和那里找到的代码混合,以及模拟数据处理瓶颈的延迟。

问题是,它只在最后一个连接服务之后开始管理下一个连接..对我没用。

 public class HtServer { public void startServer(){ HttpListener HL = new HttpListener(); HL.Prefixes.Add("http://127.0.0.1:800/"); HL.Start(); IAsyncResult HLC = HL.BeginGetContext(new AsyncCallback(clientConnection),HL); } public void clientConnection(IAsyncResult res){ HttpListener listener = (HttpListener)res.AsyncState; HttpListenerContext context = listener.EndGetContext(res); HttpListenerRequest request = context.Request; // Obtain a response object. HttpListenerResponse response = context.Response; // Construct a response. // add a delay to simulate data process String before_wait = String.Format("{0}", DateTime.Now); Thread.Sleep(4000); String after_wait = String.Format("{0}", DateTime.Now); string responseString = " BW: " + before_wait + "
AW:" + after_wait + ""; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); // Get a response stream and write the response to it. response.ContentLength64 = buffer.Length; System.IO.Stream output = response.OutputStream; // You must close the output stream. output.Write(buffer, 0, buffer.Length); output.Close(); listener.BeginGetContext(new AsyncCallback(clientConnection), listener); } }

编辑

  private static void OnContext(IAsyncResult ar) { var ctx = _listener.EndGetContext(ar); _listener.BeginGetContext(OnContext, null); Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " Handling request"); var buf = Encoding.ASCII.GetBytes("Hello world"); ctx.Response.ContentType = "text/plain"; // prevent thread from exiting. Thread.Sleep(3000); // moved these lines here.. to simulate process delay ctx.Response.OutputStream.Write(buf, 0, buf.Length); ctx.Response.OutputStream.Close(); Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " completed"); } 

输出是 在此处输入图像描述

好。 那是因为你在处理了第一个上下文之后开始获取下一个上下文。 不要那样做。 直接获取下一个上下文:

 public void clientConnection(IAsyncResult res){ HttpListener listener = (HttpListener)res.AsyncState; HttpListenerContext context = listener.EndGetContext(res); //tell listener to get the next context directly. listener.BeginGetContext(clientConnection, listener); HttpListenerRequest request = context.Request; // Obtain a response object. HttpListenerResponse response = context.Response; // Construct a response. // add a delay to simulate data process String before_wait = String.Format("{0}", DateTime.Now); Thread.Sleep(4000); String after_wait = String.Format("{0}", DateTime.Now); string responseString = " BW: " + before_wait + "
AW:" + after_wait + ""; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); // Get a response stream and write the response to it. response.ContentLength64 = buffer.Length; System.IO.Stream output = response.OutputStream; // You must close the output stream. output.Write(buffer, 0, buffer.Length); output.Close(); }

这是我的示例代码,certificate它可以工作(根据OP的请求更新):

 class Program { private static HttpListener _listener; static void Main(string[] args) { _listener = new HttpListener(); _listener.Prefixes.Add("http://localhost/asynctest/"); _listener.Start(); _listener.BeginGetContext(OnContext, null); Console.ReadLine(); } private static void OnContext(IAsyncResult ar) { var ctx = _listener.EndGetContext(ar); _listener.BeginGetContext(OnContext, null); Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " Handling request"); var buf = Encoding.ASCII.GetBytes("Hello world"); ctx.Response.ContentType = "text/plain"; // simulate work Thread.Sleep(10000); ctx.Response.OutputStream.Write(buf, 0, buf.Length); ctx.Response.OutputStream.Close(); Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " completed"); } } 

产生:

在此处输入图像描述

两个请求都开始直接处理。

为什么上面的代码有效

HTTP有一个叫做流水线的东西。 这意味着通过同一连接接收的所有请求必须以相同的顺序获得响应。 但是,内置的HttpListener似乎不支持流水线操作,而是在处理第二个请求之前完成第一个请求的响应。 因此,确保通过新连接发送每个请求非常重要。

最简单的方法是在尝试代码时使用不同的浏览器。 我做到了,当你看到我的请求同时处理时。

试试这个……

这将使用异步编码来确保没有阻塞。 阻塞意味着当线程hibernate时,这通常是程序倾向于“冻结”的方式。 通过使用此代码,您运行非阻塞,这意味着它几乎不可能“冻结”应用程序。

 public async Task handleClientConnection(HttpListener listener){ HttpListenerContext context = await listener.GetContextAsync(); var ret = handleClientConnection(listener); HttpListenerRequest request = context.Request; // Obtain a response object. HttpListenerResponse response = context.Response; // Construct a response. // add a delay to simulate data process String before_wait = String.Format("{0}", DateTime.Now); await Task.Wait(4000); String after_wait = String.Format("{0}", DateTime.Now); string responseString = " BW: " + before_wait + "
AW:" + after_wait + ""; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); // Get a response stream and write the response to it. response.ContentLength64 = buffer.Length; using(System.IO.Stream output = response.OutputStream) output.Write(buffer, 0, buffer.Length); await ret; } public void startServer(){ HttpListener HL = new HttpListener(); HL.Prefixes.Add("http://127.0.0.1:800/"); HL.Start(); await handleClientConnection(HL); }