diff --git a/src/ve/ucv/ciens/ccg/nxtar/Main.java b/src/ve/ucv/ciens/ccg/nxtar/Main.java index 2c615ef..eb9b427 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/Main.java +++ b/src/ve/ucv/ciens/ccg/nxtar/Main.java @@ -21,7 +21,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion; public class Main implements ApplicationListener, NetworkConnectionListener { private static final String TAG = "NXTAR_CORE_MAIN"; private static final String CLASS_NAME = Main.class.getSimpleName(); - + private OrthographicCamera camera; private SpriteBatch batch; private Texture texture; @@ -102,11 +102,11 @@ public class Main implements ApplicationListener, NetworkConnectionListener { } @Override - public synchronized void interfaceConnected(String iface) { - if(iface.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || iface.compareTo(RobotControlThread.THREAD_NAME) == 0) + public synchronized void networkStreamConnected(String streamName) { + if(streamName.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || streamName.compareTo(RobotControlThread.THREAD_NAME) == 0) connections += 1; if(connections >= 2){ - Gdx.app.debug(TAG, CLASS_NAME + ".interfaceConnected() :: Stopping service broadcast."); + Gdx.app.debug(TAG, CLASS_NAME + ".networkStreamConnected() :: Stopping service broadcast."); udpThread.finish(); mcastEnabler.disableMulticast(); } diff --git a/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java b/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java index a962cad..ab3d546 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java +++ b/src/ve/ucv/ciens/ccg/nxtar/interfaces/NetworkConnectionListener.java @@ -1,5 +1,5 @@ package ve.ucv.ciens.ccg.nxtar.interfaces; public interface NetworkConnectionListener { - public void interfaceConnected(String iface); + public void networkStreamConnected(String streamName); } diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java index 2446d69..1dbf3bc 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java @@ -42,7 +42,7 @@ public class RobotControlThread extends Thread { try{ client = server.accept(); if(netListener != null) - netListener.interfaceConnected(THREAD_NAME); + netListener.networkStreamConnected(THREAD_NAME); toaster.showShortToast("Client connected to RobotControlThread"); client.close(); }catch(IOException io){ diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java index 598fa96..7f78faf 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java @@ -10,10 +10,36 @@ import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; import com.badlogic.gdx.Gdx; +/** + * Ad hoc service discovery server thread. + * + *
This thread performs an ad hoc service discovery protocol. A multicast datagram packet is sent every + * 250 miliseconds carrying the string "NxtAR server is here!" on the multicast address defined + * in {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#MULTICAST_ADDRESS}. The port defined in + * {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#SERVER_UDP_PORT} is used for the transmissions. The server stops + * when another thread calls the {@link #finish()} method or the server fails to transmit {@link #MAX_RETRIES} packets in + * a row, whichever happens first.
+ * + * @author miky + * @since 27/11/2013 + */ public class ServiceDiscoveryThread extends Thread { + /** + * The name used to identify this thread. + */ public static final String THREAD_NAME = "ServiceDiscoveryThread"; + /** + * Tag used for logging. + */ private static final String TAG = "NXTAR_CORE_UDPTHREAD"; + /** + * Class name used for logging. + */ private static final String CLASS_NAME = ServiceDiscoveryThread.class.getSimpleName(); + /** + * Maximum number of transmission attempts before ending the thread abruptly. + */ + private static final int MAX_RETRIES = 5; private Object semaphore; private boolean done; @@ -21,48 +47,82 @@ public class ServiceDiscoveryThread extends Thread { private InetAddress group; public ServiceDiscoveryThread(){ + // Setup this thread name. super(THREAD_NAME); done = false; semaphore = new Object(); + // Try to get the InetAddress defined by the IP address defined in ProjectConstants.MULTICAST_ADDRESS. try{ group = InetAddress.getByName(ProjectConstants.MULTICAST_ADDRESS); - }catch(UnknownHostException uh){ } + }catch(UnknownHostException uh){ + group = null; + } + // Create a UDP socket at the port defined in ProjectConstants.SERVER_UDP_PORT. Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Creating multicast server."); try{ udpServer = new DatagramSocket(ProjectConstants.SERVER_UDP_PORT); }catch(IOException io){ - Gdx.app.error(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error creating UDP socket: " + io.getMessage(), io); + Gdx.app.error(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error creating UDP socket: " + io.getMessage()); + udpServer = null; } Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Multicast server created."); } @Override public void run(){ + int retries = 0; byte[] buffer = (new String("NxtAR server here!")).getBytes(); - try{ - while(true){ - synchronized(semaphore){ - if(done){ - udpServer.close(); - break; - } + + // If failed to get any of the required network elements then end the thread right away. + if(group == null || udpServer == null){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: No multicast address defined, ending thread."); + return; + } + if(group == null){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: No server available, ending thread."); + return; + } + + while(true){ + // Verify if the thread should end. If that is the case, close the server. + synchronized(semaphore){ + if(done){ + udpServer.close(); + break; } - //Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Resending packet"); + } + try{ + // End the thread if already at the last retry attempt after too many failed transmissions. + if(retries >= MAX_RETRIES){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Too many failed transmissions, ending thread."); + udpServer.close(); + break; + } + // Send the packet and reset the retry counter. DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, ProjectConstants.SERVER_UDP_PORT); udpServer.send(packet); - //Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Packet sent"); + retries = 0; try{ sleep(250L); }catch(InterruptedException ie){ } + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending packet: " + io.getMessage()); + retries += 1; } - }catch(IOException io){ - Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error sending packet: " + io.getMessage(), io); } + if(retries < MAX_RETRIES) + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Service discovery successfully terminated."); + else + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Service discovery terminated after too many failed transmissions."); } + /** + * Marks this thread as ready to end. + */ public void finish(){ synchronized(semaphore){ + Gdx.app.debug(TAG, CLASS_NAME + ".finish() :: Finishing service discovery thread."); done = true; } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java index 4e455a0..9bb70f1 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java @@ -42,7 +42,7 @@ public class VideoStreamingThread extends Thread { try{ client = server.accept(); if(netListener != null) - netListener.interfaceConnected(THREAD_NAME); + netListener.networkStreamConnected(THREAD_NAME); toaster.showShortToast("Client connected to VideoStreamingThread"); client.close(); }catch(IOException io){