|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Socket Problem: Exclusive BindI'm developing a TCP/IP message based client and server. Now, with the server I have a strange problem which I can't get rid of without using a strange work-around. The server is intended to exclusively bind to a given interface and port. No other application (or the same application) is allowed to bind to that interface when a server component is already listening on that port. When I bind the listening socket for the firt time, this works fine. When I then close the socket and clear its reference, I expect the socket to have unbound, releasing the port. But, when I recreate a listening socket and attempt to bind it, it now gives me an "Address in use" exception. Eventhough I closed down the previous listening socket. It won't be until a while before the 2nd bind becomes successful, unless I close and restart the application. So, I've been searching and testing around as to why this socket doesn't really want to close for some period of time. Then, I decided to fiddle around some with the garbage collector. And guess what? After actively garbage collecting, I could then bind to the address without exceptions. Here an example of the "faulty" code: public void Open() { if (_listener != null) return; _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _listener.ExclusiveAddressUse = true; _listener.Bind(new IPEndPoint(string.IsNullOrEmpty(_interface) ? IPAddress.Any : IPAddress.Parse(_interface), _port)); _listener.Listen(10); for (int counter = 0; counter < 1; counter++) _listener.BeginAccept(_acceptCallback, new SocketState(_listener)); InternalStateChange(TcpipState.Listening); } public void Close(bool gracefully) { if (_listener == null) return; _listener.Close(); _listener = null; // Current active client connections are closed here. } Looking at this code, one would expect the server could be opened immediately after close has been called without exceptions. But it won't. On a 2nd Open() the "Address in use" exception is thrown on me unless I wait for anywhere between 5 and 90 seconds. Then, added this work-around which allows me to close and instantly open the server again. public void Open() { if (_listener != null) return; #region Extremely dumb workaround using the garbage collector in order to exclusively bind to an addres that's still occupied by a "closed" socket. for (int counter = 0; counter < 5; counter++) { _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _listener.ExclusiveAddressUse = true; try { _listener.Bind(new IPEndPoint(string.IsNullOrEmpty(_interface) ? IPAddress.Any : IPAddress.Parse(_interface), _port)); break; } catch (SocketException) { _listener = null; GC.Collect(); if (counter == 4) throw; } } #endregion _listener.Listen(10); for (int counter = 0; counter < 1; counter++) _listener.BeginAccept(_acceptCallback, new SocketState(_listener)); InternalStateChange(TcpipState.Listening); } What this does, is to attempt to bind to the address 5 times. If an address in use exception is thrown, the garbage collector is called each time. After the 5th time of trying it gives up and rethrows the exception. Attempting this 2 times is not enough, but attempting to bind 5 times in this loop is enough to bind guaranteed. This altogether means, that the garbage collector cleans up something of the previously bound sockets which the socket's Close() method does not. What gives? On Win32 I never had this problem. When I closed a socket there, I could instantly rebind to it. Point is.. I just want to be able to restart the server immediately after it has shut down, and not wait for an undetermined amount of time. Calling the GC to accomplish cleanup which apparently is needed doesn't seem a very graceful way to get it to work :( -Magic On Sat, 21 Apr 2007 08:35:06 -0700, MagicBox <avh^at^runbox.com> wrote:
> [...] Actually, what it means is that garbage collection has nothing to do with > This altogether means, that the garbage collector cleans up something of > the previously bound sockets which the socket's Close() method does not. > What > gives? On Win32 I never had this problem. When I closed a socket there, I > could instantly rebind to it. your problem. Otherwise, it would have been resolved the first time you collected garbage. Why you didn't run into this issue using Winsock, I don't know. Look up "TIME_WAIT". This is a classic issue people run into when they use sockets and want to be able to recreate a socket with an identical address immediately after closing the socket. I don't know if the .NET Socket class has an equivalent to the "SO_REUSEADDR" socket option, but if it does you can set that and that will allow you to reuse the same socket address before the previous socket has been released by the system. Pete Ofcourse I didn't stop continue to look for what would be wrong. I think I'm
on to it now though! You're right in that GC is not related to the problem, provided I'm on the right track. But first, the reason why I don't use the reuse address option (Which can be set in .NET with Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)) is that clients would still connect to the old socket. At least, the clients failed connecting, while the new socket reusing the address was up and listening. But I discovered a bug in my client source. I'm running a test application which has a window to interact with the server (start/stop etc) and a button on it, that spawns a window with a client instance. An easy way to test a server with multiple clients. After I had connected a couple of clients and then stopped the server I looked at the results of netstat -a and saw there were 3 sockets with status CLOSE_WAIT and 3 port maps with status FIN_WAIT_2. This means that I just forgot to close client sockets when remotely disconnected. (lol). However, I did set the client socket references to null. Hence why the GC seemed to fix the problem; it cleaned up the 3 client sockets, finally closing them. So, I'll have to revise the close 'n disconnect code of the client a little more. It will solve the problem :) If I wouldn't have found the problem now, I would have with your reply, as that would have pointed me towards using netstat. Thanks anyways! ----- Original Message ----- From: "Peter Duniho" <NpOeStPe***@nnowslpianmk.com> Newsgroups: microsoft.public.dotnet.frameworkSent: Saturday, April 21, 2007 6:49 PM Subject: Re: Socket Problem: Exclusive Bind Show quote > On Sat, 21 Apr 2007 08:35:06 -0700, MagicBox <avh^at^runbox.com> wrote: > >> [...] >> This altogether means, that the garbage collector cleans up something of >> the previously bound sockets which the socket's Close() method does not. >> What >> gives? On Win32 I never had this problem. When I closed a socket there, I >> could instantly rebind to it. > > Actually, what it means is that garbage collection has nothing to do with > your problem. Otherwise, it would have been resolved the first time you > collected garbage. Why you didn't run into this issue using Winsock, I > don't know. > > Look up "TIME_WAIT". This is a classic issue people run into when they > use sockets and want to be able to recreate a socket with an identical > address immediately after closing the socket. I don't know if the .NET > Socket class has an equivalent to the "SO_REUSEADDR" socket option, but if > it does you can set that and that will allow you to reuse the same socket > address before the previous socket has been released by the system. > > Pete O.o
Closing the client side sockets indeed fixed it. I just have another question though, somewhat related to socket closing. The error code 10054 (socket reset be remote endpoint AKA socket remotely disconnected), when does it occur? Normally closed remote sockets just make my EndReceive method return 0 bytes on the local socket. The method does not raise a SocketException with code 10054. Even when I attempt to send data on a remotely closed socket, I don't get this 10054 exception. I've tried disconnecting a network cable, then sending data and I do not get the error. Reason why I'd like to know is to make the exception handling robust. But I'd like to know when methods return such exception. As far as I can tell I will never get a SocketException with error code 10054. Is it caught internally in the async EndXXXX methods? |
|||||||||||||||||||||||