views:

2059

answers:

5

Greetings, I'm trying to find a way to 'unbind' a socket from a particular IP/Port combination. My pseudocode looks like this:

ClassA a = new ClassA(); //(class A  instantiates socket and binds it to 127.0.0.1:4567) 
//do something 
//...much later, a has been garbage-collected away.
ClassA aa = new ClassA(); //crash here.

At this point, .Net informs me that I've already got a socket bound to 127.0.0.1:4567, which is technically true. But no matter what code I put in ClassA's destructor, or no matter what functions I call on the socket (I've tried .Close() and .Disconnect(true)), the socket remains proudly bound to 127.0.0.1:4567. What do I do to be able to 'un-bind' the socket?


EDIT: I'm not relying solely on garbage collection (though I tried that approach as well). I tried calling a.Close() or a.Disconnect() and only then instantiating aa; this doesn't solve the problem.


EDIT: I've also tried implementing IDisposable, but the code never got there without my calling the method (which was the equivalent of earlier attempts, as the method would simply try .Close and .Disconnect). Let me try calling .Dispose directly and get back to you.


EDIT (lots of edits, apologies): Implementing IDisposable and calling a.Dispose() from where 'a' loses scope doesn't work - my Dispose implementation still has to call either .Close or .Disconnect(true) (or .Shutdown(Both)) but none of those unbind the socket.

Any help would be appreciated!

A: 

you can't rely on object being garbage collected in C# (i assume you're using c#, based on tagging) if it holds resources like being bound to the network resource like in your example, or holding some other kind of stream, a file stream would be a common example.

you have to assure to release the resources that the object is holding, so that it can be garbage collected. otherwise it won't be garbage collected, but remain living somewhere in the memory. your pseudocode example doesn't provide that you're doing the resources releasing, you just state that the object gets (should get) garbage collected.

zappan
A: 

The garbage collector doesn't guarantee you that the socket will ever be closed. For a complete example read this MSDN example.

The main point is to actually call Socket.Close() as soon as possible. For example, ClassA could implement IDisposable and use it like this:

using (ClassA a = new ClassA()) 
{
    // code goes here
}
// 'a' is now disposed and the socket is closed
David Schmitt
A: 

The garbage collector runs the finalizer of the object at some indeterminate time. You could implement the IDisposable interface and call the Dispose() method before the object looses Scope - or let the using statement do that for you.

see http://msdn.microsoft.com/en-us/library/system.idisposable.aspx and http://msdn.microsoft.com/en-us/library/yh598w02.aspx

edit: works fine for me

using System;
using System.Net.Sockets;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
     class ClassA : IDisposable
     {
      protected Socket s;
      public ClassA()
      {
       s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
       s.Bind(new IPEndPoint(IPAddress.Any, 5678));
      }

      public void Dispose()
      {
       s.Close();
      }
     }

     static void Main(string[] args)
     {
      using(ClassA a=new ClassA()) {
      }
      using (ClassA b = new ClassA())
      {
      }
     }
    }
}
VolkerK
Its possible that IPAddress.Any is whats permitting this to work (its using different IP Addresses each time). In any case, this wouldn't solve the problem (sorry for being unclear): ClassA is a network-communications manager class and the Socket is a class-wide variable.
AlexeyMK
+3  A: 

(this is what finally got everything to work for me)

Make sure EVERY socket that the socket in A connects to has

socket.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress, true);

set upon being initiated.

AlexeyMK
Doesn't that suggest that in fact the previous socket is still there and bound to the port? http://msdn.microsoft.com/en-us/library/cc319473.aspxReuseAddress A value you use to allow binding to an address that is already in use.*is* in use, not *has been*
VolkerK
VolkerK; I think you're right. This is where you have to be careful and make sure that the previous instance really is gone; by default, Berkley sockets will wait for 5 minutes before releasing a port/socket combination; this is the only workaround I found to work.
AlexeyMK
A: 

The best solution is to retry to bind the socket a few times (2-3). On the first attempt, if it fails, i have found that it will properly (and permanently) close the original socket.

HTH,

_NT