5

私自身の Web サーバー ソフトウェアでは、次のスタック トレースを含むサーバーのイベント ビューアーにエントリを取得しています。

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*)

言うまでもなく、これによりプロセスがクラッシュします。つまり、サーバーがダウンします。

どのスタック トレースにも自分のコードが記載されていないため、非常に困惑しています。これは .NET のバグですか? もしそうなら、既知の回避策はありますか? または、この特定の例外には既知の原因がありますか?

スタックトレースに記載されている名前CompletionPortCallbackから、サーバーが着信 TCP 接続を受け入れようとしたときに発生すると思われるため、関連するコードを以下に含めます。もちろん、問題が他の場所にあると思われる場合は、喜んで他のコードを含めます。

への呼び出しは次のBeginAcceptようになります。

_listeningSocket.BeginAccept(acceptSocket, null);

ここで_listeningSocketは、タイプSystem.Net.Sockets.Socketです。

以下にそのacceptSocket方法を示します。コメントがコードを十分に説明していると仮定します。そうでない場合は、コメントで明確にさせていただきます。このコードはライブ サーバー上で RELEASE モードで実行されるため#if DEBUG、もちろん false になります。

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
}
4

3 に答える 3

4

答えは、がっかりするほど簡単です。

は、非同期呼び出しへのコールバックで実行され、スタック トレースにあるはずArgumentNullExceptionの自分のコードによって実際にスローされたことが判明しました。スタック トレースを信頼しすぎました。これがスタック トレースに表示されなかったという事実は、私を長い間間違った方向に導きました。

C# 開発者が知っているように、throw;ステートメント (not throw e;) は、例外スタック トレースを変更しないままにしておくことになっています。System.Net.FixedSizeReader.ReadCallbackこのようなステートメントを介して例外をキャッチして再スローしますthrow;が、イベント ビューアーに表示されるスタック トレースは切り捨てられます。これは、CLR またはイベント ビューアーのバグ、または 2 つの間の何らかの相互作用であり、throw;命令以降のスタック トレースの一部のみが表示されると推測することしかできません。

サービスとしてではなくコンソールでソフトウェアを実行すると (もっと早く考えるべきだった)、例外の完全なスタック トレースがコンソールに表示され、例外の真の原因が自分のコードにあることがわかりました。

于 2013-09-05T19:14:38.037 に答える
0

https://connect.microsoft.com/VisualStudio/feedback/details/535917と私自身の経験に基づくと、これは以前の別の問題からのノックオン例外である可能性があります。

グローバル例外ハンドラを追加して、すべての例外をディスクに記録してフラッシュし、クラッシュ後にログ ファイルをチェックして、本当の原因を見つけてください。

于 2014-09-01T13:05:35.487 に答える
-1

2 番目のパラメーターとして null 参照を渡すため、この Exception がスローされると思います。

MSDN Socket.BeginAccept ドキュメント ( http://msdn.microsoft.com/de-de/library/5bb431f9.aspx ) から:

AsyncCallback デリゲートを実装するコールバック メソッドを作成し、その名前を BeginAccept メソッドに渡す必要があります。これを行うには、最低限、リッスンしている Socket オブジェクトを状態パラメーターを介して BeginAccept に渡す必要があります。コールバックにさらに情報が必要な場合は、ソケットとその他の必要な情報を保持する小さなクラスを作成できます。このクラスのインスタンスを、state パラメーターを介して BeginAccept メソッドに渡します。

于 2013-09-05T11:32:03.067 に答える