I noticed unobvious behaviour of tcp table. If listener side doesn't properly disposes client but client gets finalized by GC, I expect in such case connection to hang in ESTABLISHED
state
in tcp table, because TcpClient
destructor
doesn't close socket. The actual behavior is that connection disappears from tcp table but it seems like port still remains occupied, because if I continue establish more connections, after 16k connections I'll get an SocketException
(10055)
, indicating that I'm out of free ports.
Code below reproduces this issue. Within infinite loop client-server connections are established in batches of 2k with period of 1 minute. I expect that if I start app, wait 7 minutes and call netstat
or GetExtendedTcpTable
I'll
see 14k ESTABLISHED
connections.
But when I do it I actually see that this number varies in range 1-5k and never goes above. So it comes to the final problem: app crashes in about 8 minutes with exception of exhausted tcp ports though netstat
nor
tcp table don't show any sign of port exhaustion.
var l = new TcpListener(IPAddress.Loopback, 0); l.Start(); Task.Factory.StartNew(() => { while (true) { // accepted TcpClients are periodically finalized by GC (without freeing socket connection) l.AcceptTcpClient(); } }); // must remember clients' references, otherwise, GC finalizes clients and since server side also finalizes accepted clients, tcp connection gets utilized var clients = new List<TcpClient>(); while (true) { for (var i = 0; i < 2000; i++) { clients.Add(new TcpClient()); clients.Last().Connect(IPAddress.Loopback, ((IPEndPoint) l.LocalEndpoint).Port); } Task.Delay(1000).GetAwaiter().GetResult(); }