2013-09-05 7 views
6

Nel mio programma webserver, sto ottenendo le voci nel Visualizzatore eventi sul server che contiene il seguente stack trace:Cosa causa una eccezione NullReferenceException in .NET Threading/accept TCP connections?

Framework Version: v4.0.30319 
Description: The process was terminated due to an unhandled exception. 
Exception Info: System.ArgumentNullException 
Stack: 
    at System.Net.FixedSizeReader.ReadCallback(System.IAsyncResult) 
    at System.Net.LazyAsyncResult.Complete(IntPtr) 
    at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
    at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
    at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) 
    at System.Net.ContextAwareResult.Complete(IntPtr) 
    at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) 
    at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) 

Inutile dire, questo si blocca il processo, il che significa che il server va giù.

Poiché nessuno degli stacktrace menziona il mio codice, sono piuttosto perplesso. Si tratta di un bug in .NET? In tal caso, esistono soluzioni alternative note? O c'è una causa nota a questa particolare eccezione?

Il nome CompletionPortCallback menzionato nello stacktrace mi porta a credere che si verifica quando il server tenta di accettare una connessione TCP in entrata, quindi includerò il codice pertinente per quello di seguito. Ovviamente sono contento di includere altri codici se pensi che il problema sia altrove.

La chiamata a BeginAccept assomiglia a questo:

Qui, _listeningSocket è di tipo System.Net.Sockets.Socket.

Il metodo acceptSocket è illustrato di seguito. Assumerò che i commenti spieghino abbastanza bene il codice; se no, sono felice di chiarire in un commento. Poiché questo codice viene eseguito in modalità RELEASE sul server live, lo #if DEBUG sarà ovviamente falso.

private void acceptSocket(IAsyncResult result) 
{ 
#if DEBUG 
    // Workaround for bug in .NET 4.0 and 4.5: 
    // https://connect.microsoft.com/VisualStudio/feedback/details/535917 
    new Thread(() => 
#endif 
    { 
     // Ensure that this callback is really due to a new connection (might be due to listening socket closure) 
     if (!IsListening) 
      return; 

     // Get the socket 
     Socket socket = null; 
     try { socket = _listeningSocket.EndAccept(result); } 
     catch (SocketException) { } // can happen if the remote party has closed the socket while it was waiting for us to accept 
     catch (ObjectDisposedException) { } 
     catch (NullReferenceException) { if (_listeningSocket != null) throw; } // can happen if StopListening is called at precisely the "wrong" time 

     // Schedule the next socket accept 
     if (_listeningSocket != null) 
      try { _listeningSocket.BeginAccept(acceptSocket, null); } 
      catch (NullReferenceException) { if (_listeningSocket != null) throw; } // can happen if StopListening is called at precisely the "wrong" time 

     // Handle this connection 
     if (socket != null) 
      HandleConnection(socket); 
    } 
#if DEBUG 
    ).Start(); 
#endif 
} 
+0

Per quanto ne so, è necessario passare '_listeningSocket' nella chiamata' BeginAccept() 'anziché null:' _listeningSocket.BeginAccept (acceptSocket, _listeningSocket); 'vedere i documenti e gli esempi. Ecco perché hai ricevuto l'eccezione ArgumentNull. –

+0

Questo è chiaramente un bug nel framework .NET. Se avevi utilizzato l'API, l'errore si sarebbe verificato proprio in quel momento, non nel profondo delle viscere del BCL su un callback IO. La domanda ora è come aggirarla. – usr

risposta

-1

Penso che questa eccezione venga generata perché si passa un riferimento null come secondo parametro.

Da MSDN Socket.BeginAccept Documentazione (http://msdn.microsoft.com/de-de/library/5bb431f9.aspx):

È necessario creare un metodo di callback che implementa il delegato AsyncCallback e passa il suo nome al metodo BeginAccept. Per fare ciò, come minimo, è necessario passare l'oggetto Socket listening a BeginAccept tramite il parametro state. Se il tuo callback ha bisogno di più informazioni, puoi creare una piccola classe per contenere il socket e le altre informazioni richieste. Passa un'istanza di questa classe al metodo BeginAccept tramite il parametro state.

+0

Questo non è vero. Non si preoccupa affatto del parametro * state *. Lo memorizza semplicemente inalterato nell'oggetto 'IAsyncResult' dove è possibile recuperarlo nel callback. Può essere 'nullo', va bene e questo ha sempre funzionato per me. – Timwi

4

La risposta è tanto semplice quanto deludente.

Si scopre che il ArgumentNullException è stato effettivamente gettato dal mio stesso codice, che eseguito nel callback per la chiamata asincrona e avrebbe dovuto essere nella traccia dello stack. Mi sono fidato troppo della traccia dello stack; il fatto che questo non sia stato mostrato nella traccia dello stack mi ha portato su una pista sbagliata per molto tempo.

Come sanno gli sviluppatori C#, l'istruzione throw; (non throw e;) deve mantenere inalterata la traccia dello stack di eccezioni. System.Net.FixedSizeReader.ReadCallback intercetta e rilancia l'eccezione tramite tale istruzione throw;, tuttavia l'analisi dello stack mostrata nel Visualizzatore eventi è stata troncata. Posso solo supporre che questo sia un bug nel CLR o nel Visualizzatore eventi o in qualche interazione tra i due, causando solo la parte della traccia dello stack dalle istruzioni throw; in poi da mostrare.

Quando eseguivo il software sulla console anziché come un servizio, cosa che avrei dovuto pensare molto prima, la traccia dello stack completo dell'eccezione mostrata sulla console indicava che la vera causa dell'eccezione era la mia codice.

+0

grazie per aver risposto alla tua stessa domanda, il tuo suggerimento che lo stacktrace è stato troncato e per eseguirlo come una console mi ha aiutato molto. Nel mio caso mi aspettavo che la mia WebException.response venisse riempita dove non era -> nullreference -> ho scaricato il mio servizio nservicebus fuori dall'acqua senza alcun errore –

0

In base a https://connect.microsoft.com/VisualStudio/feedback/details/535917 e alla mia esperienza personale, è probabile che si tratti di un'eccezione a sorpresa di un altro problema precedente.

Provare ad aggiungere un gestore di eccezioni globale e registrare & svuotare tutte le eccezioni sul disco, quindi controllare il file di registro dopo un arresto anomalo per trovare la causa reale.

Problemi correlati