How to properly handle a disconnected socket in C#?

Started by
5 comments, last by hplus0603 19 years, 9 months ago
In C# I noticed there's two ways to find out a socket has disconnected on the other end. One way is you get a SocketException, and another way is you get 0 bytes recieved. I use this same code on both types of disconnects, but for some reason it doesn't work on the 0 bytes recieved disconnect:

public void Shutdown_Socket()
{
	if (m_TCPSocket != null)
	{
		if (m_TCPSocket.Connected)
		{
			m_TCPSocket.Shutdown(SocketShutdown.Both);
			m_TCPSocket.Close();
			m_TCPSocket = null;
		}
	}
}



This works fine on the SocketException disconnect, but when I get a 0 bytes recieved disconnect then it seems to kill my whole server, no connects can be made, nor any information come in on existing sockets. It's like the socket system goes dead for several minutes, and if I wait a bit, then it starts working again, even a quick shutdown of the program and restart doesn't get the sockets working again, I just have to wait several minutes for it to clear or reset(?). So I figured maybe i'm handling the closed connection wrong or maybe need to do it in a different order or something?
Advertisement
When you close a TCP socket, the port in question cannot be bound to by another socket for a few minutes. This is to make sure that no lingering packets out in the ehter suddenly make it through and confuse your connection.

To turn this behavior off, turn off the SO_LINGER socket option (don't know what the C# version of that name is).

If that's not the issue, then try putting breakpoint in various places of your program to make sure they're actually hit, and step through the code to make sure it does what you think it does. For example, are you sure you always get into the innermost code in that sample you posted, in all cases?
enum Bool { True, False, FileNotFound };
Ahh, very interesting, I didn't know it did that. So is the proper way to reconnect quickly to just do it from a different port? What i'm doing is connecting to create an account, disconnect, then try to reconnect and play the game. Hrmm, maybe it'd be best to just do account creations from a different port than gameplay. Thanks for the help, i'll try some things out, and maybe someone knows the C# way to set SO_LINGER.
Been digging around into this TIME_WAIT state the socket enters and i'm wondering - is SO_REUSEADDR a better option to use than SO_LINGER?

Also, is a good way to check if this is actually my problem is by using the 'netstat' command?
Sorry, I brain-farted. Yes, it's SO_REUSEADDR that you want. Glad I set you on the right track, anyway :-)

netstat is, as you've found, a great tool for figuring out the behavior of these things.

However, if the socket is already open and accepting, and you're just waiting to accept another incoming connection, this is not your problem. SO_REUSEADDR only matters if you actually close your accepting socket, and then try to bind a new socket to the same port. Why would you be closing the socket and opening a new socket, and trying to re-bind it? This case should only ever happen if you try to quickly re-start a server after a crash.

Also, why disconnect after creating the account, only to re-connect again? Can't you just hand off to the "playing" code after the "creating" code is done?
enum Bool { True, False, FileNotFound };
The way I have my server is I have one listener socket that sits there waiting for connections, when it gets a connection it creates a new socket for this connection then goes back to listening for new connects. The reason I don't stay connected is when a person creates an account, it doesn't necessarily mean they'll be playing right after, the creation part is in a menu area and the person might just quit then, or mess with settings or who knows, so I thought it would be best to just disconnect and if they actually want to play, then do a reconnect. The strange thing is, when I disconnect from the player end and get a 0 bytes recieved on the server, the server listening socket seems to go to that TIME_WAIT state. I guess creating a child socket off that main listener socket on a new connect somehow intertwines the two.

[Edited by - nPawn on July 19, 2004 5:26:24 PM]
I wouldn't create new sockets for each client. That can often lead to problems with NAT gateways, for example.

Instead, I would open sockets for the ports that I want to listen to (say, 10001 for registration, and 10002 for gaming), and keep those sockets open. Then I'd use ::accept() to accept incoming connections for both registration and gaming. You close the socket returned by ::accept() when the session is completed, but not the listening socket.

This way, no connections should be in TIME_WAIT state on the server, and connecting to the listening sockets should be quick at all times (assuming you don't have too many outstanding connect requests in the listen queue).
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement