/* Author: Mike McCarthy * File: MulticastRelayClient.java * Modified By: Julian Williams, Don Brutzman * Last update: 19 June 1999 * Program Invocation: command line entry * java mil.navy.nps.bridge.MulticastRelayClient * * Description: The client class code that connects with a Multicast Relay Server. * Clients are queried periodically by the server and must respond within * three consectutive attempts or the client will be removed from the server's * recipient list. This is really just a primitive example model of how a client * DIS-Java-VRML application would implement a connection to a unicast server * port to access multicast data. This would be necessary if a user/participant * was to say, call in on a modem from home. */ package mil.navy.nps.bridge; // package to which we belong import mil.navy.nps.dis.*; import java.io.*; import java.net.*; import java.util.*; /** * Relays datagrams to/from MulticastRelayServer. */ public class MulticastRelayClient extends Object { DatagramSocket unicastDatagramSocket = null; DatagramPacket connectDatagramPacket; DatagramPacket incomingDatagramPacket; DatagramPacket outgoingDatagramPacket; ProtocolDataUnit aPdu; static int MulticastRelayServerPort; static InetAddress MulticastRelayServerAddress; protected DatagramPacket replyPacket; static String disconnect = new String("disconnect"); DatagramPacket dispacket; static final int MAX_PACKET_SIZE = 1500; static final int SOCKET_TIMEOUT = 100; // 100 msec timeout per socket listen static final String DEFAULT_MCAST_ADDRESS = "224.2.181.145"; static final String DEFAULT_MCAST_PORT = "62040"; static final int TIME_TO_LIVE = 15; static MulticastSocket multicastSocket; static InetAddress destinationMulticastAddress = null; static int destinationMulticastPort = 62040; static boolean receivedFromServer, receivedFromLocalhostOrLAN; static String LocalHostName, LocalHostAddress, LocalHostSubnet, PduHostAddress, PduHostSubnet; /************************************************************************** main method that takes clients input of multicast server name and port that client wants to connect to. Creates and instance of client class and calls methods to mange server connect and datagram processing *************************************************************************/ public static final boolean DEBUG = true; /** Debugging output. Pass in a string, and it gets printed out on the console. You can pass in strings such as "foo " + bar.getName(). */ public static void debug(String pDebugString) { if(DEBUG) System.out.println("MulticastRelayClient: " + pDebugString); } /** * Guaranteed debugging output. Pass in a string, and it gets printed out on the console. * You can pass in strings such as "foo " + bar.getName(). */ protected static void trace (String pDiagnostic) { System.out.println("MulticastRelayClient: " + pDiagnostic); } public static void main(String[] args) { if (args.length != 2) { trace ("Usage: "); System.out.println(" java mil.navy.nps.bridge.MulticastRelayClient "); System.out.println(); System.out.println("Example:"); System.out.println(" java mil.navy.nps.bridge.MulticastRelayClient devo.cs.nps.navy.mil 8010"); System.exit (-1); } try { LocalHostName = InetAddress.getLocalHost().getHostName(); LocalHostAddress = InetAddress.getLocalHost().getHostAddress(); trace ("LocalHostName is " + LocalHostName); trace ("LocalHostAddress is " + LocalHostAddress); /* LocalHostSubnet = "unparsed!"; // initialize to avoid null if error occurs for (int index=LocalHostAddress.length(); index > 5; index--) // 5 is minimum length of valid subnet { if (LocalHostAddress.charAt (index-1) == '.') { LocalHostSubnet = LocalHostAddress.substring (0, index - 1); break; } } trace ("LocalHostSubnet is " + LocalHostSubnet); */ MulticastRelayServerPort = Integer.parseInt(args[1]); MulticastRelayServerAddress = InetAddress.getByName(args[0]); destinationMulticastAddress = InetAddress.getByName(DEFAULT_MCAST_ADDRESS); multicastSocket = new MulticastSocket(destinationMulticastPort); multicastSocket.joinGroup(destinationMulticastAddress); trace ("destinationMulticastAddress=" + DEFAULT_MCAST_ADDRESS + ", port=" + destinationMulticastPort); try { /* If you have problems compiling, you'll probably need to upgrade to JDK 1.2.2 or else eliminate this method. */ debug ("First try undeprecated JDK 1.2.2 setTimeToLive method"); multicastSocket.setTimeToLive(TIME_TO_LIVE); // JDK 1.2.2 trace ("multicastSocket.setTimeToLive((" + TIME_TO_LIVE + ");"); } catch(java.lang.NoSuchMethodError nsme) // likely java.lang.NoSuchMethodError if JDK 1.1 { trace ("Exception " + nsme); trace (" now trying deprecated JDK 1.1.8 setTTL method instead"); multicastSocket.setTTL((byte) TIME_TO_LIVE); // JDK 1.1.8, deprecated trace ("multicastSocket.setTTL((byte) " + TIME_TO_LIVE + ");"); } catch(Exception e) { trace ("Exception " + e); trace (" try deprecated JDK 1.1.8 setTTL method instead"); multicastSocket.setTTL((byte) TIME_TO_LIVE); // JDK 1.1.8, deprecated trace ("multicastSocket.setTTL((byte) " + TIME_TO_LIVE + ");"); } trace (" values < 15 are local LAN/campus only"); MulticastRelayClient multicastClient = new MulticastRelayClient(); multicastClient.multicastClientConnect(MulticastRelayServerAddress, MulticastRelayServerPort); } catch(IOException e) { trace (e.getMessage()); trace ("IO exception on initial socket setup, exiting"); } }//end main /************************************************************************ performs connections to the server ************************************************************************/ public void multicastClientConnect(InetAddress serverAddress, int serverPort) { String connect = new String("connect"); //set up connect request byte[] conbuf = new byte[connect.length()]; conbuf = connect.getBytes(); byte outBuffer[] = new byte[MAX_PACKET_SIZE]; byte inBuffer[] = new byte[MAX_PACKET_SIZE]; DatagramPacket outgoingDatagramPacket=null; try { // bind to the socket unicastDatagramSocket = new DatagramSocket(); trace ("Client unicast port is: " + unicastDatagramSocket.getLocalPort()); } catch (java.io.IOException e) { System.err.println("Could not create unicastDatagramSocket."); System.exit (-1); } try { connectDatagramPacket = new DatagramPacket(conbuf, conbuf.length, serverAddress, serverPort); unicastDatagramSocket.send(connectDatagramPacket); //send connect // create the thread instance that monitors client for disconnect Runnable ot = new ClientStatusThread(unicastDatagramSocket, serverAddress, serverPort); Thread csThread = new Thread(ot); csThread.setPriority(Thread.MIN_PRIORITY); //required for IRIX to properly schedule csThread.start(); while(true) { try { incomingDatagramPacket = new DatagramPacket(inBuffer, inBuffer.length); unicastDatagramSocket.setSoTimeout(SOCKET_TIMEOUT); // millisecond server response timeout if (unicastDatagramSocket.getSoTimeout() != SOCKET_TIMEOUT) trace ("MulticastRelayClient: unicastDatagramSocket timeout=" + unicastDatagramSocket.getSoTimeout() + " msec, not " + SOCKET_TIMEOUT); receivedFromServer = false; unicastDatagramSocket.receive(incomingDatagramPacket); receivedFromServer = true; // only reached if successful } catch(SocketException se) { trace (se + ", can't set timeout on unicastDatagramSocket"); finalize (); return; } catch (IOException e) { // timed out on read, which is OK, continue to loop }//endcatch if (receivedFromServer) { trace ("Data received from MulticastRelayServer " + connectDatagramPacket.getAddress().toString()); String s = respondToServer(unicastDatagramSocket, incomingDatagramPacket, serverAddress, serverPort); if (!(s.equals("hello"))) { aPdu = ProtocolDataUnit.datagramToPdu(incomingDatagramPacket); //convert datagram to PDU /* PduHostAddress = aPdu.getSendingHostAddress(); trace ("PduHostAddress is " + PduHostAddress); PduHostSubnet = "unparsed!"; // initialize to avoid null if error occurs for (int index=PduHostAddress.length(); index > 5; index--) // 5 is minimum length of valid subnet { if (PduHostAddress.charAt (index-1) == '.') { PduHostSubnet = PduHostAddress.substring (0, index - 1); break; } } trace ("PduHostSubnet is " + PduHostSubnet); if (PduHostSubnet.compareTo (LocalHostSubnet) == 0) { trace ("subnet match, sending to MulticastRelayServer"); } else { trace ("subnet mismatch, do not send to MulticastRelayServer"); } */ // resend from server to localhost/LAN!! try { incomingDatagramPacket.setAddress (destinationMulticastAddress); incomingDatagramPacket.setPort (destinationMulticastPort); multicastSocket.send(incomingDatagramPacket); trace ("...sent datagram (" + aPdu.pduName() + ") from server to local multicastSocket"); } catch(IOException ioe) { trace ("couldn't relay packet from server to localhost, " + ioe.getMessage()); finalize (); return; } }//endif } // listen for locally generated packet and send to server - but it loops back !! try{ outgoingDatagramPacket = new DatagramPacket(outBuffer, outBuffer.length); multicastSocket.setSoTimeout(SOCKET_TIMEOUT); // millisecond server response timeout receivedFromLocalhostOrLAN = false; multicastSocket.receive(outgoingDatagramPacket); receivedFromLocalhostOrLAN = true; // only reached if successful }//endtry catch(SocketException se) { trace (se + ", can't receive on multicastSocket"); finalize (); return; } catch (IOException e) { // timed out on read, which is OK, continue to loop }//endcatch if (receivedFromLocalhostOrLAN) // resend from localhost/LAN to server!!! { trace ("Received data from " + outgoingDatagramPacket.getAddress().getHostAddress()); aPdu = ProtocolDataUnit.datagramToPdu(outgoingDatagramPacket); //convert datagram to PDU try { unicastDatagramSocket.send(outgoingDatagramPacket); trace ("...sent datagram (" + aPdu.pduName() + ") from localhost/LAN to server on unicastDatagramSocket"); } catch(IOException ioe) { trace ("couldn't relay packet from localhost/LAN to server, " + ioe); finalize (); return; } } }//endwhile }//endtry catch (Exception e) { System.err.println("Exception: " + e); e.printStackTrace(); finalize (); return; }//endcatch }//end multicastClientConnect /************************************************************************ check for hello packets from server and respond with "iAmHere" for each packet received. ***********************************************************************/ public String respondToServer(DatagramSocket pSocket, DatagramPacket pPacket, InetAddress address, int port) { String iAmHere = new String("iAmHere"); //set up reply message byte[] replyBuffer = new byte[iAmHere.length()]; replyBuffer = iAmHere.getBytes(); String s = new String(pPacket.getData(), 0, pPacket.getLength()); if (s.equals("hello")) //valid message { System.out.print("\"Hello\" from Server... "); try{ replyPacket = new DatagramPacket(replyBuffer, replyBuffer.length, address, port); pSocket.send(replyPacket); trace ("localhost replies \"" + iAmHere + "\"\n"); }//endtry catch (IOException e) { System.err.println("Had a problem sending reply, IOException: " + e); e.printStackTrace(); }//endcatch }//endif return s; }//end respondToServer() /************************************************************************ deliberately close tunnel to MulticastRelayServer by sending disconnect message ***********************************************************************/ protected void finalize() { byte[] disconnectBuffer = new byte[disconnect.length()]; disconnectBuffer = disconnect.getBytes(); try { trace ("Sending \"disconnect\" to server and exiting MulticastRelayClient..."); dispacket = new DatagramPacket(disconnectBuffer, disconnectBuffer.length, MulticastRelayServerAddress, MulticastRelayServerPort); unicastDatagramSocket.send(dispacket); trace ("MulticastRelayClient finalize method complete.\n"); System.exit (0); }//endtry catch (Exception e) { System.err.println("MulticastRelayClient finalize had a problem: " + e); e.printStackTrace(); }//endcatch }//end finalize() }//end MulticastRelayClient Class /************************************************************************ the client status thread class which monitors keyboard input for client command to dissconnect from server ***********************************************************************/ class ClientStatusThread implements Runnable { static String disconnectCommand = new String("disconnect"); DatagramPacket packet; DatagramSocket the_socket; InetAddress the_address; int the_port; public ClientStatusThread(DatagramSocket in_socket, InetAddress con_address, int con_port) { System.out.println ("Monitor Thread has started. Enter \"quit\" to exit. \n"); this.setSocket(in_socket); this.setAddress(con_address); this.setPort(con_port); System.out.println ("Socket address/port: " + con_address.getHostName() + "/"+ con_port); System.out.println ("Querying multicast relay server..."); } void setSocket(DatagramSocket temp_socket) //bunch of interface methods to get socket and { //datagram objects instances the_socket = temp_socket; } void setAddress(InetAddress temp_address) { the_address = temp_address; } void setPort(int temp_port) { the_port = temp_port; } public void run() { byte[] disconnectBuffer = new byte[disconnectCommand.length()]; disconnectBuffer = disconnectCommand.getBytes(); try { Thread.sleep(100); // go to sleep so don't block } catch(InterruptedException interruptedException) { throw new RuntimeException("Exceptional sleep"); } //infinite loop to wait for keyboard input from user while(true) { try { String instring; BufferedReader dis = new BufferedReader(new InputStreamReader(System.in)); instring = dis.readLine(); // trace ("data in = : " + instring); if (instring.equalsIgnoreCase("quit")) { System.out.println ("Sending \"" + disconnectCommand + "\" to server and exiting MulticastRelayClient."); packet = new DatagramPacket(disconnectBuffer, disconnectBuffer.length, the_address, the_port); the_socket.send(packet); dis.close(); System.exit(0); } else System.out.println ("Enter \"quit\" to exit the program. \n"); }//end try catch(Exception e) { System.out.println ("Exception" + e); finalize (); System.exit(-1); }//end catch }//end while true }//end run /************************************************************************ deliberately close tunnel to MulticastRelayServer by sending disconnect message ***********************************************************************/ protected void finalize() { byte[] disconnectBuffer = new byte[disconnectCommand.length()]; disconnectBuffer = disconnectCommand.getBytes(); try { System.out.println ("Sending \"" + disconnectCommand + "\" to server and exiting MulticastRelayClient..."); packet = new DatagramPacket(disconnectBuffer, disconnectBuffer.length, the_address, the_port); the_socket.send(packet); System.out.println ("ClientStatusThread finalize method complete.\n"); System.exit (0); }//endtry catch (Exception e) { System.err.println("ClientStatusThread finalize had a problem: " + e); e.printStackTrace(); }//endcatch }//end finalize() }//end ClientStatusThread class