2019-11-08
Closing System.Net.Sockets.TcpClient kills the connection for other TCPClients at the same IP Address
stackoverflow
Question

Just to be clear, all of the TCPClients I'm referring to here are not instances of my own class, they are all instances of System.Net.Sockets.TcpClient from Mono's implementation of .NET 4.0.

I have a server that is listening for client connections, as servers do. Whenever it gets a new client it creates a new TCPClient to handle the connection on a new thread. I'm keeping track of all the connections and threads with a dictionary. If the client disconnects, it sends a disconnect message to the server, the TCPClient is closed, the dictionary entry is removed and the thread dies a natural death. No fuss, no muss. The server can handle multiple clients with no problem.

However, I'm simulating what happens if the client gets disconnected, doesn't have a chance to send a disconnect message, then reconnects. I'm detecting whether a client has reconnected with a username system (it'll be more secure when I'm done testing). If I just make a new TCPClient and leave the old one running, the system works just fine, but then I have a bunch of useless threads lying around taking up space and doing nothing. Slackers.

So I try to close the TCPClient associated with the old connection. When I do that, the new TCPClient also dies and the client program throws this error:

E/mono    (12944): Unhandled Exception: System.IO.IOException: Write failure ---> System.Net.Sockets.SocketException: The socket has been shut down

And the server throws this error:

Unable to write data to the transport connection: An established connection was aborted by the software in your host machine.

Cannot read from a closed TextReader.

So closing the old TCPClient with a remote endpoint of say: 192.168.1.10:50001

Also breaks the new TCPClient with a remote endpoint of say:192.168.1.10:50002

So the two TCPClient objects have the same remote endpoint IP address, but different remote endpoint ports. But closing the one seems to stop the other from working. I want to be able to close the old TCPClient to do my cleanup, without closing the new TCPClient.

I suspect this is something to do with how TCPClient works with sockets at a low level, but not having any real understanding of that, I'm not in a position to fix it.

Answer
1

I had a similar issue on my socket server. I used a simple List instead of a dictionary to hold all of my current connections. In a continuous while loop that listens for new streams, I have a try / catch and in the catch block it kills the client if it has disconnected.

Something like this on the sever.cs:

public static void CloseClient(SocketClient whichClient)
        {
            ClientList.Remove(whichClient);
            whichClient.Client.Close();
            // dispose of the client object
            whichClient.Dispose();
            whichClient = null;
        }

and then a simple dispose method on the client:

public void Dispose()
        {
            System.GC.SuppressFinalize(this);
        }

EDIT: this paste is the OPs resolution which he or she found on their own with help from my code.

So to clarify, the situation is that I have two TCPClient objects TCPClientA and TCPClientB with different remote endpoints ports, but the same IP:

TCPClientA.Client.RemoteEndPoint.ToString();

returns: 192.168.1.10:50001

TCPClientB.Client.RemoteEndPoint.ToString();

returns: 192.168.1.10:50002

TCPClientA needs to be cleaned up because it's no longer useful, so I call

TCPClientA.Close();

But this closes the socket for the client at the other end of TCPClientB, for some reason. However, writing

TCPClientA.Client.Close();
TCPClientA.Close();

Successfully closes TCPClientA without interfering with TCPClientB. So I've fixed the problem, but I don't understand why it works that way.

1

I had a similar issue on my socket server. I used a simple List instead of a dictionary to hold all of my current connections. In a continuous while loop that listens for new streams, I have a try / catch and in the catch block it kills the client if it has disconnected.

Something like this on the sever.cs:

public static void CloseClient(SocketClient whichClient)
        {
            ClientList.Remove(whichClient);
            whichClient.Client.Close();
            // dispose of the client object
            whichClient.Dispose();
            whichClient = null;
        }

and then a simple dispose method on the client:

public void Dispose()
        {
            System.GC.SuppressFinalize(this);
        }

EDIT: this paste is the OPs resolution which he or she found on their own with help from my code.

So to clarify, the situation is that I have two TCPClient objects TCPClientA and TCPClientB with different remote endpoints ports, but the same IP:

TCPClientA.Client.RemoteEndPoint.ToString();

returns: 192.168.1.10:50001

TCPClientB.Client.RemoteEndPoint.ToString();

returns: 192.168.1.10:50002

TCPClientA needs to be cleaned up because it's no longer useful, so I call

TCPClientA.Close();

But this closes the socket for the client at the other end of TCPClientB, for some reason. However, writing

TCPClientA.Client.Close();
TCPClientA.Close();

Successfully closes TCPClientA without interfering with TCPClientB. So I've fixed the problem, but I don't understand why it works that way.

Closing System.Net.Sockets.TcpClient kills the connection for other TCPClients at the same IP Address
See more ...