/// <summary> /// Adds or updates a peer to the local availability list. Useful for when a peer informs us of an updated availability. /// </summary> /// <param name="connectionInfo">The connectionInfo of the remote peer</param> /// <param name="latestChunkFlags">The new chunk flags</param> /// <param name="superPeer">True if this peer is a superPeer</param> /// <param name="setIPEndPointOnline">Set the relevant IPEndPoint online as a result of updating chunk flags</param> public void AddOrUpdateCachedPeerChunkFlags(ConnectionInfo connectionInfo, ChunkFlags latestChunkFlags, bool superPeer = false, bool setIPEndPointOnline = true) { if (connectionInfo.NetworkIdentifier == ShortGuid.Empty) throw new Exception("networkIdentifier should not be empty."); lock (peerLocker) { if (connectionInfo.ConnectionType != ConnectionType.TCP) throw new Exception("Only the TCP side of a DFS peer should be tracked."); //Extract the correct endpoint from the provided connectionInfo //If this is taken from a connection we are after the remoteEndPoint IPEndPoint endPointToUse = null; if (((IPEndPoint)connectionInfo.RemoteEndPoint).Address == IPAddress.Any || ((IPEndPoint)connectionInfo.RemoteEndPoint).Address == IPAddress.IPv6Any) endPointToUse = (IPEndPoint)connectionInfo.LocalEndPoint; else endPointToUse = (IPEndPoint)connectionInfo.RemoteEndPoint; string endPointToUseString = endPointToUse.ToString(); //We can only add a peer if it is listening correctly if (endPointToUse.Port <= DFS.MaxTargetLocalPort && endPointToUse.Port >= DFS.MinTargetLocalPort) { //Ensure the endpoint is correctly recorded RemoveOldPeerAtEndPoint(connectionInfo.NetworkIdentifier, endPointToUse); //If we have an existing record of this peer if (peerAvailabilityByNetworkIdentifierDict.ContainsKey(connectionInfo.NetworkIdentifier)) { //If the existing peerInfo is not aware of this endPoint if (!peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].PeerContainsIPEndPoint(connectionInfo.NetworkIdentifier, endPointToUse)) { //Add the information to the peerInfo and local index peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].AddPeerIPEndPoint(connectionInfo.NetworkIdentifier, endPointToUse); peerEndPointToNetworkIdentifier[endPointToUseString] = connectionInfo.NetworkIdentifier; } //Finally update the chunk flags peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].PeerChunkFlags.UpdateFlags(latestChunkFlags); if (DFS.loggingEnabled) DFS.Logger.Trace("Updated existing chunk flags for " + connectionInfo + " [" + latestChunkFlags.NumCompletedChunks() + "]."); } else { //If we don't know anything about this peer we add it to our local swarm availability //We used comms to get any existing connections to the peer //We have to create new ConnectionInfo in the select as we need to correctly set the "LOCAL IPEndPoint" when passing to new PeerInfo() List<ConnectionInfo> peerConnectionInfos = (from current in NetworkComms.GetExistingConnection(connectionInfo.NetworkIdentifier, ConnectionType.TCP) select new ConnectionInfo(ConnectionType.TCP, current.ConnectionInfo.NetworkIdentifier, current.ConnectionInfo.RemoteEndPoint, true)).ToList(); //Don't forget to add the originating info if it was not pulled out from above ConnectionInfo originatingConnectionInfo = new ConnectionInfo(ConnectionType.TCP, connectionInfo.NetworkIdentifier, endPointToUse, true); if (!peerConnectionInfos.Contains(originatingConnectionInfo)) peerConnectionInfos.Add(originatingConnectionInfo); peerAvailabilityByNetworkIdentifierDict.Add(connectionInfo.NetworkIdentifier, new PeerInfo(peerConnectionInfos, latestChunkFlags, superPeer)); //We finish by adding the endPoint references foreach (ConnectionInfo connInfo in peerConnectionInfos) peerEndPointToNetworkIdentifier[connInfo.LocalEndPoint.ToString()] = connectionInfo.NetworkIdentifier; if (DFS.loggingEnabled) DFS.Logger.Trace("Added new chunk flags for " + connectionInfo); } if (setIPEndPointOnline) //By updating cached peer chunk flags we set the peer as being online peerAvailabilityByNetworkIdentifierDict[connectionInfo.NetworkIdentifier].SetPeerIPEndPointOnlineStatus(connectionInfo.NetworkIdentifier, endPointToUse, true); //We will trigger the alive peers event when we have at least a third of the existing peers if (!alivePeersReceivedEvent.WaitOne(0)) { int numOnlinePeers = (from current in peerAvailabilityByNetworkIdentifierDict.Values where current.HasAtleastOneOnlineIPEndPoint() select current).Count(); if (numOnlinePeers >= DFS.MaxTotalItemRequests || numOnlinePeers > peerAvailabilityByNetworkIdentifierDict.Count / 3.0) alivePeersReceivedEvent.Set(); } } else throw new Exception("Attempted to AddOrUpdateCachedPeerChunkFlags for client which was not listening or was using port outside the valid DFS range. Provided connectionInfo: " + connectionInfo + ". EndPointToUse:" + endPointToUse); //LogTools.LogException("Attempted to AddOrUpdateCachedPeerChunkFlags for client which was not listening or was using port outside the valid DFS range.", "PeerChunkFlagsUpdateError", "IP:" + endPointToUse.Address.ToString() + ", Port:" + endPointToUse.Port); } }