2011-05-05 2 views
5

Ho implementato un listener HTTP asincrono in C#.HttpListener asincrono ogni richiesta viene ricevuta due volte

ho seguito il tutorial fornito here by Microsoft

e ha trovato un altro tutorial che non ho stupidamente segnalibro e ora non riesce a trovare di nuovo. Significa che ho un codice che non avrei scritto in quel modo da solo, ma le spiegazioni fornite avevano senso, quindi l'ho seguito.

Ora sto affrontando due problemi:

In primo luogo, devo riavviare l'ascoltatore dopo ogni richiesta con Listener.Stop() e poi chiamare e di nuovo e secondo il metodo StartListening, quando faccio questo, ricevo ogni richiesta due volte. La richiesta viene inviata due volte, ma la ricevo due volte. Tuttavia non viene ricevuto due volte quando metto in pausa la discussione su cui sto ascoltando per circa 2 secondi.

Mi dispiace se sono abbastanza vago nelle mie spiegazioni, ma così è la mia comprensione del mio problema, non ho idea di cosa lo stia causando. Dato che il metodo di callback è dove succede la maggior parte delle cose, lo posterò, per favore dimmi se hai bisogno di altro codice. Qualsiasi aiuto sarà apprezzato, poiché sono davvero bloccato su questo.

public void ListenAsynchronously() 
    { 

     if (listener.Prefixes.Count == 0) foreach (string s in prefixes) listener.Prefixes.Add(s); 

     try 
     { 
      listener.Start(); 
     } 
     catch (Exception e) 
     { 
      Logging.logException(e); 
     } 

     System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Listen)); 
    } 


    private void Listen(object state) 
    { 
     while (listener.IsListening) 
     { 
      listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener); 
      listenForNextRequest.WaitOne(); 
     } 
    } 
    private void ListenerCallback(IAsyncResult ar) 
    { 

     HttpListener httplistener = ar.AsyncState as System.Net.HttpListener; 
     System.Net.HttpListenerContext context = null; 

     int requestNumber = System.Threading.Interlocked.Increment(ref requestCounter); 

     if (httplistener == null) return; 

     try 
     { 
      context = httplistener.EndGetContext(ar); 
     } 
     catch(Exception ex) 
     { 
      return; 
     } 
     finally 
     { 
      listenForNextRequest.Set(); 
     } 

     if (context == null) return; 


     System.Net.HttpListenerRequest request = context.Request; 

     if (request.HasEntityBody) 
     { 
      using (System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream, request.ContentEncoding)) 
      { 
       string requestData = sr.ReadToEnd(); 

       //Stuff I do with the request happens here 

      } 
     } 


     try 
     { 
      using (System.Net.HttpListenerResponse response = context.Response) 
      { 
       //response stuff happens here 

       } 

       byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); 
       response.ContentLength64 = buffer.LongLength; 
       response.OutputStream.Write(buffer, 0, buffer.Length); 
       response.Close(); 


       StopListening(); 
       //If I dont set the thread to sleep here, I receive the double requests 
       System.Threading.Thread.Sleep(2500); 

       ListenAsynchronously(); 


      } 
     } 
     catch (Exception e) 
     { 
     } 

    } 
+0

Senza sapere che cosa invoca questo callback, come WaitHandle listenForNextRequest viene utilizzata e quale metodo fa ListenAsynchronously, è un po 'un gioco di indovinare . – spender

+0

Ci scusiamo per questo, ho aggiunto il codice – Daniel

+0

che dovresti stampare su console (o accedere al file, se preferisci) alcune utili informazioni di debug e postare qui. Si prega di specificare quale sistema operativo si utilizza per eseguire questo codice e la sua versione. In questo modo sarà più semplice cercare di aiutarti ... Cordiali saluti, Giacomo – gsscoder

risposta

6

io non sono sicuro perché si sta chiamando StopListening() e ListenAsynchronously() nel metodo ListenerCallback(). Il metodo Listen() viene eseguito in una discussione e continuerà a ricevere ogni successiva richiesta in entrata. Se stavo scrivendo questo, non userei una variabile di istanza di HttpListener. Creare uno nuovo nel metodo ListenAsynchronously e passarlo nel vostro oggetto di stato, ad esempio,

public class HttpListenerCallbackState 
{ 
    private readonly HttpListener _listener; 
    private readonly AutoResetEvent _listenForNextRequest; 

    public HttpListenerCallbackState(HttpListener listener) 
    { 
     if (listener == null) throw new ArgumentNullException("listener"); 
     _listener = listener; 
     _listenForNextRequest = new AutoResetEvent(false); 
    } 

    public HttpListener Listener { get { return _listener; } } 
    public AutoResetEvent ListenForNextRequest { get { return _listenForNextRequest; } } 
} 

public class HttpRequestHandler 
{ 
    private int requestCounter = 0; 
    private ManualResetEvent stopEvent = new ManualResetEvent(false); 

    public void ListenAsynchronously(IEnumerable<string> prefixes) 
    { 
     HttpListener listener = new HttpListener(); 

     foreach (string s in prefixes) 
     { 
      listener.Prefixes.Add(s); 
     } 

     listener.Start(); 
     HttpListenerCallbackState state = new HttpListenerCallbackState(listener); 
     ThreadPool.QueueUserWorkItem(Listen, state); 
    } 

    public void StopListening() 
    { 
     stopEvent.Set(); 
    } 


    private void Listen(object state) 
    { 
     HttpListenerCallbackState callbackState = (HttpListenerCallbackState)state; 

     while (callbackState.Listener.IsListening) 
     { 
      callbackState.Listener.BeginGetContext(new AsyncCallback(ListenerCallback), callbackState); 
      int n = WaitHandle.WaitAny(new WaitHandle[] { callbackState.ListenForNextRequest, stopEvent}); 

      if (n == 1) 
      { 
       // stopEvent was signalled 
       callbackState.Listener.Stop(); 
       break; 
      } 
     } 
    } 

    private void ListenerCallback(IAsyncResult ar) 
    { 
     HttpListenerCallbackState callbackState = (HttpListenerCallbackState)ar.AsyncState; 
     HttpListenerContext context = null; 

     int requestNumber = Interlocked.Increment(ref requestCounter); 

     try 
     { 
      context = callbackState.Listener.EndGetContext(ar); 
     } 
     catch (Exception ex) 
     { 
      return; 
     } 
     finally 
     { 
      callbackState.ListenForNextRequest.Set(); 
     } 

     if (context == null) return; 


     HttpListenerRequest request = context.Request; 

     if (request.HasEntityBody) 
     { 
      using (System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream, request.ContentEncoding)) 
      { 
       string requestData = sr.ReadToEnd(); 

       //Stuff I do with the request happens here 
      } 
     } 


     try 
     { 
      using (HttpListenerResponse response = context.Response) 
      { 
       //response stuff happens here 
       string responseString = "Ok"; 

       byte[] buffer = Encoding.UTF8.GetBytes(responseString); 
       response.ContentLength64 = buffer.LongLength; 
       response.OutputStream.Write(buffer, 0, buffer.Length); 
       response.Close(); 
      } 
     } 
     catch (Exception e) 
     { 
     } 
    } 
} 
+1

Ho usato questo come base per servire pagine semplici da un'app .Net 2.0 e sono molto impressionato da quanto bene funzioni. Sul mio portatile sono in grado di servire circa 130-200 richieste al secondo per un semplice script di test del carico a thread singolo e circa 20 richieste al secondo per ognuna delle 5 istanze di quello script di test contemporaneamente. Il codice del server ha utilizzato circa il 12% della mia CPU durante questi test. –