【问题标题】:Stopping and restarting HttpListener?停止并重新启动 HttpListener?
【发布时间】:2013-06-01 12:49:52
【问题描述】:

我正在开发一个具有HttpListener 的应用程序。我的目标是让用户根据自己的选择关闭和打开监听器。我将侦听器放在一个新线程中,但在中止该线程时遇到问题。我在某处读到,如果您尝试中止处于非托管上下文中的线程,那么一旦它重新进入托管上下文,ThreadAbortException 就会被触发。似乎 HttpListener 的 GetContext() 方法是不受管理的,因为当我尝试中止线程时,在我对我的应用程序发出 Web 请求之前什么都没有发生。然后线程退出。问题是当我尝试终止线程时,我可能稍后在同一个端口上再次启动该线程,然后HttpListenerException 熄灭,表示前缀已注册。

我怎样才能杀死一个跨线程的HttpListener? GetContext() 是否有可管理的替代方案允许线程中止?我可以以非托管代码停止的方式中止线程吗?

【问题讨论】:

  • 如果可能,使用 BeginGetContext() 和 EndGetContext() 实现您的 HttpListener
  • 您能否发布一些代码,显示您如何启动线程等? - 您可能可以在某处潜入CancellationToken,并且每当用户“关闭”监听器时,您可以在CancellationToken 上调用Cancel 方法——它有一个延续,表示它应该调用HttpListener.Stop()

标签: c# multithreading httplistener


【解决方案1】:

类似的东西呢:

public class XListener
{
    HttpListener listener;

    public XListener(string prefix)
    {
        listener = new HttpListener();
        listener.Prefixes.Add(prefix);
    }

    public void StartListen()
    {
        if (!listener.IsListening)
        {
            listener.Start();

            Task.Factory.StartNew(async () =>
            {
                while (true) await Listen(listener);
            }, TaskCreationOptions.LongRunning);

            Console.WriteLine("Listener started");
        }
    }

    public void StopListen()
    {
        if (listener.IsListening)
        {
            listener.Stop();
            Console.WriteLine("Listener stopped");
        }
    }

    private async Task Listen(HttpListener l)
    {
        try
        {
            var ctx = await l.GetContextAsync();

            var text = "Hello World";
            var buffer = Encoding.UTF8.GetBytes(text);

            using (var response = ctx.Response)
            {
                ctx.Response.ContentLength64 = buffer.Length;
                ctx.Response.OutputStream.Write(buffer, 0, buffer.Length);
            }
        }
        catch (HttpListenerException)
        {
            Console.WriteLine("screw you guys, I'm going home!");
        }
    }
}

用法:

var x = new XListener("http://locahost:8080");

x.StartListen();
Thread.Sleep(500); // test purpose only

x.StopListen();
Thread.Sleep(500); // test purpose only

x.StartListen();

/* OUTPUT:
=> Listener started
=> Listener stopped
=> screw you guys, I'm going home!
=> Listener started */

【讨论】:

    【解决方案2】:

    您需要通知线程调用 HttpListener.Stop() 并通过调用 Thread.Join() 等待线程完成

    【讨论】:

    • 使用全局变量。我假设您的侦听线程正在检查传入连接,因此您可能会执行以下操作: while(!shutdown){ //listen };侦听器.Stop();在信号线程中你会做:shutdown=true;监听线程.Join();
    • matt-dot-net:问题是,线程正在等待HttpListener.GetContextAsync(),因此无法定期检查是否请求停止。唉,没有过载GetContextAsync(CancellationToken)。使线程停止监听的唯一方法是调用HttpListener.Stop()
    • GetContextAsync() 不会阻塞,除非您使用 await。您需要处理接收和处理传入连接的(a)同步性。应该允许此循环检查是否关闭,然后清理生成的异步任务。从 GetContextAsync() 返回的 Task 是您需要开始的地方,或者可能使用 Begin/End GetContext 的原始 IAsyncResult 模式
    【解决方案3】:

    您需要做的就是在侦听器上调用 stop。由于您的侦听器线程在GetContext 上被阻塞,您需要在另一个线程上执行此操作。 IIRC 这将导致GetContext 抛出,因此您需要处理该异常并进行清理。调用Thread.Abort 应该是您最后的手段,并且不会导致侦听器停止侦听,直到它被垃圾回收为止。

    【讨论】:

      【解决方案4】:
      using System;
      using System.Net;
      using System.Text;
      
      
         class Server
         {
              HttpListener listener = new HttpListener();
      
              public Server(string url)
              {
                  listener.Prefixes.Add(url);
              }
      
              void Callback(IAsyncResult result)
              {
                  HttpListenerContext context = listener.EndGetContext(result);
                  byte[] buffer = Encoding.UTF8.GetBytes("Hello world!");
                  context.Response.ContentLength64 = buffer.Length;
                  context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                  context.Response.OutputStream.Close();
                  listener.BeginGetContext(new AsyncCallback(Callback), listener);
              }
      
              public void Start()
              {
                  listener.Start();
                  listener.BeginGetContext(new AsyncCallback(Callback), listener);
              }
      
              public void Stop()
              {
                  listener.Stop();
              }
              
              public void Close()
              {
                  listener.Close();
              }
         }
      

      【讨论】:

      • 您应该解释它是如何工作的,以及究竟是做什么的。它会改善这个答案