单声道中的HttpListener可以';t在同一时间内处理2个以上的请求

本文关键字:同一时间 处理 请求 2个 声道 可以 HttpListener 单声道 | 更新日期: 2023-09-27 18:25:40

我在mono中使用HttpListener处理多线程http请求时遇到了问题。看起来httpListener.BeginGetContext(_handleRequestCallback,null)不能"同时"处理2个以上的请求。我写了一个小测试:

using System;
using System.Net;
using NUnit.Framework;
using System.IO;
using log4net;
using System.Threading;
namespace Other
{
    public class ListenerTest
    {
        private ILog _log;
        private HttpListener _httpListener;
        private const int PORT = 8080;
        private const int REQUESTS = 20;
        private AsyncCallback _handleRequestCallback;
        [TestFixtureSetUp]
        public void SetUp()
        {
            ServicePointManager.DefaultConnectionLimit = REQUESTS;
            ThreadPool.SetMinThreads(REQUESTS, REQUESTS);
            ThreadPool.SetMaxThreads(REQUESTS, REQUESTS);
            _httpListener = new HttpListener();
            _httpListener.Prefixes.Add(String.Format("http://*:{0}/", PORT));
            _httpListener.Start();
            _handleRequestCallback = new AsyncCallback(processAsync);
            _log = LogManager.GetLogger(typeof(ListenerTest));
        }
        [Test]
        public void SyncTest()
        {
            Thread serverThread = new Thread(() => processSyncThread());
            serverThread.Start();
            for(int i = 0; i < REQUESTS; ++i)
            {
                makeRequest(i.ToString(), HttpStatusCode.OK, 1000);
            }
        }
        [Test]
        public void AsyncTest()
        {
            int serverThreads = 5;
            Thread serverThread = new Thread(() => processAsyncThread(serverThreads));
            serverThread.Start();
            for(int i = 0; i < REQUESTS; ++i)
            {
                int position = i;
                Thread thread = new Thread(() => makeRequest(position.ToString(), HttpStatusCode.OK, 5000));
                thread.Start();
            }
            Thread.Sleep(5000);
        }
        private void processAsyncThread(int threads)
        {
            for(int i = 0; i < threads; ++i)
                _httpListener.BeginGetContext(_handleRequestCallback, null);
        }
        private void processAsync(IAsyncResult asyncResult)
        {
            Thread thread = new Thread(() => processSync(_httpListener.EndGetContext(asyncResult)));
            thread.Start();
//            processSync(_httpListener.EndGetContext(asyncResult));
            _httpListener.BeginGetContext(_handleRequestCallback, null);
        }
        private void processSyncThread()
        {
            while(true)
            {
                var context = _httpListener.GetContext();
                processSync(context);
            }
        }
        private void processSync(HttpListenerContext context)
        {
            string request = context.Request.RawUrl.TrimStart('/');
            _log.InfoFormat("Received request:{0}", request);
            Thread.Sleep(500);
            using(StreamWriter writer = new StreamWriter(context.Response.OutputStream))
                writer.Write(request);
            _log.InfoFormat("Sent response for:{0}", request);
        }
        private string makeRequest(string request, HttpStatusCode expectedCode, int timeout)
        {
            _log.InfoFormat("Sending {0} request.", request);
            WebRequest webRequest = WebRequest.Create(String.Format("http://localhost:{0}/{1}", PORT, request));
            webRequest.Timeout = timeout;
            try
            {
                HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
                if(expectedCode != response.StatusCode)
                    throw new IOException(String.Format(
                        "Returned unexpected status code: {0} for request: {1} when expected: {2}",
                        response.StatusCode, request, expectedCode));
                StreamReader reader = new StreamReader(response.GetResponseStream());
                string responseContent = reader.ReadToEnd();
                response.Close();
                _log.InfoFormat("Received response:{0}", responseContent);
                return responseContent;
            }
            catch(WebException exception)
            {
                if (exception.Response is HttpWebResponse
                    && expectedCode == ((HttpWebResponse)exception.Response).StatusCode)
                    return "";
                else if (exception.Status == WebExceptionStatus.Timeout
                    && expectedCode == HttpStatusCode.RequestTimeout)
                    return "";
                throw;
            }
        }
    }
}

(这需要Nunit和log4net运行,但它们可能很容易被删除)有了SyncTest,一切如愿以偿。在AsyncTest中,我看到httpListener并行调用_handleRequestCallback的次数不会超过2次。第三个请求在处理任何先前请求时开始处理:

