protected virtual bool OnRequestToRegister(Packet p) { return false; }
/// Last Modified: 11/28/09 /// <summary> /// No longer in use. /// NOTE: TO use to notify the user that the message sent /// did not reach its target. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnInvalidAddress(Packet p) { //The address doesn't exist or the user has disconnected but the parent // is unaware of the situation. //Update the Status. OnNodeEvent(NodeEvent.INVALID_NODE, ParentNode.GetNodeType(), p.Msg); }
protected virtual void OnNodeRemoved(Packet p) { }
protected virtual void OnRegistered(Packet p) { }
/// Last Modified: 11/28/09 /// <summary> /// Sends a packet to the supplied IPEndPoint. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: NA -- No preconditions exist. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet of information to send. /// </param> /// <param name="ipep"> /// The destination endpoint to send the packet. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// ----------------------------------------------------- /// Return Value: public void SendTo(Packet p, IPEndPoint ipep) { if (m_nodeSocket != null) m_nodeSocket.SendTo(p, ipep); }
protected virtual void OnMessage(Packet p) { }
/// Last Modified: 10/24/10 /// <summary> /// Attempts to register the child node with the parent. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="ipAddr"> /// Contains the IPAddress of the parent. /// </param> /// <param name="nPort"> /// Contains the port of the parent. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// ----------------------------------------------------- /// Return Value: private void RegisterWithParent(IPAddress ipAddr, int nPort) { //Create the parent endpoint. m_iepParentNode = new IPEndPoint(ipAddr, nPort); Register r = new Register(NodeSocket.GetLocalIP(), HostPort, "", GetNodeType()); //Create a new packet to send to the parent. Packet p = new Packet( Packet.EventTag.Register, //Protocol NodeName, //SrcName -- Unique name to register the node with. ParentNode.GetNodeType(), //DestName -- Not used in registration. r.ToString(), //Msg LogicalTime); //Logical Time. //Send the packet synchronously. SendToParent(p); }
/// Last Modified: 10/24/10 /// <summary> /// A static method that will send packet information /// to the IPEndpoint supplied. This method is blocking /// and does not support asynchronous sending of information. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: NA -- No preconditions exist. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet of information to send. /// </param> /// <param name="ipep"> /// The destination endpoint to send the packet. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: The message will be sent or an exception will have been thrown. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// ----------------------------------------------------- /// Return Value: public void SendTo(Packet p, IPEndPoint ipep) { Socket s = null; try { //Create the socket to send information to. s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //Cast the endpoint. EndPoint ep = (EndPoint)ipep; //Perform any necessary encryptions based on the security model. byte[] byData = (m_nodeSecurityModel != null) ? m_nodeSecurityModel.ByteEncrypt(p.ToByteArray()) : p.ToByteArray(); //Marshall the string data into a byte stream and send the data. s.SendTo(byData, SocketFlags.None, ep); } catch (ObjectDisposedException ode) { throw new NodeSocketInvalid("The Socket has been closed. ", ode); } catch (SocketException se) { //MSDN NOTE: If you receive a SocketException, use the SocketException.ErrorCode property to obtain the specific error code. // After you have obtained this code, refer to the Windows Sockets version 2 API error code documentation in the MSDN library //for a detailed description of the error. throw new NodeSocketInvalid(se.Message, se); } catch (Exception ex) { //Call the error callback if one exists. throw new NodeUnknownException(ex.Message, ex); } finally { //Close the socket. s.Close(); } }
/// Last Modified: 9/15/10 /// <summary> /// This event occurs when the child node has been registered /// with the parent. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnRegistered(Packet p) { OnNodeEvent(NodeEvent.REGISTRATION_SUCCESSFUL, ParentNode.GetNodeType(), p.Msg); }
/// Last Modified: 9/15/10 /// <summary> /// This event occurs when the child cannot register with the parent node. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnRegistrationFailure(Packet p) { NodeEvent ne = (NodeEvent)Enum.Parse(typeof(NodeEvent), p.Msg); if (ne == NodeEvent.ALREADY_REGISTERED) { //Clear the parent during registration failure. m_iepParentNode = null; OnNodeEvent(NodeEvent.REGISTRATION_FAILURE, ParentNode.GetNodeType(), ""); } }
/// Last Modified: 9/15/10 /// <summary> /// This event occurs when the parent notifies the child /// that a node has been removed. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnNodeRemoved(Packet p) { OnNodeEvent(NodeEvent.NODE_REMOVED, ParentNode.GetNodeType(), p.Msg); }
/// Last Modified: 9/15/10 /// <summary> /// This event occurs when the parent notifies the child /// that a new node has been added. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnNodeAdded(Packet p) { OnNodeEvent(NodeEvent.NODE_ADDED, ParentNode.GetNodeType(), p.Msg); }
/// Last Modified: 11/28/09 /// <summary> /// This event occurs when the child node receives a message. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnMessage(Packet p) { OnNodeEvent(NodeEvent.MESSAGE, p.SourceName, p.Msg); }
/// Last Modified: 9/18/10 /// <summary> /// Attempts to notify the child node of registration, /// whether it was successful or not. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="ipAddr"> /// Contains the IPAddress of the child. /// </param> /// <param name="nPort"> /// Contains the port of the child. /// </param> /// <param name="sName"> /// Contains the name of the child node to notify of registration. /// </param> /// <param name="bSuccessful"> /// Is true if registration was successful. /// </param> /// <param name="sData"> /// Contains the status of registration. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// 3) Exception /// ----------------------------------------------------- /// Return Value: private void NotifyChildOfRegistration(IPAddress ipAddr, int nPort, string sName, bool bSuccessful, string sData) { //Create the datapacket to notify the child of registration. Packet p = new Packet( bSuccessful ? Packet.EventTag.Registered : Packet.EventTag.RegistrationFailure, //Protocol GetNodeType(), //SrcLink sName, //DestLink sData, //Msg LogicalTime); //LogicalTime. //Notify the children of a successful registration. //NOTE: Send the packet synchronously. SendTo(p, new IPEndPoint(ipAddr, nPort)); //Update the clock. Tick(); }
/// Last Modified: 9/18/10 /// <summary> /// Sends a message to the parent node. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: NA -- No preconditions exist. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: Refer to the return statement. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// ----------------------------------------------------- /// Return Value: /// bool -- Returns true if successful. private bool SendToParent(Packet p) { //Only send if the parent node is valid. if (m_iepParentNode != null) { //Send the packet synchronously. SendTo(p, m_iepParentNode); //Update the clock. Tick(); //Successful. return true; } //END OF if (m_iepParentNode != null)... //The send operation failed. return false; }
/// Last Modified: 11/28/09 /// <summary> /// Starts the sending the packet. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments: /// ----------------------------------------------------- /// Parameters: /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: public void AsynchSend(Packet p, IPEndPoint ipep) { //Avoid invalid socket access. if (m_Socket == null) return; //Prepare the state object. StateObject state = new StateObject(); state.socket = m_Socket; state.iep = ipep; state.buffer = p.ToByteArray(); //Begin sending the packet information. m_Socket.BeginSendTo(state.buffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None, (EndPoint)ipep, new AsyncCallback(OnSendCallback), state); }
/// Last Modified: 10/24/10 /// <summary> /// Attempts to unregister the child with the parent. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: The child must be registered with the parent. /// ----------------------------------------------------- /// Parameters: /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// ----------------------------------------------------- /// Return Value: private void UnregisterWithParent() { //Only attempt to unregister the child if the parent is valid. if (m_iepParentNode != null) { Register r = new Register(NodeSocket.GetLocalIP(), HostPort, "", GetNodeType()); //Create a new packet to send to the parent. Packet p = new Packet( Packet.EventTag.Unregister, //Protocol NodeName, //SrcName -- Unique name to un-register the node with. ParentNode.GetNodeType(), //DestName -- Not used in registration. r.ToString(), //Msg LogicalTime); //Logical Time. //Send the packet synchronously. SendToParent(p); } //END OF if (m_iepParentNode != null)... }
/// Last Modified: 10/24/10 /// <summary> /// This event is fired when an asynchronous receive occurs. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="ar"> /// An object that contains the received packet. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: /// 1) Async receiving will continue successfull after receiving a message. /// 2) An exception will be thrown. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// ----------------------------------------------------- /// Return Value: protected virtual void OnReceiveCallback(IAsyncResult ar) { try { //Retrieve the stateobject. StateObject state = (StateObject)ar.AsyncState; //Retrieve the socket of the local server // and cast the endpoint. Socket socket = state.socket; EndPoint ep = (EndPoint)state.iep; //Retrive the remaining data. int read = 0; if ((read = socket.EndReceiveFrom(ar, ref ep)) > 0) { byte[] byData = state.buffer; try { //Perform any necessary decryptions based on the security model. if (m_nodeSecurityModel != null) byData = m_nodeSecurityModel.ByteDecrypt(state.buffer, ref read); //Marshall the byte stream into a packet object. Packet p = new Packet(byData, read); //Call the callback method if it is valid. if (m_delegateOAR != null) m_delegateOAR(p); } catch (System.Security.Cryptography.CryptographicException ce) { //Call the error callback if one exists. if (m_delegateOARE != null) m_delegateOARE(new NodeSecurityInvalidKeyException("Invalid Key.", ce)); } //Continue receiving messages. socket.BeginReceiveFrom(state.buffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None, ref ep, new AsyncCallback(OnReceiveCallback), state); } //END OF if ((read = socket.EndReceiveFrom(ar, ref ep)) > 0)... } catch (System.ObjectDisposedException ode) { //MSDN NOTE: This occurs when the socket is closed. if (m_delegateOARE != null) m_delegateOARE(new NodeSocketInvalid("The Socket has been closed. ", ode)); } catch (System.Net.Sockets.SocketException se) { //MSDN NOTE: If you receive a SocketException, use the SocketException.ErrorCode property to obtain the specific error code. // After you have obtained this code, refer to the Windows Sockets version 2 API error code documentation in the MSDN library //for a detailed description of the error. if (m_delegateOARE != null) m_delegateOARE(new NodeSocketInvalid(se.Message, se)); } catch (Exception ex) { //Call the error callback if one exists. if (m_delegateOARE != null) m_delegateOARE(new NodeUnknownException(ex.Message, ex)); } }
/// Last Modified: 10/24/10 /// <summary> /// Closes the parent node by shutting down the local /// server and notifying all children of the event. /// All cases: O(n) -- Where n is size of the routing table. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: NA -- No preconditions exist. /// ----------------------------------------------------- /// Parameters: /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: public override void Close() { //Iterate through all endpoints in the table. //Time Complexity: //All cases: O(n) -- Where n is size of the routing table. //foreach (IPEndPoint iep in m_iepRoutingTable) foreach(KeyValuePair<string, IPEndPoint> kvp in m_iepRoutingTable) { //Create the datapacket to notify the child of registration. Packet p = new Packet( Packet.EventTag.Shutdown, //Protocol "Goodbye", //Msg LogicalTime); //LogicalTime. //Notify the children the parent is shutting down. //NOTE: Send the packet synchronously. //Ignore all exceptions, since we are closing this node anyways. try { SendTo(p, kvp.Value); } catch (NodeException) { } } //END OF foreach (IPEndPoint iep in m_iepRoutingTable)... base.Close(); }
protected virtual void OnInvalidAddress(Packet p) { }
/// Last Modified: 10/24/10 /// <summary> /// This event occurs when the parent node receives a message. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnMessage(Packet p) { //Default to the endpoint to null. IPEndPoint ipepRedirect = null; //Verify that the destination is valid. if (m_iepRoutingTable.ContainsKey(p.DestinationName)) { ipepRedirect = m_iepRoutingTable[p.DestinationName]; } //Otherwise bounce back. else { //Get the source IEP. ipepRedirect = m_iepRoutingTable[p.SourceName]; //Change the type of tag p.Tag = Packet.EventTag.InvalidAddress; p.Msg = "Destination " + p.DestinationName + " does not exist."; //Bounce the packet back. p.InvertPath(); } //END OF if (epRedirect != null)... try { //Send the packet synchronously. SendTo(p, ipepRedirect); } catch (NodeException ne) { Close(); OnNodeEvent(NodeEvent.NETWORK_ERROR, GetNodeType(), ne.Message); } }
protected virtual void OnNodeAdded(Packet p) { }
/// Last Modified: 10/24/10 /// <summary> /// This event is triggered when a child is requesting /// to register with the parent node. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: /// <returns> /// Returns true if the child could be registered. /// </returns> protected override bool OnRequestToRegister(Packet p) { //Obtain the register message. Register r = new Register(p.Msg); //Generate message to display. string sMsg = r.IPAddress.ToString() + ":" + r.Port.ToString(); try { //Update the states based on the link. if (RegisterEndPoint(p.SourceName, new IPEndPoint(r.IPAddress, r.Port))) { //temporarily package all of the user's into a tilde delimited string and attach // it to the message. StringBuilder sb = new StringBuilder(); foreach (KeyValuePair<string, IPEndPoint> kvp in m_iepRoutingTable) if (kvp.Key != p.SourceName) sb.Append(((sb.Length > 0) ? "~" : "") + kvp.Key); //Notify the child of registration. NotifyChildOfRegistration(r.IPAddress, r.Port, p.SourceName, true, sb.ToString()); //Notify all children of the new node. NotifyAllChildrenOfNodeEvent(p.SourceName, true); //Update listeners of the node event. OnNodeEvent(NodeEvent.REGISTERED, p.SourceName, sMsg); return true; } else { //Notify the child of the failure. NotifyChildOfRegistration(r.IPAddress, r.Port, p.SourceName, false, NodeEvent.ALREADY_REGISTERED.ToString()); //Update listeners of the node event. OnNodeEvent(NodeEvent.ALREADY_REGISTERED, p.SourceName, sMsg); } //END OF if (RegisterEndPoint(p.SourceName, new IPEndPoint(r.IPAddress, r.Port)))... } catch (NodeException ne) { Close(); OnNodeEvent(NodeEvent.NETWORK_ERROR, GetNodeType(), ne.Message); } //All other conditions are a failure. return false; }
/// Last Modified: 11/28/09 /// <summary> /// This event is triggered when a packet is begin received. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments: /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains the information being sent. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected virtual void OnPacketReceive(Packet p) { //Obtain the local time. if (m_lLogicalTime < p.LogicalTime) m_lLogicalTime = p.LogicalTime; //Trigger various events. if (p.Tag == Packet.EventTag.Register) OnRequestToRegister(p); else if (p.Tag == Packet.EventTag.RegistrationFailure) OnRegistrationFailure(p); else if (p.Tag == Packet.EventTag.Registered) OnRegistered(p); else if (p.Tag == Packet.EventTag.Message) OnMessage(p); else if (p.Tag == Packet.EventTag.InvalidAddress) OnInvalidAddress(p); else if (p.Tag == Packet.EventTag.Shutdown) OnShutdown(); else if (p.Tag == Packet.EventTag.Unregister) OnRequestToUnregister(p); else if (p.Tag == Packet.EventTag.NodeAdded) OnNodeAdded(p); else if (p.Tag == Packet.EventTag.NodeRemoved) OnNodeRemoved(p); }
/// Last Modified: 10/24/10 /// <summary> /// This event is triggered when the child is requesting /// to unregister with the parent. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="p"> /// A packet that contains information about the event. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: NA -- No postconditions exist. /// ----------------------------------------------------- /// Return Value: protected override void OnRequestToUnregister(Packet p) { //Obtain the register message. Register r = new Register(p.Msg); //Unregister the child node and obtain the link. UnregisterEndPoint(p.SourceName); try { //Notify all children of the new node. NotifyAllChildrenOfNodeEvent(p.SourceName, false); //Update listeners of the node event. OnNodeEvent(NodeEvent.UNREGISTERED, p.SourceName, ""); } catch (NodeException ne) { Close(); OnNodeEvent(NodeEvent.NETWORK_ERROR, GetNodeType(), ne.Message); } }
protected virtual void OnRegistrationFailure(Packet p) { }
/// Last Modified: 9/18/10 /// <summary> /// Notifies all of the children about the new node that /// has been added. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: Refer to the following arguments. /// ----------------------------------------------------- /// Parameters: /// <param name="sNameOfNode"> /// Contains the name of the node being manipulated. /// </param> /// <param name="bNodeAdded"> /// If true, notifies all children that the node is being added /// otherwise it is being removed. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: All children will have been notified of the node event. /// ----------------------------------------------------- /// Exceptions: /// 1) NodeSocketInvalid /// 2) NodeUnknownException /// 3) Exception /// ----------------------------------------------------- /// Return Value: private void NotifyAllChildrenOfNodeEvent(string sNameOfNode, bool bNodeAdded) { foreach (KeyValuePair<string, IPEndPoint> kvp in m_iepRoutingTable) { if (kvp.Key != sNameOfNode) { //Create the datapacket to notify the child of registration. Packet p = new Packet( bNodeAdded ? Packet.EventTag.NodeAdded : Packet.EventTag.NodeRemoved, //Protocol GetNodeType(), //SrcLink kvp.Key, //DestLink sNameOfNode, //Msg LogicalTime); //LogicalTime. //Notify all children of the new node. //NOTE: Send the packet synchronously. SendTo(p, kvp.Value); //Update the clock. Tick(); } //END OF if (kvp.Key != sNameOfNode)... } //END OF foreach (KeyValuePair<string, IPEndPoint> kvp in m_iepRoutingTable)... }
protected virtual void OnRequestToUnregister(Packet p) { }
/// Last Modified: 10/4/09 /// <summary> /// Sends a message to the parent node. /// </summary> /// ----------------------------------------------------- /// PRECONDITIONS: NA -- No preconditions exist. /// ----------------------------------------------------- /// Parameters: /// <param name="sEntityId"> /// The name or identity of the entity of the entity to send the message to. /// </param> /// <param name="sMsg"> /// A string that contains the message to send. /// </param> /// ----------------------------------------------------- /// POSTCONDITIONS: Refer to the return statement. /// ----------------------------------------------------- /// Return Value: /// bool -- Returns true if successful. public override bool SendMsg(string sEntityId, string sMsg) { //Create the packet. Packet p = new Packet( Packet.EventTag.Message, //Protocol NodeName, //SrcLink sEntityId, //DestLink sMsg, LogicalTime); //Logical Time. return SendToParent(p); }