1

I have a problem using DatagramSocket. The problem is that I can't run two Android JUnit tests one after another using DatagramSockets because the second tests throws the following exception:

java.net.BindException: Address already in use

I guess this will be a problem in Activities too, because when the Activity moves from background to foreground, I would probably get the same exceptions.

I'm satisfied if I could either disconnect and reconnect the socket or if I'm able to reuse the old socket but I can't get one of them working. My reusing concept looked like this:

    if (serverSocket == null || !serverSocket.isBound())  {
        serverSocket = new DatagramSocket(9800);
    }

But this doesn't work, same exception. It doesn't even work when I try to reinstantiate it (when I don't negate the 2nd term).

I tried to dis- and reconnect it...

        serverSocket.disconnect();
        serverSocket = new DatagramSocket(null); 
        serverSocket.setReuseAddress(true);
        serverSocket.bind(new InetSocketAddress("localhost", 9800));

Doesn't work either. Same exception when executing the second test. What can I do to solve this? Here is the whole class:

public class UdpListener extends Thread implements Subject {
    private DatagramSocket serverSocket;
    private DatagramPacket receivedPacket;
    private volatile boolean running = false;
    private String sentence = "";

    private Observer observer;

    private static final String TAG = "UdpListener";

    public UdpListener(Observer o) throws SocketException  {
        observer = o;

        if (serverSocket == null || !serverSocket.isBound())  {
            serverSocket = new DatagramSocket(9800);
        }
    }

    @Override
    public void run() {
        setName(TAG);
        while (isRunning())  {
            byte[] receivedData = new byte[1024];
            receivedPacket = new DatagramPacket(receivedData, receivedData.length);
            try {
                serverSocket.receive(receivedPacket);
            } 
            catch (IOException e) {
                Log.w(TAG, e.getMessage());
            }

            try {
                sentence = new String(receivedPacket.getData(), 0, receivedPacket.getLength(), "UTF-8");
                if (UdpState.UPDATE.toString().equals(sentence))  {
                    notifyObserver();
                }
            } 
            catch (UnsupportedEncodingException e) {
                Log.w(TAG, e.getMessage());
            }
        }
    }

    private boolean isRunning() {
        return running;
    }

    public void setThreadRunning(boolean running) throws SocketException {
        this.running = running;
        if (running)  {
//          serverSocket = new DatagramSocket(9800);
            this.start();
        }
        else  {
//          serverSocket.disconnect();
//          serverSocket = new DatagramSocket(null); 
//          serverSocket.setReuseAddress(true);
//          serverSocket.bind(new InetSocketAddress("localhost", 9800));
        }
    }

    @Override
    public void notifyObserver() {
        observer.update();
    }
}
Bevor
  • 8,396
  • 15
  • 77
  • 141
  • 1
    on a side note, why do you extend Thread instead of implementing Runnable? – Green Day Jan 17 '12 at 18:28
  • I meant no harm by it. But in Runnable how should I start and stop the thread, retrieve its running state, or how can I name it as above? – Bevor Jan 17 '12 at 18:40
  • You would create a Runnable object and pass it into the new Thread constructor. Then you just call thread.start() on the thread. Calling start invokes the run method. You would stop the thread the same way (eg exit run method in your example) and so on – Green Day Jan 17 '12 at 19:22
  • I don't know why this is more beneficial. I'm not persuaded yet. – Bevor Jan 17 '12 at 19:30
  • you can read more here. http://stackoverflow.com/questions/541487/java-implements-runnable-vs-extends-thread generally speaking you should extend a Class when you want to modify its behavior, not for "convenience" reasons :) – Green Day Jan 17 '12 at 19:37
  • 1
    Good, I'll give it a try. Unfortunately this doesn't solve my socket problem. – Bevor Jan 17 '12 at 20:12
  • 1
    A lot of people extend `Thread` and even more extend classes for "convenience" reasons. – Gray Jan 17 '12 at 22:14
  • Although @Green Day might be right about the close, if this turns out to be a timing issue, what I would recommend is putting the connect in a loop and try every 100ms or so with a `Thread.sleep(100);` and then put a timeout on your junit test. We use that pattern a lot when waiting for network ports to clear. – Gray Jan 17 '12 at 22:15

2 Answers2

3

ok I was just looking at your code for a while and I just realized you never call:

serverSocket.close();

call it after you call:

serverSocket.disconnect();

and your problem should be solved.

Green Day
  • 654
  • 1
  • 8
  • 17
  • Actually not even serverSocket.disconnect() works. If I set threadRunning to false, the whole test suddenly just hangs and does nothing anymore. – Bevor Jan 18 '12 at 19:15
  • Ok, I think it works now. When I discard everything in else branch but socket.close(), I can execute both tests. add: In the IF branch, I uncommended serverSocket = new DatagramSocket(9800) and I discarded everything belonging to socket handling so my socket handling is only in setThreadRunning method. – Bevor Jan 18 '12 at 19:42
0

Your code:

if (serverSocket == null || !serverSocket.isBound())  {
    serverSocket = new DatagramSocket(9800);
}

Will be short-circuited if the serverSocket instance is null, aka the second check to ensure it is not bound may not get called.

Keep in mind also that there may be a lag period between calling disconnect on your Datagram socket and when the OS actually releases said socket for reuse.

Perception
  • 79,279
  • 19
  • 185
  • 195
  • I know, this is just for testing. So I need a solution how to deal with that. I guess I'm not the first one with such a problem. Unfortunately I didn't find a working solution yet. – Bevor Jan 17 '12 at 19:05
  • You can explicitly unbind the socket after each test and sleep the thread for a certain period of time, to give the OS time to clean up the socket registration. – Perception Jan 17 '12 at 19:29