I just found the answer to my problem:
The reason why the exception wouldn't show off is because it was actually thrown on a different thread. So removing my existing try/catch blocks in the main thread was of no help to show the exception. Adding a try/catch block to the Main method (to catch anything thrown in the entire application) wouldn't help either, since exceptions thrown on different threads don't fall in the catch blocks of the main thread.
My guess was correct: the problem came from the async TCP networking, which automatically generates and uses underlying threads. The exception was thrown whenever the client forcibly closed the TCP socket. Since the exception was on another thread, it silently forced the XNA runtime to stop.
This is the exception that was silently thrown and that stopped the runtime:
// Unable to read data from the transport connection: An existing connection was
// forcibly closed by the remote host.
// at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
Update: Just to make it perfectly clear for anyone who might encounter this problem in the future, here's how to correct it.
This is the problematic code:
// runs on the main thread
private void ReadAsync()
{
tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, readCallback, null);
}
// runs on an hidden underlying thread
// because of the asynchronous operation
private void OnRead(IAsyncResult result)
{
// dangerous exception thrown here
// which won't fall in the catch blocks of the main thread
tcpClient.GetStream().EndRead(result);
// process the received data
}
A simple try/catch block in the asynchronous thread will fix the problem:
private void OnRead(IAsyncResult result)
{
try
{
// dangerous exception thrown here
// which won't fall in the catch blocks of the main thread
tcpClient.GetStream().EndRead(result);
// process the received data
}
catch(Exception ex)
{
// handle the exception
}
}
Naturally, this pitfall is not limited to TCP networking. You might encounter this problem for any type of .NET asynchronous operation and also in your own custom threads, so be sure to always put some try/catch blocks at these places!