Обратный вызов httpListener вызывается дважды - PullRequest
0 голосов
/ 29 ноября 2018

У меня такая же проблема, как эта Здесь , но с использованием класса WebClient на моем клиенте, а также второй пример кода из это ответ .
Так что я могу сделатьполучить только один вызов от моего клиента WebClient?

Мой обратный вызов httplistener вызывается дважды, первый в порядке, но второй вызывает эту ошибку на HttpListenerContext context = Listener.EndGetContext(ar);

Система.Net.HttpListenerException: «Операция ввода-вывода была прервана из-за выхода из потока или запроса приложения»

Код сервера:

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 голосов
/ 30 ноября 2018

Пример в документации HttpListener может использоваться для обработки только одного вызова.Для обработки большего количества вызовов код между listener.Start() и listener.Stop() должен выполняться в цикле.

Чтобы сделать этот код асинхронным, все, что нужно, это использовать асинхронные версии HttpListener.GetContext и Stream.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.

Основной метод ждет нажатия клавиши сейчас, прежде чем подать сигнал об отмене и ждать, пока 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();
    }
}
...