I wrote a sample unit test with TcpListener. It works fine in .NET Framework 4.8, but it fails in .Net8.0-windows.
TcpListener tcpListener;
TaskScheduler.UnobservedTaskException += HandleUnhandledException;
public void TestTcpListener()
{
tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 12345));
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(null, null);
tcpListener.Stop();
}
I subscribed to TaskScheduler's UnobservedTaskException event to catch the exception and then I run GC.Collect() And GC.WaitForPendingFinalizers() at the end of this UT(in TearDown method). I got this error message.
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. (The I/O operation has been aborted because of either a thread exit or an application request.)
---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request.
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Net.Sockets.Socket>.GetResult(Int16 token)
at System.Net.Sockets.TcpListener.<AcceptTcpClientAsync>g__WaitAndWrap|32_0(ValueTask`1 task)
--- End of inner exception stack trace ---
You need to observe the result of BeginAcceptTcpClient
with a matching EndAcceptTcpClient
public void TestTcpListener()
{
tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 12345));
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(MyCallback, null);
tcpListener.Stop();
}
void MyCallback(IAsyncResult ar)
{
try
{
var client = tcpListener.EndAcceptTcpClient(ar);
}
catch(Exception ex)
{
// you should end up here!!!
Console.WriteLine($"Callback handler: {ex.Message}");
}
}
You can also do this via the return parameter from BeginAcceptTcpClient
, but the callback approach is more common.
You can also make use of the state parameter if you are concerned that the tcpListener
field might have been wiped:
public void TestTcpListener()
{
tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 12345));
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(MyCallback, tcpListener);
tcpListener.Stop();
}
static void MyCallback(IAsyncResult ar)
{
try
{
var listener = (TcpListener)ar.AsyncState!;
var client = listener.EndAcceptTcpClient(ar);
}
catch (Exception ex)
{
Console.WriteLine($"Callback handler: {ex.Message}");
}
}