0

I am working on a Java UDP application. There is a thread in the application whose only job is to listen to a server on a specific port.

I wrote the application under the mistaken assumption that the server I am listening to will always be up; this, however, was a bad assumption.

If my application starts after the server is running, then everything works fine. If my application starts before the server is up, or if the server is restarted while my application is running, my application breaks.

MainClass.java

public class MainClass { public static void main(String[] args){ ListeningClass myListeningClass = new ListeningClass(); Thread listenerThread = new Thread(myListeningClass); listenerThread.setName("My Listening Thread"); listenerThread.start(); } } 

ListeningClass.java

public class ListeningClass implements Runnable { private volatile boolean run = true; private byte[] receiveBuffer; private int receiveBufferSize; private DatagramSocket myDatagramSocket; private DatagramPacket myDatagramPacket; @Override public void run(){ try { myDatagramSocket = new DatagramSocket(null); InetSocketAddress myInetSocketAddress = new InetSocketAddress(15347); myDatagramSocket.bind(myInetSocketAddress); receiveBuffer = new byte[2047]; myDatagramPacket = new DatagramPacket(receiveBuffer, 2047); while(run){ myDatagramSocket.receive(myDatagramPacket); byte[] data = myDatagramPacket.getData(); receiveBufferSize = myDatagramPacket.getLength(); // process the data received here } } catch (SocketException se){ // do stuff } catch (IOException ioe){ // do stuff } } public boolean isRun(){ return run; } public void setRun(boolean run){ this.run = run; } } 

Like I said, if my application starts after the server is running, everything works perfectly, just as expected. However, if my application starts before the server is running, then nothing works. Obviously, is is because the thread tries to open the connection once, and if it fails, then it never tries again.

I moved the DatagramSocket open code to within the while block but that wasn't pretty. I got a bunch of "port already bound" errors.

So, how can I reconstruct this so that it works properly?

2 Answers 2

1

It's not really a concurrency question. You just need to check the exceptions thrown on receive and handle appropriately. In this case, rebind the socket. See the docs for receive.

For example:

... while(run) { try { myDatagramSocket.receive(myDatagramPacket); byte[] data = myDatagramPacket.getData(); receiveBufferSize = myDatagramPacket.getLength(); // process the data received here } catch (IOException ioe) { // Perhaps use PortUnreachableException but not guaranteed rebind(myDatagramSocket, myInetSocketAddress); } } private void rebind(DatagramSocket s, InetSocketAddress addr) { s.bind(addr); } 

I think that should be enough. the point is, you only want to rebind if your receive indicates there's an I/O problem with the server. You're binding for each receive if you place the bind in the loop - which is ok in your happy-path situation.

Sign up to request clarification or add additional context in comments.

Comments

1

The important things to note here are the precise points in which the program fails and the type of exception that you're given.

Presumably it fails on line myDatagramSocket.receive(myDatagramPacket);, but double check with the stacktrace on your exception. The second thing to check is the type of exception. Is it a SocketException or a subclass of SocketException? Is there a specific error code? Try to be as specific as possible.

At this point, you should surround the section of code that fails in its own try catch within the while loop. You want to be able to say that should it fail, your thread will sleep and try again after a hiatus (to not bombard the server with requests). And to simply things further, I would section the code in its own method, so you would expect something like:

public class ListeningClass implements Runnable { private static final int MAX_RETRIES = 30; private static final int RETRY_SLEEPTIME = 30000; private volatile boolean run = true; private InetSocketAddress myInetSocketAddress; @Override public void run(){ try { DatagramSocket myDatagramSocket = new DatagramSocket(null); myInetSocketAddress = new InetSocketAddress(15347); myDatagramSocket.bind(myInetSocketAddress); byte[] receiveBuffer = new byte[2047]; DatagramPacket myDatagramPacket = new DatagramPacket(receiveBuffer, 2047); awaitRequests(myDatagramSocket, myDatagramPacket) } catch (SocketException se){ // do stuff } catch (IOException ioe){ // do stuff } } private void awaitRequests(DatagramSocket myDatagramSocket, DatagramPacket myDatagramPacket) throws SocketException, IOException { int maxRetries = MAX_RETRIES; while(run){ try { myDatagramSocket.receive(myDatagramPacket); byte[] data = myDatagramPacket.getData(); // Packet received correctly, reset retry attempts maxRetries = MAX_RETRIES; process(myDatagramPacket); } catch (SocketException e) { maxRetries--; // Good place to write to log of some kind if(maxRetries == 0) { throw e; } Thread.currentThread().sleep(RETRY_SLEEPTIME); // Lets attempt to restablish the connection reconnect(myDatagramSocket); } } } private void process(DatagramPacket myDatagramPacket) { int receiveBufferSize = myDatagramPacket.getLength(); // process the data received here } private void reconnect(DatagramSocket myDatagramSocket) { myDatagramSocket.bind(myInetSocketAddress); } public boolean isRun(){ return run; } public void setRun(boolean run){ this.run = run; } } 

Note a couple things. I only caught SocketException because I am assuming the type of exception that you're getting is a SocketException. If you're getting an IOException of some kind, then you should check that. Better still if you're specifying the subtype of that exception. The reason is this: you don't want to blanket handle all errors, but only those pertaining to the server being down. If the program lacks authentication to open the socket, you would want to fail immediately, not continually retry.

The second thing is that I've separated the processing of the packet in its own method, because I think that's the proper thing to do in these cases.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.