2011-05-05 2 views
5

J'ai implémenté un écouteur HTTP asynchrone dans C#.HttpListener asynchrone a reçu chaque demande deux fois

J'ai suivi le tutoriel fourni here by Microsoft

et a trouvé un autre tutoriel que je bêtement pas et maintenant bookmarked ne peut pas trouver de nouveau. Ce qui veut dire que j'ai un code que je n'aurais pas écrit de cette façon moi-même mais les explications fournies avaient du sens alors j'ai suivi ça.

Maintenant, je suis confronté à deux problèmes:

D'abord, je dois redémarrer l'auditeur après chaque demande avec Listener.Stop(), puis appeler la méthode StartListening et encore et, deuxièmement, quand je fais cela, je reçois chaque demande deux fois. La requête n'est pas envoyée deux fois, mais je la reçois deux fois. Il n'est cependant pas reçu deux fois lorsque je mets en pause le fil que j'écoute pendant environ 2 secondes.

Je suis désolé si je suis assez vague dans mes explications, mais ma compréhension de mon problème est aussi, je n'ai aucune idée de ce qui le cause. Puisque la méthode de rappel est là où la plupart des choses se passent, je vais juste poster, s'il vous plaît dites-moi si vous avez besoin de plus de code. Toute aide sera volontiers appréciée, car je suis vraiment bloqué sur celui-ci.

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

Sans savoir ce qui appelle ce rappel, comment WaitHandle listenForNextRequest est utilisé et quelle méthode ListenAsynchronously fait, il est un peu un jeu de devinettes . – spender

+0

Désolé pour cela, j'ai ajouté le code – Daniel

+0

, vous devez imprimer sur la console (ou se connecter au fichier, si vous préférez) quelques informations de débogage utiles et poster ici. Veuillez spécifier quel système d'exploitation vous utilisez pour exécuter ce code et sa version. De cette façon sera plus simple en essayant de vous aider ... Cordialement, Giacomo – gsscoder

Répondre

6

Je ne sais pas pourquoi vous appelez StopListening() et ListenAsynchronously() dans votre méthode ListenerCallback(). La méthode Listen() est en cours d'exécution dans un thread et continuera à recevoir chaque requête entrante suivante. Si j'écrivais ceci, je n'utiliserais pas une variable d'instance de HttpListener. Créer un nouveau dans votre méthode ListenAsynchronously et le transmettre dans votre objet d'état, par exemple,

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

Je l'ai utilisé comme base pour servir des pages simples à partir d'une application. Net 2.0 et je suis très impressionné par son fonctionnement. Sur mon ordinateur portable, je peux traiter environ 130 à 200 requêtes par seconde vers un script de test de charge simple à un seul thread et environ 20 requêtes par seconde vers chacune des 5 instances de ce script de test simultanément. Le code du serveur utilisait environ 12% de mon CPU pendant ces tests. –