////////////////////////////////////////////////////////////////////// // SPONHost.java // // --Host Process used to communicate on the SPON Network-- // // // // Supervised Peer-to-peer Overlay Network (SPON) Simulator // // // // Computer Science Independent Study Project // // Modified by: Peter Keeler // // Added multiple-parent support for greater system stability // // Refactored code to eliminate ugly 'mega run loop' // // Changed to packets carrying any data type, instead of // // carrying strings only // // Added ability of any node in SPON tree to accept connections // // All data now sent via UDP (no 'special' communications) // // // // Computer Science M.S.E. Project // // Programmed by: Joey Chau // // Copyright: February, 2003 // // // // Research Advisor: Christian Scheideler // // Computer Science // // Johns Hopkins University // ////////////////////////////////////////////////////////////////////// import java.io.*; import java.net.*; import java.util.*; /************************************************************** The Actual Host process used to communicate on the SPON Network *************************************************************/ class SPONHost { private String mLeftChild = "0.0.0.0"; // address of priumary left child private String mRightChild = "0.0.0.0"; // Address of primary right child private String sLeftChild = "0.0.0.0"; // Address of secondary left child private String sRightChild = "0.0.0.0"; // Address of secondary right child private String coparent = "0.0.0.0"; // Address of co-parent private String mParent = "0.0.0.0"; // Address of parent private String sParent = "0.0.0.0"; // Address of secondary parent private String mAncestor = "0.0.0.0"; // Address of grandparent private String mNext = "0.0.0.0"; // Address of Next Node for a root node private boolean isSupervisor = false; private boolean isAccepting = true; private boolean mLon = false; // left child active? private boolean mRon = false; // right child active? private boolean mNon = false; private boolean sLon = false; // secondary lchild active? private boolean sRon = false; // secondary rchild active? private boolean pDead = false; // parent is dead? (using coparent) private boolean cpDead = false; // coparent is dead? (contact grandparent) private int JOIN = SPONConstants.JOIN; private int JOINCONF = SPONConstants.JOINCONF; private int LOCATE = SPONConstants.LOCATE; private int LOCNOT = SPONConstants.LOCNOT; private int LOCCONF = SPONConstants.LOCCONF; private int MESSAGE = SPONConstants.MESSAGE; private int MESSCONF = SPONConstants.MESSCONF; private int SUPERMESS = SPONConstants.SUPERMESS; private int REMOVE = SPONConstants.REMOVE; private int SPONSOCKETNO = SPONConstants.SPONSOCKETNO; private int PACKETSIZE = SPONConstants.PACKETSIZE; // 4 KB packets private int WINDOWSIZE = SPONConstants.WINDOWSIZE; private int PACKMAX = SPONConstants.PACKMAX; // Maintenance Communcation private DatagramSocket mUDPSocket; // Display Frame private TalkFrame frame = new TalkFrame(); // General Information private String sentence; private BufferedReader inFromUser = new BufferedReader( new InputStreamReader(System.in)); private InetAddress mSupervisorAddress; private InetAddress mParentAddress; private InetAddress sParentAddress; private InetAddress mLocalAddress; private String mMyName = "0.0.0.0"; private int poolMax = 0; private Vector freePool = new Vector(poolMax); private RootVector myTree = new RootVector(); // how many transient nodes may be held in the free pool private int poolRatio = 1; // freePool.size()/poolRatio = how many nodes will be entered into the system. static boolean debug = true; private int countdownRetries = 3; private long countdownTimeout = 60000; private long countdownTimer = (new Date()).getTime(); private boolean parentDown = false; private boolean coparentDown = false; private int nextRequest = 0; public SPONHost(boolean rt, boolean listen) { isSupervisor = rt; isAccepting = listen; if (isSupervisor) { mMyName = "[SUPERVISOR]"; } } static void dbg(String str){ if(debug) { System.out.println(str); System.out.flush(); } } public static void main(String argv[]) throws Exception { boolean rt = false; boolean listen = false; try { if (argv[0].compareToIgnoreCase("root") == 0) rt = true; try { if (argv[1].compareToIgnoreCase("listen") == 0) listen = true; } catch (ArrayIndexOutOfBoundsException aioo) { // do nothing; supervisor is not listening to children. } } catch (ArrayIndexOutOfBoundsException aioobe) { // do nothing; it's a regular node } SPONHost myHost = new SPONHost(rt, listen); myHost.hostGo(); } public void hostGo() { while (true) { // Broadcast Communication try { mLocalAddress = InetAddress.getLocalHost(); if (isSupervisor) { mParent = mLocalAddress.getHostAddress(); coparent = mLocalAddress.getHostAddress(); myTree = new RootVector(mParent,true); } else { myTree = new RootVector(mLocalAddress.getHostAddress()); } } catch (java.net.UnknownHostException jnuhe) { System.out.println("Local address not known; exiting."); System.exit(-1); } mSupervisorAddress = mLocalAddress; System.out.println( "Running SPON Host on <" + mLocalAddress + ">" ); try { mUDPSocket = new DatagramSocket( SPONSOCKETNO, mLocalAddress ); dbg( "Started Maintenance Socket: " + mUDPSocket.toString() + " listening to " + mUDPSocket.getLocalPort() ); } catch (java.net.SocketException jnse) { System.out.println("Socket could not be created; exiting."); System.exit(-1); } frame.setTitle( "SPON" ); frame.show(); // a root node only waits for connections. if (!isSupervisor) join(); communicate(); checkPool(); } } /*************************************************************** * Join a SPON network ***************************************************************/ private void join() { // Join the SPON network int joinStat = 0; // 0=Failed, 1=Success, otherwise quit while( joinStat != 1 ) { if( frame.readyToJoin() ) { InetAddress mJoinAddress; String[] joinInfo = frame.getJoinInfo(); mMyName = joinInfo[0]; sentence = joinInfo[1]; // join process dbg("connecting to " + sentence + " ..."); try{ mJoinAddress = InetAddress.getByName( sentence ); joinStat = 1; } catch (java.net.UnknownHostException jnuhee) { System.out.println("Could not resolve requested host."); joinStat = 0; continue; } catch( Exception e ) { dbg( "Error finding host name " + e.getMessage() ); joinStat = 0; continue; } // Datagram send int retry = 3; int timeoutLength = 30000; dbg( "Sending Join Information" ); SPONPacket buffer = new SPONPacket(); buffer.MSGTYPE = JOIN; buffer.payload = mLocalAddress.getHostAddress(); DatagramPacket mDPacket; try { byte[] bytes = getBytes(buffer); mDPacket = new DatagramPacket( bytes, bytes.length, mJoinAddress, SPONSOCKETNO ); } catch (java.io.IOException ioe) { dbg("Error creating join packet, exiting."); joinStat = 0; continue; } DatagramPacket mReceivePacket = new DatagramPacket( new byte[PACKETSIZE], PACKETSIZE); while( retry > 0 ) { //dbg( "Sending: " + mDPacket ); // Diagnostic dbg( " Data: " + new String( mDPacket.getData() ) ); dbg( " Length: " + mDPacket.getLength() ); // Try to join the network. No other packets should be // received before the join is completed. Join packet // should contain more information now, though. try{ mUDPSocket.setSoTimeout( timeoutLength ); mUDPSocket.send( mDPacket ); mUDPSocket.receive( mReceivePacket ); break; } catch( SocketTimeoutException e ) { retry--; joinStat = 0; dbg( "Time Out!"); } catch( Exception e ) { retry = 0; joinStat = 0; dbg( "Socket Failure" ); } } if( retry == 0 ) { joinStat = 0; continue; } dbg("Received " + mReceivePacket.getLength() + " bytes =>" + (new String(mReceivePacket.getData())) + " from " + mDPacket.getAddress() ); // mReceivePacket now contains a SPONPacket, // which has information on parent's addr and co-parent. mParentAddress = mReceivePacket.getAddress(); SPONPacket joinRcvPacket = new SPONPacket(); try { joinRcvPacket = (SPONPacket)((new ObjectInputStream(new ByteArrayInputStream(mReceivePacket.getData()))).readObject()); } catch (Exception anyException) { System.out.println("Packet improperly received."); System.out.println("Loss of control packets could lead to" + " system inconsistency."); } String joinData = (String) joinRcvPacket.payload; StringTokenizer joinST = new StringTokenizer(joinData, ";"); mParent = joinST.nextToken(); sParent = joinST.nextToken(); mAncestor = joinST.nextToken(); String rootAddr = joinST.nextToken(); try { mSupervisorAddress = InetAddress.getByName(rootAddr); } catch (java.net.UnknownHostException jnuhe3) { System.out.println("Could not look up root."); joinStat = 0; } // at this point, the joining node should have no children // and no coparents, only the supervisor and its parents. dbg( "Joined SPON Supervised by: " + mSupervisorAddress ); } } } // join method /************************************************************ * After joining the network, receive and handle packets ************************************************************/ private void communicate() { // SPON Communication Loop // Receive a packet, check its type, process that type of packet. int TimeoutLength = 500; try { mUDPSocket.setSoTimeout( TimeoutLength ); } catch (java.net.SocketException jnse) { System.out.println("Could not set socket timeout."); } frame.setTitle( "SPON " + mLocalAddress.getHostAddress() ); // Active State Flag boolean stillActive = true; boolean exitOnClose = false; while(stillActive) { if (((new Date()).getTime() - countdownTimer) > countdownTimeout) { SPONPacket timerPack = new SPONPacket(); timerPack.MSGTYPE = MESSAGE; timerPack.payload = "-1"; if (!parentDown) { UDPSend(mParent,timerPack); } else if (!coparentDown) { UDPSend(sParent,timerPack); } else { UDPSend(mAncestor,timerPack); } countdownRetries--; countdownTimer = (new Date()).getTime(); } // check to see if current parent has timed out. if (countdownRetries < 0) { if (!parentDown) { parentDown = true; // if so, notify coparent; send a REMOVE packet // indicating the downed parent as leaving to both // the coparent and to ancestor. SPONPacket remPack = new SPONPacket(); remPack.MSGTYPE = REMOVE; remPack.payload = mParent; UDPSend(sParent, remPack); UDPSend(mAncestor,remPack); } else if (!coparentDown) { coparentDown = true; // if coparent times out, notify grandparent; // send REMOVE packets indicating parent and coparent // as leaving. SPONPacket remPack = new SPONPacket(); remPack.MSGTYPE = REMOVE; remPack.payload = mParent; UDPSend(mAncestor,remPack); } else { // if grandparent times out, give up disconnect(); } } DatagramPacket mReceiveUDP = new DatagramPacket( new byte[PACKETSIZE], PACKETSIZE); try { mUDPSocket.receive( mReceiveUDP ); System.out.println("Packet received! " + mReceiveUDP); System.out.flush(); // mReceiveUDP should contain a SPONpacket. SPONPacket inPacket = (SPONPacket)((new ObjectInputStream(new ByteArrayInputStream(mReceiveUDP.getData()))).readObject()); System.out.println("SPONPacket detected! Type " + inPacket.MSGTYPE); InetAddress senderAddress = mReceiveUDP.getAddress(); if (((senderAddress.getHostAddress()).compareToIgnoreCase(mParent)==0) || ((senderAddress.getHostAddress()).compareToIgnoreCase(sParent)==0) || ((senderAddress.getHostAddress()).compareToIgnoreCase(mAncestor)==0) ) { countdownTimer = (new Date()).getTime(); } if (inPacket.MSGTYPE == JOIN) joinMsg(inPacket); else if (inPacket.MSGTYPE == JOINCONF) joinConfMsg(inPacket); else if (inPacket.MSGTYPE == LOCATE) locateMsg(inPacket); else if (inPacket.MSGTYPE == LOCNOT) locateNotMsg(inPacket); else if (inPacket.MSGTYPE == LOCCONF) locateConfMsg(inPacket); else if (inPacket.MSGTYPE == MESSAGE) { dataMsg(inPacket); SPONPacket replyPack = inPacket; replyPack.MSGTYPE = MESSCONF; UDPSend(senderAddress.getHostAddress(),replyPack); } else if (inPacket.MSGTYPE == MESSCONF) messConfMsg(inPacket); else if (inPacket.MSGTYPE == SUPERMESS) superMsg(inPacket); else if (inPacket.MSGTYPE == REMOVE) removeMsg(inPacket); else dbg( "Recieved unknown packet: " + mReceiveUDP ); }// try catch( SocketTimeoutException e ) { } catch (IOException ioe) { // do nothing; packet is received incorrectly and is ignored. System.out.println("Error 2"); } catch (ClassNotFoundException cnfe) { // do nothing; was not a SPON packet System.out.println("Error 3"); } try { if( inFromUser.ready() ) { String userInput = inFromUser.readLine(); if( userInput.equals( "quit" ) ){ disconnect(); } } } catch (IOException ioee) { System.out.println("Input error; try again?"); } if( frame.close() && stillActive ) { disconnect(); stillActive = false; } if( frame.readyToLeave() && stillActive ) { disconnect(); stillActive = false; frame.leaveMe(); } if( frame.ready() ) { dbg("Frame ready"); SPONPacket newPack = new SPONPacket(); newPack.MSGTYPE = ((isSupervisor)?MESSAGE:SUPERMESS); newPack.payload = "0" + ";" + SPONConstants.STRINGTYPE + ";" + mMyName + ":" + frame.getInfo() + "\n"; if (mSupervisorAddress.equals(mLocalAddress)) { dataMsg(newPack); } else { UDPSend(mSupervisorAddress.getHostAddress(),newPack); } } } // SPON Communication Loop dbg( "End of Communication" ); } // Communicate method /************************************************************ * If the free pool is too full, start putting nodes into the * network properly. ************************************************************/ private void checkPool() { if (freePool.size() > poolMax) { int sendNum = freePool.size() / poolRatio; while (sendNum > 0) { // determine where a new node can be put. // if no spaces are available, send parent's addr. String str = (String)freePool.elementAt(sendNum); String joined = myTree.join(str); SPONPacket passPack = new SPONPacket(); passPack.MSGTYPE = LOCATE; if (joined.compareToIgnoreCase("0.0.0.0")==0) { dbg("No space available for growth; sending nodes to parent."); // make LOCATE packet for parent with position of freePool. String sendTo = ((pDead)?((cpDead)?mAncestor:sParent):mParent); String payload = str + ";" + sendTo + ";" + "4"; passPack.payload = payload; UDPSend(str,passPack); } else { dbg("Node entered into network!"); // parse 'joined' to learn what LOCATE messages need to be sent // send LOCATE packets to nodes in the pool, as needed StringTokenizer strtok = new StringTokenizer(joined, "|"); String nextPacket; StringTokenizer linetok; while (strtok.hasMoreTokens()) { nextPacket = strtok.nextToken(); // receive data: // Addr of receiving node // height of receiving node // parent of receiving node // (own top-level children will never be moved) // position for receiving node (l,r,sl,sr, or -1 if // still in own tree.) // send LOCATE packets to newly-placed node // and own coparent. Other details will be // handled in normal LOC-NOT-CONF cycle. linetok = new StringTokenizer(nextPacket, ";"); String receiver = linetok.nextToken(); String newHgt = linetok.nextToken(); String newParent = linetok.nextToken(); String newPosi = linetok.nextToken(); passPack.payload = receiver + ";" + newParent + ";" + newPosi; UDPSend(receiver, passPack); UDPSend(coparent, passPack); } } sendNum--; } } } // JOIN: Own address from new node to network. // Any node with a co-parent may accept JOINs into // its private subtree. // JOINCONF: contains co-parent for receiving node, supervisor, max // permitted height. // LOCATE: contains address for node's new parent. May be sent to // move a node from the free pool into the system, or to // order the node to reposition itself in the tree. // LOCNOT: Contains the address of the new node. Sent from a node // being put into a new place, to its new parents. // LOCCONF: contains co-parent and any children for newly-positioned // node. Also sent to already-positioned co-parent to let it // know about its new partner. Co-parent then sends another // LOCCONF message to tell new node about private children. // MESSAGE: A message, as a String or other object // MESSCONF: A confirmation from one parent to the coparent that // messages are being conveyed. Also makes sure that // coparent is still alive. If one parent determines that its // coparent is dead, that parent sends a REMOVE message to // its own parent on behalf of the vanished node. // A child can also send a MESSCONF to its parent if // it suspects the parent to have vanished, and may take // similar actions as a coparent. This covers the case of both // parents simultaneously disconnecting. // One messy thing happens: If both parents vanish, any private // children have to be taken directly by the grandparent and // reallocated. // SUPERMESS: A String or other object sent to supervisor // REMOVE: Address. (message goes to co-parent and to ancestor. // co-parent takes over as parent until ancestor assigns // a replacement; also takes over private children). // Sent even if the leaving is spawned by a LOCATE message. // May also be sent from an ancestor to a child; in that case, // child should notify all of its children to contact the root // node for re-insertion. This happens when both parents and the // grandparent of some node die; only the supervisor is left // as a known address. /************************************************************** * joinMsg: A Join packet has been received. * Payload: address of joining node. *************************************************************/ void joinMsg(SPONPacket pack) { System.out.println("Someone's trying to join!"); String joindata = new String(""); // make joinConf packet containing own address, coparent address, // own parent's address, supervisor's address, separated by semicolons // add new node to free pool. if (coparent.compareToIgnoreCase("0.0.0.0")==0) { // if node has no coparent, it is not eligible to have own children. UDPSend(mParent,pack); return; } SPONPacket replPack = new SPONPacket(); replPack.MSGTYPE = JOINCONF; joindata = joindata + mLocalAddress + ";"; joindata = joindata + ((coparent.compareToIgnoreCase("0.0.0.0")==0)?" ":coparent) + ";"; joindata = joindata + mParent + ";"; joindata = joindata + mSupervisorAddress.getHostAddress(); replPack.payload = joindata; freePool.add(pack.payload); UDPSend((String)pack.payload,replPack); } /************************************************************** * joinConfMsg: A Join-Confirm packet has been received. * This should never happen normally; it should only be received during * the join phase. Ignore. *************************************************************/ void joinConfMsg(SPONPacket pack) { // do nothing. } /************************************************************** * locateMsg: A Locate packet has been received. * Payload: * address of recipient node * address of new parent for node after moving. * position new node will occupy for its parent * 0=Left, 1=Right, 2=sLeft, 3=sRight, 4=freePool *************************************************************/ void locateMsg(SPONPacket pack) { SPONPacket replPack = new SPONPacket(); replPack.MSGTYPE = LOCNOT; String newAddr = ""; StringTokenizer locTok = new StringTokenizer((String)pack.payload, ";"); String recAddr = locTok.nextToken(); String newParAddr = locTok.nextToken(); String position = locTok.nextToken(); String str = ""; if (recAddr.compareToIgnoreCase("0.0.0.0")==0) { // If listed recipient is 0.0.0.0: // a higher-level node is requesting a replacement node // (new parent address is position where node needs to go) if (freePool.size() > 0) { // if freePool not empty: // pass packet on to node from freePool str = (String) freePool.elementAt(freePool.size()-1); replPack.payload = pack.payload; UDPSend(str,replPack); dbg("Passing along request for a node."); return; } else if ( mLeftChild.compareToIgnoreCase("0.0.0.0")==0 && mRightChild.compareToIgnoreCase("0.0.0.0")==0 && sLeftChild.compareToIgnoreCase("0.0.0.0")==0 && sRightChild.compareToIgnoreCase("0.0.0.0")==0 && myTree.size() == 0) { dbg("Returning self as requested node."); // if recipient has no children: // send locate-notify to indicated new parent // send REMOVE to own parent. str = mLocalAddress.getHostAddress() + ";" + position; replPack.payload = str; UDPSend(newParAddr,replPack); replPack.MSGTYPE = REMOVE; replPack.payload = mLocalAddress.getHostAddress(); UDPSend(mParent,replPack); UDPSend(sParent,replPack); return; } else { // pass locate message down to one child if (mLon) UDPSend(mLeftChild,pack); else if (mRon) UDPSend(mRightChild,pack); else if (sLon) UDPSend(sLeftChild,pack); else if (sRon) UDPSend(sRightChild,pack); else if (myTree.size() > 0) { replPack.payload = pack.payload; UDPSend(myTree.getLowest(),replPack); } // if none of the above cases hold // then this node has no children and // an empty free pool, so control should never get here. return; } } else if (recAddr.compareToIgnoreCase(mLocalAddress.getHostAddress())==0) { // If recipient node is receiving node if (newParAddr.compareToIgnoreCase(mParent)==0) { // If recipient node's parent is already the indicated parent, do nothing return; } else { // send REMOVE notification to coparent // empty children and free pool // send Locate-Notify packet to indicated new parent SPONPacket remPack = new SPONPacket(); remPack.MSGTYPE = REMOVE; remPack.payload = mLocalAddress.getHostAddress(); UDPSend(coparent,remPack); mLeftChild = "0.0.0.0"; mRightChild = "0.0.0.0"; sLeftChild = "0.0.0.0"; sRightChild = "0.0.0.0"; myTree = new RootVector(mLocalAddress.getHostAddress()); freePool = new Vector(); SPONPacket lnPack = new SPONPacket(); lnPack.MSGTYPE = LOCNOT; } } // if recipient node is own child, msg was sent from coparent. // remove child; child should have no other children in system to worry about. else if (recAddr.compareToIgnoreCase(mLeftChild)==0) { mLon = false; mLeftChild = "0.0.0.0"; } else if (recAddr.compareToIgnoreCase(mRightChild)==0) { mRon = false; mRightChild = "0.0.0.0"; } else if (recAddr.compareToIgnoreCase(sLeftChild)==0) { sLon = false; sLeftChild = "0.0.0.0"; } else if (recAddr.compareToIgnoreCase(sRightChild)==0) { sRon = false; sRightChild = "0.0.0.0"; } else if (myTree.isChild(recAddr)) { if (position.compareToIgnoreCase("-1")==0) { // do nothing; node is still in own tree. } else myTree.removeNode(recAddr); } } /************************************************************** * locateConfMsg: A Locate-Confirm packet has been received. * Payload: * addr of newly-added node * addr of coparent * (or 0.0.0.0 if no coparent exists) * addr of primary left child for receiving node * (or 0.0.0.0 if no child exists) * addr of primary right child for receiving node * (or 0.0.0.0 if no child exists) * addrs of secondary children for receiving node * (or 0.0.0.0 if they don't exist) * integer indicating max permitted height for private tree * integer containing number of private children known. * addrs of private children, left-to-right order. * *************************************************************/ void locateConfMsg(SPONPacket pack) { System.out.println("Received Locate-Confirm packet:"); System.out.println(" " + (String)pack.payload); System.out.flush(); String lcStr = (String)pack.payload; StringTokenizer lcStrtok = new StringTokenizer(lcStr, ";"); String newNode = lcStrtok.nextToken(); String newAnc = lcStrtok.nextToken(); String newCP = lcStrtok.nextToken(); String newLC = lcStrtok.nextToken(); String newRC = lcStrtok.nextToken(); String newSLC = lcStrtok.nextToken(); String newSRC = lcStrtok.nextToken(); String newHgt = lcStrtok.nextToken(); String newKids = lcStrtok.nextToken(); int intHgt = (new Integer(newHgt)).intValue(); int numKids = (new Integer(newKids)).intValue(); String [] privKids = new String[numKids]; int i = 0; boolean isKid = false; while (lcStrtok.hasMoreTokens()) { privKids[i] = lcStrtok.nextToken(); if (privKids[i].compareToIgnoreCase(mLocalAddress.getHostAddress())==0) isKid = true; } // if coparent address is own address: if (newCP.compareToIgnoreCase(mLocalAddress.getHostAddress())==0) { // make own Locate-Confirm packet containing private children // send that to new coparent SPONPacket newLCPack = new SPONPacket(); newLCPack.MSGTYPE = LOCCONF; String newLCStr = newNode + ";" + mAncestor + ";" + newCP + ";" + mLeftChild + ";" + mRightChild + ";" + sLeftChild + ";" + sRightChild + ";" + myTree.getHeight() + myTree.getAll(); newLCPack.payload = newLCStr; UDPSend(newNode,newLCPack); // stop taking responsibility for secondary children sLon = false; sRon = false; } else if (newLC.compareToIgnoreCase(mLocalAddress.getHostAddress())==0 || newRC.compareToIgnoreCase(mLocalAddress.getHostAddress())==0 || newSLC.compareToIgnoreCase(mLocalAddress.getHostAddress())==0 || newSRC.compareToIgnoreCase(mLocalAddress.getHostAddress())==0 || isKid) { // else if own address is listed as any child // Update own record of who is parent mParent = newNode; } else if (newNode.compareToIgnoreCase(mLocalAddress.getHostAddress())==0) { // else if own address is listed as recipient: // set new coparent // set new ancestor // set new left child // set new right child // set new secondary left child // set new secondary right child // set max permitted height // store private children // Notify all children, public or private, of their new parent // via new Locate-Confirm message mAncestor = newAnc; coparent = newCP; mLeftChild = newLC; mRightChild = newRC; sLeftChild = newSLC; sRightChild = newSRC; myTree = new RootVector(mLocalAddress.getHostAddress(), intHgt); for (int j = 0; j < numKids; j++) { myTree.reposition(privKids[j],j); } UDPSend(mLeftChild,pack); UDPSend(mRightChild,pack); UDPSend(sLeftChild,pack); UDPSend(sRightChild,pack); String lcKids = myTree.sendTo(false); StringTokenizer strtok = new StringTokenizer(lcKids,";"); while (strtok.hasMoreTokens()) { UDPSend(strtok.nextToken(),pack); } } } /************************************************************** * locateNotMsg: A Locate-Notify packet has been received. * Payload: * address of node entering into the system * position (L,R,sL,sR, freePool) for node after entering. **************************************************************/ void locateNotMsg(SPONPacket pack) { System.out.println("Received Locate=Notify packet."); System.out.println(" " + (String)pack.payload); System.out.flush(); // add node as 'public' child (or into freePool) // create Locate-Confirm packet // send Locate-Confirm packet to new node and to new node's coparent // (if any) String lnstr = (String) pack.payload; StringTokenizer strtok = new StringTokenizer(lnstr, ";"); String entering = strtok.nextToken(); String pos = strtok.nextToken(); SPONPacket locconf = new SPONPacket(); String payload = ""; String nullKids = "0.0.0.0;0.0.0.0;0.0.0.0;0.0.0.0;"; nullKids += Integer.toString(myTree.getHeight()); nullKids += ";0;"; locconf.MSGTYPE = LOCCONF; if (pos.compareToIgnoreCase("L")==0) { dbg("Left child connecting."); mLon = true; mLeftChild = entering; payload = entering + ";" + mParent + ";" + mRightChild + ";" + nullKids; locconf.payload = payload; UDPSend(mLeftChild,locconf); UDPSend(mRightChild,locconf); } else if (pos.compareToIgnoreCase("R")==0) { dbg("Right child connecting."); mRon = true; mRightChild = entering; payload = entering + ";" + mParent + ";" + mLeftChild + ";" + nullKids; locconf.payload = payload; UDPSend(mLeftChild,locconf); UDPSend(mRightChild,locconf); } else if (pos.compareToIgnoreCase("SL")==0) { // don't send messages; the primary parent will send that. dbg("sLeft child connecting."); sLeftChild = entering; } else if (pos.compareToIgnoreCase("SR")==0) { // don't send messages; the primary parent will send that. dbg("sRight child connecting."); sRightChild = entering; } else if (pos.compareToIgnoreCase("free")==0) { // put in free pool, do nothing else // (in case of overflow pool) freePool.add(entering); } else { // invalid option dbg("Improper LOCNOT packet received"); } } /************************************************************** * dataMsg: A data packet has been received. * Payload: * integer indicating message number (% WINDOWSIZE) * integer indicating data type * data (only Strings supported now) * If the msgnum integer is -1, the message is a keepalive; send * a confirmation, but do nothing else. * Otherwise, propagate the message to all children and all nodes in * the free pool. *************************************************************/ void dataMsg(SPONPacket pack) { // if msgno >= 0: // propagate to all children and elements of free pool // Send data to TalkFrame for display to user. String payload = (String) pack.payload; StringTokenizer strtok = new StringTokenizer(payload, ";"); String msgnm = strtok.nextToken(); String dataType = ""; String data = ""; if (msgnm.compareToIgnoreCase("-1")!=0) { dataType = strtok.nextToken(); while (strtok.hasMoreTokens()) { data = data + strtok.nextToken(); if (strtok.hasMoreTokens()) data += ";"; } frame.update(data); if (!isSupervisor || (isSupervisor && isAccepting)) { SPONPacket prop = new SPONPacket(); prop.MSGTYPE = MESSAGE; prop.payload = payload; if (mLon) UDPSend(mLeftChild, prop); if (mRon) UDPSend(mRightChild, prop); if (sLon) UDPSend(sLeftChild, prop); if (sRon) UDPSend(sRightChild, prop); for (int i = 0; i < freePool.size(); i++) { UDPSend((String)freePool.elementAt(i), prop); } String sendTo = myTree.sendTo(sLon); StringTokenizer sttok = new StringTokenizer(sendTo, ";"); while (sttok.hasMoreTokens()) { UDPSend(sttok.nextToken(),prop); } } } } /************************************************************** * messConfMsg: A Message-Confirm packet has been received. * Payload == integer indicating confirmed message number. *************************************************************/ void messConfMsg(SPONPacket pack) { // do-nothing. Purpose of Message-Confirm is to reset timeout. } /************************************************************** * superMsg: A Super packet has been received. * Payload = same as dataMsg. * Supervisor now decides whether or not to send the message out. *************************************************************/ void superMsg(SPONPacket pack) { if (isSupervisor && isAccepting) { dataMsg(pack); } } /************************************************************** * removeMsg: A Remove packet has been received. * Contains the addr of the departing node. * If the addr is this node's coparent, take over secondary children. * If the addr is a child of this node, assign a replacement via LOCATE * then send LOCATE messages to own coparent and new node's coparent * with new data (unless departing child is a leaf). If no replacement * can be made, system does nothing. * If the addr is a secondary child of this node and the responsible * coparent is not known to be dead, remove. A LOCATE message will * arrive with the replacement info, if needed. * If the addr is parent or coparent of this node, send REMOVE messages * to own children (with own address), then JOIN the root. * If the addr is in the free pool, remove it from the free pool. * If the addr is unknown, then ignore. *************************************************************/ void removeMsg(SPONPacket pack) { String leaving = (String) pack.payload; if (leaving.compareToIgnoreCase(coparent)==0) { // coparent dead; take over secondary children. if (sLeftChild.compareToIgnoreCase("0.0.0.0")!=0) { sLon = true; } if (sRightChild.compareToIgnoreCase("0.0.0.0")!=0) { sRon = true; } // also take over any secondary children in rootVector; // automatically handled by dataMsg. } else if ( (leaving.compareToIgnoreCase(mLeftChild)==0) || (leaving.compareToIgnoreCase(mRightChild)==0) || (leaving.compareToIgnoreCase(sLeftChild)==0 && sLon) || (leaving.compareToIgnoreCase(sRightChild)==0 && sRon) || myTree.isChild(leaving) ) { String replaceString = myTree.replace(leaving); // if a node is available to replace the missing node, take it. Node replIn = new Node(); replIn.parseString(replaceString); if (replIn.getKey().compareToIgnoreCase("0.0.0.0")==0) { // do nothing; next node to join will be put in place // (no replacement available) } else { // assign replacement, inform own coparent // and new node's coparent SPONPacket locPack = new SPONPacket(); locPack.MSGTYPE = LOCATE; String locstr = replIn.getKey() + ";" + replIn.getMParent() + ";"; if (myTree.isLeft(replIn)) { String lstr = locstr + "0"; locPack.payload = lstr; UDPSend(replIn.getMParent(),locPack); lstr = locstr + "2"; locPack.payload = lstr; UDPSend(replIn.getSParent(),locPack); } else { String rstr = locstr + "1"; locPack.payload = rstr; UDPSend(replIn.getMParent(),locPack); rstr = locstr + "3"; locPack.payload = rstr; UDPSend(replIn.getSParent(),locPack); } String cstr = locstr + ";" + myTree.getPos(replIn); locPack.payload = cstr; UDPSend(coparent, locPack); } } else if ( (leaving.compareToIgnoreCase(sLeftChild)==0) || (leaving.compareToIgnoreCase(sRightChild)==0) || myTree.isChild(leaving)) { // do nothing } else if ((leaving.compareToIgnoreCase(mParent)==0) || (leaving.compareToIgnoreCase(coparent)==0)) { // send new REMOVE messages to own children and free pool. SPONPacket rPack = new SPONPacket(); rPack.MSGTYPE = REMOVE; rPack.payload = mLocalAddress.getHostAddress(); UDPSend(mLeftChild, rPack); UDPSend(mRightChild, rPack); UDPSend(sLeftChild, rPack); UDPSend(sRightChild, rPack); for (int i = 0; i < freePool.size(); i++) { UDPSend((String)freePool.elementAt(i),rPack); } String sendTo = myTree.sendTo(sLon); StringTokenizer sttok = new StringTokenizer(sendTo, ";"); while (sttok.hasMoreTokens()) { UDPSend(sttok.nextToken(), rPack); } } else { // if addr is in free pool, remove from free pool. // otherwise, just ignore it. SPONPacket rPack2 = new SPONPacket(); rPack2.MSGTYPE = REMOVE; rPack2.payload = mLocalAddress.getHostAddress(); for (int i = 0; i < freePool.size(); i++) { UDPSend((String)freePool.elementAt(i),rPack2); } } } /************************************************************** * Leave the SPON network. *************************************************************/ void disconnect() { // Notify parents // (no need to notify children; parents will provide replacements) SPONPacket discon = new SPONPacket(); discon.MSGTYPE = REMOVE; discon.payload = mLocalAddress; UDPSend(mParent,discon); UDPSend(sParent,discon); System.exit(0); } /************************************************************** * Transmit data over UDP. *************************************************************/ void UDPSend(String str, SPONPacket pack) { if (str.compareToIgnoreCase("0.0.0.0") == 0) return; if (str.length() < 7) return; // if an invalid address, do nothing. Else send. byte[] data; DatagramPacket leavePacket; try { data = getBytes(pack); try { leavePacket = new DatagramPacket( data,data.length,InetAddress.getByName(str),SPONSOCKETNO); } catch (UnknownHostException ukee) { try { StringTokenizer strtok = new StringTokenizer(str, "/"); leavePacket = new DatagramPacket( data,data.length,InetAddress.getByName(strtok.nextToken()),SPONSOCKETNO); } catch (UnknownHostException ukee2) { System.out.println("Could not connect to " + str); return; } } } catch (IOException ioe) { // do nothing; packet could not be created/sent System.out.println("Error converting packet to bytes, not sent."); return; } try { mUDPSocket.send( leavePacket ); } catch (IOException ioee) { System.out.println("Error sending packet, not sent."); } } /************************************************************** * Translate an object into a byte array. *************************************************************/ byte[] getBytes(Object obj) throws java.io.IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.flush(); oos.close(); baos.close(); byte [] output = baos.toByteArray(); return output; } }