2

I'm implementing a Discover process that:

  • Open a UDP socket to listen for broadcast response on a given port
  • Send some requests (and expect later response)
  • Close the UDP socket after a given period of time

The first call works. But other call get a bind error. Address already in use: bind

I'm running Windows 7. I did some tests and found that after a channel.close(); Netstat still gives:

netstat -a -b -sp udp | grep 55224

UDP 0.0.0.0:55224 :

So the udp port is still opened at the OS level

I searched the web and it may be a leak at the os level : Some java Datagram Socket questions

I ran 2 tests one using NIO channel and one without (from a test found on the web). I reproduce my error with the NIO version but it works if I don`t use NIO.

I anybody can point me how I can make it works with NIO. The targetted platform is Android where I dont want to always listen for broadcast but only for repetitive period of time.

TEST SOCKET

    public void testConnectCloseWithSocket() {
    long tCumulative = 0;
    int errAt = -1;
    System.out.println("start...");
    for (int i = 0; i < 4000; i++) {
        try {
            errAt = i;
            DatagramSocket result = new DatagramSocket(null);
            result.bind(new InetSocketAddress(InetAddress.getLocalHost(), 9005));
            result.close();

            //success at last
            tCumulative = 0;

        } catch (Exception e) {
            System.out.println("Error (at="+errAt+") (waited="+tCumulative+"ms): " + e.getMessage());

            tCumulative+=50;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e1) {


            }
            i--;
        }
    }
    System.out.println("end...");

}

RESULT SOCKET<

start... Error (at=1319) (waited=0ms): Address already in use: Cannot bind

Error (at=1438) (waited=0ms): Address already in use: Cannot bind

Error (at=1587) (waited=0ms): Address already in use: Cannot bind

Error (at=1740) (waited=0ms): Address already in use: Cannot bind

end...


I did get some errors but the socket get closed properly... which is oki for my needs

TEST WITH CHANNEL

    public void testConnectCloseWithChannel() {
    long tCumulative = 0;
    int errAt = -1;
    System.out.println("start...");
    for (int i = 0; i < 4000; i++) {
        try {
            errAt = i;
            Selector selector = Selector.open();
            DatagramChannel channel = DatagramChannel.open();
            channel.configureBlocking(true);
            channel.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), 9005));
            SelectionKey clientKey = channel.register(selector, SelectionKey.OP_READ);
            clientKey.cancel();
            channel.close();

            //success at last
            tCumulative = 0;

        } catch (Exception e) {
            System.out.println("Error (at="+errAt+") (waited="+tCumulative+"ms): " + e.getMessage());

            tCumulative+=50;
            try {
                Thread.sleep(tCumulative);
            } catch (InterruptedException e1) {


            }
            i--;
        }
    }
    System.out.println("end...");

}

NOTE: It the channel.register is commented the test works ..


RESULT WITH CHANNEL

start... Error (at=0) (waited=0ms): null Error (at=0) (waited=50ms): Address already in use: bind

Error (at=0) (waited=100ms): Address already in use: bind

Error (at=0) (waited=150ms): Address already in use: bind ...


Thanks for any help

Community
  • 1
  • 1

2 Answers2

3

I did get some errors but the socket get closed properly... which is oki for my needs

No, in case you've got errors your channel is NOT closed properly.

You have to do close in the finally clause of your try block.

Selector selector = Selector.open();
try
{
  DatagramChannel channel = DatagramChannel.open();

  try
  {
    channel.configureBlocking(true);
    channel.socket().bind(
      new InetSocketAddress(InetAddress.getLocalHost(), 9005)
    );
    SelectionKey clientKey = channel.register(selector, SelectionKey.OP_READ);
    clientKey.cancel();
  }
  finally
  {
    channel.close();
  }
}
finally
{
  selector.close( )
}
Alexander Pogrebnyak
  • 44,836
  • 10
  • 105
  • 121
0

Some parts of a channel close are deferred to the next select() if the channel is registered with a Selector. It is documented somewhere in the forest of Selector, AbstractSelector, SelectorSpi, SelectableChannel, AbstractSelectableChannel, where I can never find it when I need it. If you're within the select loop and thread when you close the channel, you can make it immediate by calling selectNow().

user207421
  • 305,947
  • 44
  • 307
  • 483