httpListener回调两次被调用

时间:2018-11-29 12:48:38

标签: c# .net webclient httplistener

我也遇到类似的问题Here,但是我在客户端上使用了WebClient类,还使用了this answere中的第二个代码示例。
那么我该怎么做才能只得到一个从我的WebClient客户端拨打电话?

我的httplistener回调被调用了两次,第一个没问题,但是第二个在HttpListenerContext context = Listener.EndGetContext(ar);

上引发了此错误
  

System.Net.HttpListenerException:'由于线程退出或应用程序请求,I / O操作已中止'

服务器代码:

private void DoWork(object arg)
    {
        Listener = new HttpListener();
        Listener.Prefixes.Add("https://+:28210");
        Listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
        Console.WriteLine("Listening...");
        Listener.Start();
        Listener.BeginGetContext(ListenerContext, null);
        Console.ReadKey();      
    }

`

 private static void ListenerContext(IAsyncResult ar)
    {
        Console.WriteLine("Get Data...");
        HttpListenerContext context = Listener.EndGetContext(ar);
        HttpListenerRequest request = context.Request;
        HttpListenerResponse response = context.Response;
        HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
        Listener.BeginGetContext(ListenerContext, null);
        Console.WriteLine("Got Data!");

        //Some more Code...

        byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseData);
        response.ContentLength64 = buffer.Length;
        System.IO.Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);
    }



客户代码:

using (WebClient client = new WebClient())
            {
                string serialisedData = JsonConvert.SerializeObject(Data);
                client.Credentials = new NetworkCredential(config.UserData.Username, config.UserData.Password);
                byte[] responsebyte = client.UploadData(config.ServerAddress, System.Text.Encoding.UTF8.GetBytes(serialisedData));
                response = System.Text.Encoding.UTF8.GetString(responsebyte);
            }

1 个答案:

答案 0 :(得分:1)

HttpListener's文档中的示例可用于仅处理 个呼叫。要处理更多呼叫,listener.Start()listener.Stop()之间的代码必须循环运行。

要使此代码异步,所需要做的就是使用HttpListener.GetContextStream.Write的异步版本:

public static async Task  ListenAsync(params string[] prefixes)
{
    if (prefixes == null || prefixes.Length == 0)
    throw new ArgumentException("prefixes");

    using(var listener = new HttpListener())
    {
        // Add the prefixes.
        foreach (string s in prefixes)
        {
            listener.Prefixes.Add(s);
        }
        listener.Start();
        Console.WriteLine("Listening...");
        for (int i=0;i<3;i++)
        {
            var context = await listener.GetContextAsync();
            Console.WriteLine($"Got {i}");

            var response = context.Response;
            string responseString = $"<HTML><BODY> Hello world {i}!</BODY></HTML>";
            var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
            response.ContentLength64 = buffer.Length;
            using(var output = response.OutputStream)
            {
                await output.WriteAsync(buffer,0,buffer.Length);
            }
        }
        listener.Stop();
    }
}

ListenAsync必须仅被调用一次并等待直到完成。在这种情况下,退出前它最多可循环处理3个请求。

在控制台应用程序中调用它可以很简单:

static async Task Main(string[] args)
{
    Console.WriteLine("Starting !");
    await ListenAsync(@"http://*:19999/");
    Console.WriteLine("Finished");
}

要以线程安全的方式停止侦听器,必须使用CancellationToken表示侦听器必须取消。 GetContextAsync()本身不接受取消令牌。尽管可以通过调用HttpListener.Abort中止该操作。如果GetContextAsync()在等待时发生,将抛出ObjectDisposedException

main方法现在要等待按键,然后发出取消信号并等待ListenAsync完成当前请求:

static async Task Main(string[] args)
{
    Console.WriteLine("Starting !");
    using(var cts=new CancellationTokenSource())
    {
        try
        {
            var task= ListenAsync(cts.Token, @"http://*:19999/");
            Console.ReadKey();
            cts.Cancel();
            await task;
        }
        catch(ObjectDisposedException)
        {
            Console.WriteLine("Listener aborted");
        }
    }
    Console.WriteLine("Finished");
}

ListenAsync本身在取消令牌上使用token.Register(()=>listener.Abort());来中止侦听器。 for循环更改为while(!token.IsCancellationRequested),使收听者可以继续收听,直到按下某个键为止:

public static async Task  ListenAsync(CancellationToken token,params string[] prefixes)
{
    if (prefixes == null || prefixes.Length == 0)
    throw new ArgumentException("prefixes");

    using(var listener = new HttpListener())
    {
        foreach (string s in prefixes)
        {
            listener.Prefixes.Add(s);
        }
        listener.Start();
        Console.WriteLine("Listening. Hit any key to end.");

        //Abort if the token is signalled            
        token.Register(()=>listener.Abort());

        int i=0;
        //Loop until cancellation is requested
        while (!token.IsCancellationRequested)
        {
            var context = await listener.GetContextAsync();
            Console.WriteLine($"Got {i++}");

            var response = context.Response;
            string responseString = $"<HTML><BODY> Hello world {i}!</BODY></HTML>";
            var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
            response.ContentLength64 = buffer.Length;
            using(var output = response.OutputStream)
            {
                await output.WriteAsync(buffer,0,buffer.Length);
            }
        }
        listener.Stop();
    }
}