代码之家  ›  专栏  ›  技术社区  ›  yossi

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

  •  10
  • yossi  · 技术社区  · 10 年前

    在我构建的应用程序中,需要能够同时为多个客户端提供服务的Web服务器。
    为此,我使用 HttpListener 对象用它 Async 方法\事件 BeginGetContext EndGetContext .
    在委托方法中,有一个调用要求侦听器重新开始侦听,并且它有效。。主要地

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

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

    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 = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
            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");
        }
    

    输出为 enter image description here

    2 回复  |  直到 10 年前
        1
  •  22
  •   jgauffin    10 年前

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

    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 = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
        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();
    }
    

    以下是我的示例代码,证明它有效(根据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");
        }
    }
    

    生成:

    enter image description here

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

    为什么上述代码有效

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

    最简单的方法是在尝试代码时使用不同的浏览器。我做到了,正如你所见,我的两个请求都是同时处理的。

        2
  •  7
  •   Aron    10 年前

    请尝试此操作。。

    这将使用异步编码来确保没有阻塞。阻塞意味着当线程休眠时,程序通常会“冻结”。通过使用此代码,您可以运行非阻塞,这意味着几乎不可能“冻结”应用程序。

    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 = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
        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);
    }