1

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.

Excrubulent
  • 467
  • 7
  • 15
  • What's the key to the dictionary? If it is the username, this seems to come from closing the wrong TCPClient – Eugen Rieck Feb 08 '12 at 01:39
  • Good thought, but I don't add the new connection to the dictionary until the clash has been resolved. I close the old connection using the dictionary, remove the dictionary entry, then add the new connection to the dictionary. – Excrubulent Feb 08 '12 at 01:42
  • Also, the key to the dictionary is indeed the username. – Excrubulent Feb 08 '12 at 01:46
  • Sorry to say so, I don't think it's the runtime, I think it's the code. Please try the following: Connect, record the relevant part of netstat, force disconnect, reconnect (error happens), check netstat. If the "old" connection is still there, you close the wrong tcpclient. – Eugen Rieck Feb 08 '12 at 01:50
  • Okay, I gotta go figure out how to use netstat now. I'll be back. – Excrubulent Feb 08 '12 at 01:53
  • Which OS? I see you use mono, is it Linux? – Eugen Rieck Feb 08 '12 at 01:56
  • I'm building and running the server on my Win 7 machine, although the plan is eventually to make it cross-platform. The client is on an Android, made with Mono for Android. – Excrubulent Feb 08 '12 at 05:14
  • Okay, using netstat, I can confirm that the old tcpclient does indeed disconnect. After calling close, just once, on the old tcpclient, neither the new nor the old connection is active. I've got the answer, though, I'll post it below. – Excrubulent Feb 08 '12 at 05:24

3 Answers3

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.

Lance Roberts
  • 22,383
  • 32
  • 112
  • 130
Christopher Johnson
  • 2,629
  • 7
  • 39
  • 70
  • Thanks Christopher, I've edited my question with code that I found to work after reading your code. If you want to add my code to your answer, I'd be happy to mark it as the answer. I don't want to right now, though, because your code is a bit different to mine. For a start, you're using a SocketClient, and TCPClient doesn't have a Dispose() method. Also, setting whichClient to null isn't necessary in my code. – Excrubulent Feb 08 '12 at 06:13
  • SocketClient is a custom class that I wrote that is more or less just an extension of TCPClient. But ya sure, I'll move your code to my answer. – Christopher Johnson Feb 08 '12 at 15:41
  • 1
    to answer your question of why it works using TCPClientA.Client.Close()...it's hard to say because we can't see your class structure. My assumption is that TCPClientA is a class with a property called Client that is your actual TcpClient. That's exactly the way that mine is set up but my class is called SocketClient. I'm actually surprised that TCPClientA.Close() does anything for you (assuming that my assumption on your class structure is correct). To actually close the the TcpClient, you have to drill into that object through your member list which is what I do and seems to work for you. – Christopher Johnson Feb 08 '12 at 15:52
  • TCPClient isn't my class structure, it's .NET's. I probably should've been clearer - TCPClientA is an instance of System.Net.Sockets.TCPClient from Mono's implementation of .NET 4.0. If this were a problem with my class, I wouldn't be asking the question, I'd just fix the class. I'm asking because it's a class that should just work, but somehow it doesn't. – Excrubulent Feb 08 '12 at 21:04
-2

This is clearly an error in your code. Merely closing one inbound connection cannot possibly close another one. Clearly something else is happening elsewhere in your code.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    It doesn't "Close()" it, it just causes it to stop functioning correctly. And if it is an error in the code, can you explain why calling TCPClientA.Client.Close(); followed by TCPClientA.Close(); fixes the problem, when just calling TCPClientA.Close(); by itself causes the problem? If I were closing the wrong TCPClient, then both of these actions should kill the wrong connection. The former doesn't, the latter does. – Excrubulent Feb 08 '12 at 11:54
-2

Looks like you have found a solution but just so you are aware there are many similar pitfalls when writing client/server applications in .net. There is an open source network library (which is fully supported in mono) where these problems have already been solved, networkComms.net. A basic sample is here.

Disclaimer: This is a commercial product and I am the founder.

caesay
  • 16,932
  • 15
  • 95
  • 160
MarcF
  • 3,169
  • 2
  • 30
  • 57