Socket Problem: Exclusive Bind

  • Thread starter Thread starter MagicBox
  • Start date Start date
M

MagicBox

Hello all,

I'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
 
[...]
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
 
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" <[email protected]>
Newsgroups: microsoft.public.dotnet.framework
Sent: Saturday, April 21, 2007 6:49 PM
Subject: Re: Socket Problem: Exclusive Bind

[...]
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?
 
Back
Top