01:24:23,788 [(null)-27] INFO  Other.ListenerTest {(null)} - Sending 10 request.
01:24:23,788 [(null)-30] INFO  Other.ListenerTest {(null)} - Sending 13 request.
...
01:24:23,788 [(null)-23] INFO  Other.ListenerTest {(null)} - Sending 8 request.
01:24:23,793 [(null)-36] INFO  Other.ListenerTest {(null)} - Sending 19 request.
01:24:23,885 [(null)-40] INFO  Other.ListenerTest {(null)} - Received request:12
01:24:23,885 [(null)-39] INFO  Other.ListenerTest {(null)} - Received request:5
01:24:24,400 [(null)-39] INFO  Other.ListenerTest {(null)} - Sent response for:5
01:24:24,400 [(null)-40] INFO  Other.ListenerTest {(null)} - Sent response for:12
01:24:24,413 [(null)-20] INFO  Other.ListenerTest {(null)} - Received response:5
01:24:24,414 [(null)-29] INFO  Other.ListenerTest {(null)} - Received response:12
01:24:24,414 [(null)-54] INFO  Other.ListenerTest {(null)} - Received request:0
01:24:24,414 [(null)-53] INFO  Other.ListenerTest {(null)} - Received request:15
01:24:24,915 [(null)-54] INFO  Other.ListenerTest {(null)} - Sent response for:0
01:24:24,915 [(null)-53] INFO  Other.ListenerTest {(null)} - Sent response for:15
01:24:24,917 [(null)-15] INFO  Other.ListenerTest {(null)} - Received response:0
01:24:24,918 [(null)-32] INFO  Other.ListenerTest {(null)} - Received response:15
01:24:24,919 [(null)-65] INFO  Other.ListenerTest {(null)} - Received request:18
01:24:24,919 [(null)-66] INFO  Other.ListenerTest {(null)} - Received request:3
01:24:25,419 [(null)-65] INFO  Other.ListenerTest {(null)} - Sent response for:18
01:24:25,420 [(null)-66] INFO  Other.ListenerTest {(null)} - Sent response for:3
01:24:25,458 [(null)-18] INFO  Other.ListenerTest {(null)} - Received response:3
01:24:25,458 [(null)-77] INFO  Other.ListenerTest {(null)} - Received request:10
01:24:25,558 [(null)-35] INFO  Other.ListenerTest {(null)} - Received response:18
01:24:25,558 [(null)-80] INFO  Other.ListenerTest {(null)} - Received request:6
01:24:25,959 [(null)-77] INFO  Other.ListenerTest {(null)} - Sent response for:10
01:24:25,998 [(null)-27] INFO  Other.ListenerTest {(null)} - Received response:10
01:24:25,998 [(null)-81] INFO  Other.ListenerTest {(null)} - Received request:4
01:24:26,059 [(null)-80] INFO  Other.ListenerTest {(null)} - Sent response for:6
01:24:26,059 [(null)-21] INFO  Other.ListenerTest {(null)} - Received response:6
01:24:26,060 [(null)-82] INFO  Other.ListenerTest {(null)} - Received request:2

我在MS.Net上运行相同的代码(但没有log4net),看看我期望的是什么:

01:38:39:754 Sending 9 request.
01:38:39:738 Sending 5 request.
01:38:39:738 Sending 3 request.
01:38:39:738 Sending 1 request.
01:38:40:660 Received request:18
01:38:40:660 Received request:1
01:38:40:660 Received request:0
01:38:40:660 Received request:2
01:38:40:660 Received request:3
01:38:40:660 Received request:4
01:38:40:676 Received request:5
01:38:40:676 Received request:6
01:38:40:676 Received request:7
01:38:40:676 Received request:8
01:38:40:692 Received request:9
01:38:40:692 Received request:10
01:38:40:692 Received request:11
01:38:40:692 Received request:12
01:38:40:707 Received request:13
01:38:40:707 Received request:14
01:38:40:707 Received request:15
01:38:40:707 Received request:16
01:38:40:723 Received request:17
01:38:40:723 Received request:19
01:38:41:160 Sent response for:4
01:38:41:160 Received response:18
01:38:41:160 Received response:1
01:38:41:160 Sent response for:3
01:38:41:160 Sent response for:2
01:38:41:176 Received response:8
01:38:41:160 Sent response for:1
01:38:41:160 Sent response for:0
01:38:41:160 Sent response for:18
01:38:41:160 Received response:2
01:38:41:160 Received response:3
01:38:41:160 Received response:4
01:38:41:176 Sent response for:8
01:38:41:176 Sent response for:7
01:38:41:176 Sent response for:6
01:38:41:176 Sent response for:5
01:38:41:176 Received response:7
01:38:41:176 Received response:6
01:38:41:176 Received response:5
01:38:41:192 Received response:9
01:38:41:192 Sent response for:9
01:38:41:192 Received response:10
01:38:41:192 Sent response for:10
01:38:41:192 Received response:11
01:38:41:192 Sent response for:11
01:38:41:192 Received response:12
01:38:41:192 Sent response for:12
01:38:41:160 Received response:0
01:38:41:207 Received response:16
01:38:41:207 Sent response for:16
01:38:41:223 Sent response for:19
01:38:41:223 Received response:19
01:38:41:223 Sent response for:13
01:38:41:223 Sent response for:14
01:38:41:223 Sent response for:15
01:38:41:223 Sent response for:17
01:38:41:238 Received response:13
01:38:41:238 Received response:14
01:38:41:238 Received response:15
01:38:41:223 Received response:17

我认为这个问题可能与这个问题有关,但建议的解决方案不起作用。那么,有人对变通方法有想法吗?

单声道中的HttpListener可以';t在同一时间内处理2个以上的请求

Mono 2.10太旧,请使用Mono 3.2.x 进行测试

如果您在这个版本中仍然面临同样的问题,那么尝试使用更新的版本:编译您自己的mono(主分支),因为刚刚实现并推送了一个新的标志"--server"。