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
}
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. –
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