0

我在单声道中使用 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

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

4

1 回答 1

0

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

如果您在使用此版本时仍然遇到同样的问题,请尝试使用更新的版本:编译您自己的单声道(主分支),因为刚刚实现并推送了一个新标志“--server”。

于 2013-08-16T14:00:20.680 回答