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
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