public ItemRemovalUpdate(ShortGuid sourceNetworkIdentifier, string itemCheckSum, bool removeSwarmWide) { if (sourceNetworkIdentifier == null) throw new NullReferenceException("Unable to create ItemRemovalUpdate unless a valid sourceNetworkIdentifier is provided."); this.SourceNetworkIdentifier = sourceNetworkIdentifier; this.ItemCheckSum = itemCheckSum; this.RemoveSwarmWide = removeSwarmWide; }
/// <summary> /// Get the provided peer IPEndPoint online status /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public bool IPEndPointOnline(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) peerAvailabilityByNetworkIdentifierDict[networkIdentifier].IsPeerIPEndPointOnline(networkIdentifier, peerIPEndPoint); } return false; }
/// <summary> /// Set the provided peer IPEndPoint as offline /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> public void SetIPEndPointOffline(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) peerAvailabilityByNetworkIdentifierDict[networkIdentifier].SetPeerIPEndPointOnlineStatus(networkIdentifier, peerIPEndPoint, true); } }
/// <summary> /// Removes any peers which have the same endPoint as the provided currentActivePeerEndPoint except one with matching currentActivePeerIdentifier /// </summary> /// <param name="currentActivePeerIdentifier">The NetworkIdenfier of the known active peer</param> /// <param name="currentActivePeerEndPoint">The endPoint of the known active peer</param> public void RemoveOldPeerAtEndPoint(ShortGuid currentActivePeerIdentifier, IPEndPoint currentActivePeerEndPoint) { lock (peerLocker) { //If we already have an entry for the provided endPoint but it does not match the provided currentActivePeerIdentifier //We need to remove the provided endPoint from the old peer string ipEndPointString = currentActivePeerEndPoint.ToString(); if (peerEndPointToNetworkIdentifier.ContainsKey(ipEndPointString) && peerEndPointToNetworkIdentifier[ipEndPointString] != currentActivePeerIdentifier) { //Remove the provided currentActivePeerEndPoint from the old peer RemovePeerIPEndPointFromSwarm(peerEndPointToNetworkIdentifier[ipEndPointString], currentActivePeerEndPoint); } } }
/// <summary> /// Create an ChunkAvailabilityReply which will precede the requested data. /// </summary> /// <param name="sourceNetworkIdentifier">The network identifier of the source of this ChunkAvailabilityReply</param> /// <param name="itemCheckSum">The checksum of the DFS item</param> /// <param name="chunkIndex">The chunkIndex of the requested item</param> /// <param name="packetIdentifier">The packet identifier used to send the data</param> public ChunkAvailabilityReply(ShortGuid sourceNetworkIdentifier, string itemCheckSum, byte chunkIndex, string packetIdentifier) { this.SourceNetworkIdentifier = sourceNetworkIdentifier; this.ItemCheckSum = itemCheckSum; this.ChunkIndex = chunkIndex; this.PacketIdentifier = packetIdentifier; this.ReplyState = ChunkReplyState.DataIncluded; }
/// <summary> /// Execute this method when a peer is discovered /// </summary> /// <param name="peerIdentifier">The network identifier of the discovered peer</param> /// <param name="discoveredPeerEndPoints">The discoverable endpoints found for the provided peer</param> private static void PeerDiscovered(ShortGuid peerIdentifier, Dictionary<ConnectionType, List<EndPoint>> discoveredPeerEndPoints) { //Lock to ensure we do not write to the console in parallel. lock (locker) { Console.WriteLine("\nEndpoints discovered for peer with networkIdentifier {0} ...", peerIdentifier); foreach (ConnectionType connectionType in discoveredPeerEndPoints.Keys) { Console.WriteLine(" ... endPoints of type {0}:", connectionType); foreach (EndPoint endPoint in discoveredPeerEndPoints[connectionType]) Console.WriteLine(" -> {0}", endPoint.ToString()); } } }
/// <summary> /// Returns the new timeout count value after incrementing the timeout count for the provided peer IPEndPoint. /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public int GetNewTimeoutCount(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (!peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) return 0; return peerAvailabilityByNetworkIdentifierDict[networkIdentifier].GetNewTimeoutCount(networkIdentifier, peerIPEndPoint); } }
/// <summary> /// Returns true if a peer has a complete copy of the DFS item /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="totalNumChunks">The total number of chunks in this item</param> /// <returns></returns> public bool PeerIsComplete(ShortGuid networkIdentifier, byte totalNumChunks) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (!peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) throw new Exception("networkIdentifier provided does not exist in peerChunksByNetworkIdentifierDict. Check with PeerExistsInSwarm before calling this method."); return peerAvailabilityByNetworkIdentifierDict[networkIdentifier].PeerChunkFlags.AllFlagsSet(totalNumChunks); } }
/// <summary> /// Update the provided peer IPEndPoint busy status /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <param name="busyStatus">The new peer busy status</param> public void SetPeerIPEndPointBusyStatus(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint, bool busyStatus) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); IPEndPointBusyDict[connectionInfo] = busyStatus; IPEndPointBusyAnnounceTimeDict[connectionInfo] = DateTime.Now; } }
/// <summary> /// Returns the current busy status of the requested peer IPEndPoint /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public bool IsPeerIPEndPointBusy(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); if (IPEndPointBusyDict.ContainsKey(connectionInfo)) return IPEndPointBusyDict[connectionInfo]; else return false; } }
/// <summary> /// Update the provided peer IPEndPoint online status /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <param name="onlineStatus">The new online status</param> public void SetPeerIPEndPointOnlineStatus(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint, bool onlineStatus) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); IPEndPointOnlineDict[connectionInfo] = onlineStatus; } }
/// <summary> /// Returns true if the specified peer has the specified IPEndPoint online. /// </summary> /// <param name="networkIdentifier">The network identifier of the peer to check</param> /// <param name="peerIPEndPoint">The IPEndPoint of the peer to check</param> /// <returns></returns> public bool IsPeerIPEndPointOnline(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); if (SuperPeer) { //We may have many possible IPEndPoints for a super peer. //If we have at least one IPEndPoint that is indeed marked as online we only return true for that match if (IPEndPointOnlineDict.ContainsKey(connectionInfo)) return IPEndPointOnlineDict[connectionInfo]; else { if ((from current in IPEndPointOnlineDict where current.Value == true select current).Count() > 0) return false; else //If no IPEndPoints are yet marked as online we return true by default return true; } } if (IPEndPointOnlineDict.ContainsKey(connectionInfo)) return IPEndPointOnlineDict[connectionInfo]; else return false; } }
/// <summary> /// Returns the chunk flag availability of the requested peer. /// </summary> /// <param name="peerIdentifier">The relevant network identifier</param> /// <returns></returns> public ChunkFlags PeerChunkAvailability(ShortGuid peerIdentifier) { if (peerIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (PeerExistsInSwarm(peerIdentifier)) return peerAvailabilityByNetworkIdentifierDict[peerIdentifier].PeerChunkFlags; else throw new Exception("A peer with the provided peerIdentifier does not exist in this swarm."); } }
/// <summary> /// Single method for determining if contact can be made with the request peer. /// Prevents loop back contact via matching identifier and currentLocalListenEndPoints. /// Finally uses the DFS.AllowedPeerIPS and DFS.DisallowedPeerIPS if set. /// </summary> /// <param name="peerIdentifier">The relevant network identifier</param> /// <param name="peerEndPoint">The relevant IPEndPoint</param> /// <param name="superPeer">True if this peer is a super peer</param> /// <returns></returns> public bool PeerContactAllowed(ShortGuid peerIdentifier, IPEndPoint peerEndPoint, bool superPeer) { if (peerIdentifier == NetworkComms.NetworkIdentifier) return false; List<EndPoint> currentLocalListenEndPoints = Connection.ExistingLocalListenEndPoints(ConnectionType.TCP); if (currentLocalListenEndPoints.Contains(peerEndPoint)) return false; //We always allow super peers //If this is not a super peer and we have set the allowedPeerIPs then we check in there bool peerAllowed = false; if (!superPeer && (DFS.allowedPeerIPs.Count > 0 || DFS.disallowedPeerIPs.Count > 0)) { if (DFS.allowedPeerIPs.Count > 0) peerAllowed = DFS.allowedPeerIPs.Contains(peerEndPoint.Address.ToString()); else peerAllowed = !DFS.disallowedPeerIPs.Contains(peerEndPoint.Address.ToString()); } else peerAllowed = true; return peerAllowed; }
/// <summary> /// Returns true if a peer with the provided networkIdentifier exists in this SwarmChunkAvailability /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <returns></returns> public bool PeerExistsInSwarm(ShortGuid networkIdentifier) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) return peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier); }
/// <summary> /// Returns true if the specified peer has the specified chunkIndex. /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="chunkIndex">The desired chunkIndex</param> /// <returns></returns> public bool PeerHasChunk(ShortGuid networkIdentifier, byte chunkIndex) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) return peerAvailabilityByNetworkIdentifierDict[networkIdentifier].PeerChunkFlags.FlagSet(chunkIndex); else throw new Exception("No peer was found in peerChunksByNetworkIdentifierDict with the provided networkIdentifier."); } }
/// <summary> /// Returns the new timeout count value after incrementing the timeout count. /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public int GetNewTimeoutCount(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); if (IPEndPointTimeoutCountDict.ContainsKey(connectionInfo)) return ++IPEndPointTimeoutCountDict[connectionInfo]; else { IPEndPointTimeoutCountDict[connectionInfo] = 1; return 1; } } }
/// <summary> /// Returns true if the specified peer is a super peer /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <returns></returns> public bool PeerIsSuperPeer(ShortGuid networkIdentifier) { if (networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (!peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) return false; return peerAvailabilityByNetworkIdentifierDict[networkIdentifier].SuperPeer; } }
/// <summary> /// Removes the provided connectionInfo from all internal dictionaries. Returns true if connectionInfo exists, otherwise false /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public bool RemovePeerIPEndPoint(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); return RemovePeerIPEndPoint(connectionInfo); }
/// <summary> /// Deletes knowledge of a peer IPEndPoint from our local swarm chunk availability. /// If peerEndPoint.Address is IPAddress.Any then the entire peer will be deleted. /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerEndPoint">The relevant IPEndPoint</param> /// <param name="forceRemoveWholePeer">If true every IPEndPoint is removed for the provided network identifier</param> public void RemovePeerIPEndPointFromSwarm(ShortGuid networkIdentifier, IPEndPoint peerEndPoint, bool forceRemoveWholePeer = false) { try { if (networkIdentifier == null || networkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); if (peerEndPoint.Address == IPAddress.Any && !forceRemoveWholePeer) throw new Exception("IPEndPoint may only reference IPAddress.Any if forceRemoveWholePeer is true."); lock (peerLocker) { //If we have an entry for this peer in peerAvailabilityByNetworkIdentifierDict //We only remove the peer if we have more than one and it is not a super peer if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) { //We can remove this peer if //1. We have set force remove //or //2. We have more than at least 1 other peer AND if this is a super peer we need at least 1 other super peer in order to remove if (forceRemoveWholePeer || (peerAvailabilityByNetworkIdentifierDict.Count > 1 && (!peerAvailabilityByNetworkIdentifierDict[networkIdentifier].SuperPeer || (from current in peerAvailabilityByNetworkIdentifierDict where current.Value.SuperPeer select current.Key).Count() > 1))) { //If we have set force remove for the whole peer //or this is the last IPEndPoint for the peer if (forceRemoveWholePeer || peerAvailabilityByNetworkIdentifierDict[networkIdentifier].NumberOfConnectionInfos == 1) { //We need to remove all traces of this peer if (peerAvailabilityByNetworkIdentifierDict[networkIdentifier].NumberOfConnectionInfos == 1 && !peerAvailabilityByNetworkIdentifierDict[networkIdentifier].GetConnectionInfo()[0].LocalEndPoint.Equals(peerEndPoint) && !forceRemoveWholePeer) { //This circumstance could happen if multiple threads attempt to remove the same peer endPoint. The first one would succeed, the second //one was previously hitting this exception incorrectly. if (DFS.loggingEnabled) DFS._DFSLogger.Trace(" ... attempted to remove peer from swarm which has one remaining localEndPoint, but which doesn't match the provided peerEndPoint to remove - " + peerEndPoint + "."); return; //throw new Exception("Possible corruption detected in SwarmChunkAvailability - 1 - " + peerAvailabilityByNetworkIdentifierDict[networkIdentifier].GetConnectionInfo()[0].LocalEndPoint + " - " + peerEndPoint); } if (peerEndPointToNetworkIdentifier.ContainsKey(peerEndPoint.ToString()) && peerEndPointToNetworkIdentifier[peerEndPoint.ToString()] != networkIdentifier) throw new Exception("Possible corruption detected in SwarmChunkAvailability - 2"); List<ConnectionInfo> peerConnectionInfos = peerAvailabilityByNetworkIdentifierDict[networkIdentifier].GetConnectionInfo(); foreach (ConnectionInfo connInfo in peerConnectionInfos) { peerAvailabilityByNetworkIdentifierDict[networkIdentifier].RemovePeerIPEndPoint(connInfo); peerEndPointToNetworkIdentifier.Remove(connInfo.LocalEndPoint.ToString()); } peerAvailabilityByNetworkIdentifierDict.Remove(networkIdentifier); if (DFS.loggingEnabled) DFS._DFSLogger.Trace(" ... removed entire peer from swarm - " + networkIdentifier + "."); } else { bool removeResult = peerAvailabilityByNetworkIdentifierDict[networkIdentifier].RemovePeerIPEndPoint(networkIdentifier, peerEndPoint); peerEndPointToNetworkIdentifier.Remove(peerEndPoint.ToString()); if (removeResult) { if (DFS.loggingEnabled) DFS._DFSLogger.Trace(" ... removed peer IPEndPoint from swarm - " + networkIdentifier + " - " + peerEndPoint.ToString() + "."); } else { if (DFS.loggingEnabled) DFS._DFSLogger.Trace(" ... attempted to removed peer IPEndPoint from swarm but it didn't exist - " + networkIdentifier + " - " + peerEndPoint.ToString() + "."); } } } else { if (DFS.loggingEnabled) DFS._DFSLogger.Trace(" ... remove failed as forceRemove= " + forceRemoveWholePeer + ", peerAvailabilityByNetworkIdentifierDict.Count=" + peerAvailabilityByNetworkIdentifierDict.Count + ", isSuperPeer=" + peerAvailabilityByNetworkIdentifierDict[networkIdentifier].SuperPeer + ", superPeerCount=" + (from current in peerAvailabilityByNetworkIdentifierDict where current.Value.SuperPeer select current.Key).Count()); } } else { if (DFS.loggingEnabled) DFS._DFSLogger.Trace(" ... peer did not exist in peerAvailabilityByNetworkIdentifierDict. Checking for old ipEndPoint references"); //Remove any accidental entries left in the endpoint dict peerEndPointToNetworkIdentifier.Remove(peerEndPoint.ToString()); } } } catch (Exception ex) { LogTools.LogException(ex, "Error_RemovePeerFromSwarm"); } }
/// <summary> /// Add new IPEndPoint for a peer. Returns true if successfully added, otherwise false. /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public bool AddPeerIPEndPoint(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); if (PeerConnectionInfo.Contains(connectionInfo)) return false; else { PeerConnectionInfo.Add(connectionInfo); IPEndPointBusyAnnounceTimeDict.Add(connectionInfo, DateTime.Now); IPEndPointOnlineDict.Add(connectionInfo, false); IPEndPointBusyDict.Add(connectionInfo, false); IPEndPointTimeoutCountDict.Add(connectionInfo, 0); return true; } } }
/// <summary> /// Returns true if the provided IPEndPoint exists for this peer /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> /// <returns></returns> public bool PeerContainsIPEndPoint(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { ConnectionInfo connectionInfo = new ConnectionInfo(ConnectionType.TCP, networkIdentifier, peerIPEndPoint, true); lock (syncRoot) { ValidateNetworkIdentifier(connectionInfo); return PeerConnectionInfo.Contains(connectionInfo); } }
/// <summary> /// Set the provided peer IPEndPoint busy status to busy /// </summary> /// <param name="networkIdentifier">The network identifier of the relevant peer</param> /// <param name="peerIPEndPoint">The relevant IPEndPoint</param> public void SetIPEndPointBusy(ShortGuid networkIdentifier, IPEndPoint peerIPEndPoint) { lock (peerLocker) { if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(networkIdentifier)) peerAvailabilityByNetworkIdentifierDict[networkIdentifier].SetPeerIPEndPointBusyStatus(networkIdentifier, peerIPEndPoint, true); } }
/// <summary> /// Create a new ChatMessage /// </summary> /// <param name="sourceIdentifier">The source identifier</param> /// <param name="sourceName">The source name</param> /// <param name="message">The message to be sent</param> /// <param name="messageIndex">The index of this message</param> public ChatMessage(ShortGuid sourceIdentifier, string sourceName, string message, long messageIndex) { this._sourceIdentifier = sourceIdentifier; this.SourceName = sourceName; this.Message = message; this.MessageIndex = messageIndex; this.RelayCount = 0; }
/// <summary> /// Deserializes remote listeners that are discoverable /// </summary> /// <param name="data"></param> /// <param name="networkIdentifier"></param> /// <returns></returns> private static List<PeerListenerEndPoint> DeserializeRemoteListenerList(byte[] data, out ShortGuid networkIdentifier) { List<PeerListenerEndPoint> result = new List<PeerListenerEndPoint>(); int offset = 0; byte[] idData = new byte[16]; Buffer.BlockCopy(data, 0, idData, 0, 16); offset += 16; networkIdentifier = new ShortGuid(new Guid(idData)); int numElements = BitConverter.ToInt32(data, offset); offset += sizeof(int); for (int i = 0; i < numElements; i++) { int size = BitConverter.ToInt32(data, offset); offset += sizeof(int); if (size > data.Length) throw new Exception(); byte[] peerData = new byte[size]; Buffer.BlockCopy(data, offset, peerData, 0, peerData.Length); offset += peerData.Length; result.Add(PeerListenerEndPoint.Deserialize(peerData)); } return result; }