How to process multiple connections simultaneously with HttpListener?

后端 未结 2 1429
猫巷女王i
猫巷女王i 2020-12-15 10:58

In the application that I build, there is a need for webserver that can serve, simultaneously, multiple clients.
For that I use the HttpListener object. wit

相关标签:
2条回答
  • 2020-12-15 11:19

    Try this instead..

    This will use asynchronous coding to ensure that there is no blocking. Blocking means when a thread, sleeps, which is typically how programs tend to "freeze". By using this code, you run non-blocking, which means that its almost impossible to "freeze" the application.

    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);
    }
    
    0 讨论(0)
  • 2020-12-15 11:20

    Well. That's because you start to fetch the next context after you have processed the first. Don't do that. Get the next context directly:

    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();
    }
    

    Here is my sample code that proves that it work (updated per request of the 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");
        }
    }
    

    Generates:

    enter image description here

    Both requests starts to get processed directly.

    Why the above code works

    HTTP have something called pipelining. It means that all requests that are received over the same connection must get their responses in the same order. However, the built in HttpListener doesn't seem to support pipelining, instead it's completes the response for the first request before taking care of the second. It's therefore important that you make sure that every request is sent over a new connection.

    The easiest way to do that is to use different browsers when trying out the code. I did that, and as you see both my requests are handled at the same time.

    0 讨论(0)
提交回复
热议问题